Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active April 6, 2023 11:58
Show Gist options
  • Star 67 You must be signed in to star a gist
  • Fork 89 You must be signed in to fork a gist
  • Save mbostock/1353700 to your computer and use it in GitHub Desktop.
Save mbostock/1353700 to your computer and use it in GitHub Desktop.
Epicyclic Gearing
license: gpl-3.0
redirect: https://observablehq.com/@mbostock/epicyclic-gearing

From Wikipedia:

Epicyclic gearing or planetary gearing is a gear system consisting of one or more outer gears, or planet gears, revolving about a central, or sun gear. … Epicyclic gearing systems also incorporate the use of an outer ring gear or annulus, which meshes with the planet gears.

Use the menu in the top-left to change the frame of reference, fixing the specified gear in-place.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
width: 960px;
height: 500px;
position: relative;
}
form {
position: absolute;
top: 1em;
left: 1em;
}
path {
fill-rule: evenodd;
stroke: #333;
stroke-width: 2px;
}
.sun path {
fill: #6baed6;
}
.planet path {
fill: #9ecae1;
}
.annulus path {
fill: #c6dbef;
}
</style>
<form>
<input type="radio" name="reference" id="ref-annulus">
<label for="ref-annulus">Annulus</label><br>
<input type="radio" name="reference" id="ref-planet" checked>
<label for="ref-planet">Planets</label><br>
<input type="radio" name="reference" id="ref-sun">
<label for="ref-sun">Sun</label>
</form>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var width = 960,
height = 500,
radius = 80,
x = Math.sin(2 * Math.PI / 3),
y = Math.cos(2 * Math.PI / 3);
var offset = 0,
speed = 4,
start = Date.now();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(.55)")
.append("g");
var frame = svg.append("g")
.datum({radius: Infinity});
frame.append("g")
.attr("class", "annulus")
.datum({teeth: 80, radius: -radius * 5, annulus: true})
.append("path")
.attr("d", gear);
frame.append("g")
.attr("class", "sun")
.datum({teeth: 16, radius: radius})
.append("path")
.attr("d", gear);
frame.append("g")
.attr("class", "planet")
.attr("transform", "translate(0,-" + radius * 3 + ")")
.datum({teeth: 32, radius: -radius * 2})
.append("path")
.attr("d", gear);
frame.append("g")
.attr("class", "planet")
.attr("transform", "translate(" + -radius * 3 * x + "," + -radius * 3 * y + ")")
.datum({teeth: 32, radius: -radius * 2})
.append("path")
.attr("d", gear);
frame.append("g")
.attr("class", "planet")
.attr("transform", "translate(" + radius * 3 * x + "," + -radius * 3 * y + ")")
.datum({teeth: 32, radius: -radius * 2})
.append("path")
.attr("d", gear);
d3.selectAll("input[name=reference]")
.data([radius * 5, Infinity, -radius])
.on("change", function(radius1) {
var radius0 = frame.datum().radius, angle = (Date.now() - start) * speed;
frame.datum({radius: radius1});
svg.attr("transform", "rotate(" + (offset += angle / radius0 - angle / radius1) + ")");
});
d3.selectAll("input[name=speed]")
.on("change", function() { speed = +this.value; });
function gear(d) {
var n = d.teeth,
r2 = Math.abs(d.radius),
r0 = r2 - 8,
r1 = r2 + 8,
r3 = d.annulus ? (r3 = r0, r0 = r1, r1 = r3, r2 + 20) : 20,
da = Math.PI / n,
a0 = -Math.PI / 2 + (d.annulus ? Math.PI / n : 0),
i = -1,
path = ["M", r0 * Math.cos(a0), ",", r0 * Math.sin(a0)];
while (++i < n) path.push(
"A", r0, ",", r0, " 0 0,1 ", r0 * Math.cos(a0 += da), ",", r0 * Math.sin(a0),
"L", r2 * Math.cos(a0), ",", r2 * Math.sin(a0),
"L", r1 * Math.cos(a0 += da / 3), ",", r1 * Math.sin(a0),
"A", r1, ",", r1, " 0 0,1 ", r1 * Math.cos(a0 += da / 3), ",", r1 * Math.sin(a0),
"L", r2 * Math.cos(a0 += da / 3), ",", r2 * Math.sin(a0),
"L", r0 * Math.cos(a0), ",", r0 * Math.sin(a0));
path.push("M0,", -r3, "A", r3, ",", r3, " 0 0,0 0,", r3, "A", r3, ",", r3, " 0 0,0 0,", -r3, "Z");
return path.join("");
}
d3.timer(function() {
var angle = (Date.now() - start) * speed,
transform = function(d) { return "rotate(" + angle / d.radius + ")"; };
frame.selectAll("path").attr("transform", transform);
frame.attr("transform", transform); // frame of reference
});
</script>
@Pomax
Copy link

Pomax commented Sep 28, 2013

basedon the wiki quote, the sun and annulus radio buttons need their labels exchanged =)

@alvineadel
Copy link

This example is amazing indeed! :)

@Manjulajntuk
Copy link

Hello sir i want to perform intersection with two geojson files with draw tool. I have tried with by using example https://bl.ocks.org/pgiraud/2ed05b0a9e394c5652b3. By using it i can able to perform intersection to one geojson with draw tool. Now my intension is to perform intersection to the two geojson files and then find intersection of the one geojson with other geojson, i have tried with the code like below for reference sir.

<title>Select features example</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.10.1/ol-debug.js"></script> <script src="ol.js"></script> <script src="jsts_geom_Polygon.js"></script> <script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/lib/0.16.0/javascript.util.min.js"></script> <script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/lib/0.16.0/jsts.min.js"></script> <script src="jsts_io_GeoJSONParser.js"></script> <style> html, body { padding: 0; margin: 0; } html, body, .map { height: 100%; width: 100%; } </style>

<script> var geojsonFormat = new ol.format.GeoJSON(); var style = new ol.style.Style({ fill: new ol.style.Fill({ color: 'rgba(255, 255, 255, 0.3)' }), stroke: new ol.style.Stroke({ color: 'rgba(255, 120, 0, 0.6)', width: 1 }) }); var styles = [style]; var highlightStyle = [ new ol.style.Style({ fill: new ol.style.Fill({ color: 'rgba(255, 120, 0, 0.3)' }), stroke: new ol.style.Stroke({ color: 'rgba(255, 120, 0, 0.6)', width: 1 }) }) ]; var vector3 = new ol.layer.Vector({ maxResolution: 0.8, source: new ol.source.Vector({ url: 'ap_taluk.geojson', format: new ol.format.GeoJSON({ defaultDataProjection:'EPSG:4326' }) }), style: styles }); var vector2 = new ol.layer.Vector({ maxResolution: 0.8, source: new ol.source.Vector({ url: 'flood.geojson', format: new ol.format.GeoJSON({ defaultDataProjection:'EPSG:4326' }) }), style: styles }); var layer1 = new ol.layer.Tile({ title: 'India', source: new ol.source.TileWMS({ url: '', params: {LAYERS: ''} }), transparent: false }); var dist = new ol.layer.Tile({ title: 'flood', source: new ol.source.TileWMS({ url: '', params: {LAYERS: ''} }) }); var distaluk = new ol.layer.Tile({ title: 'taluk', source: new ol.source.TileWMS({ url: '', params: {LAYERS: ''} }) }); intersectionLayer = new ol.layer.Vector({ source: new ol.source.Vector() }); var map = new ol.Map({ layers: [layer1,vector2,vector3,intersectionLayer,distaluk], target: 'map', view: new ol.View({ projection: 'EPSG:4326', center: [79.419,15.428], zoom: 8 }) }); var draw_inter = new ol.interaction.Draw({ type: 'Polygon' }); map.addInteraction(draw_inter); draw_inter.on('drawstart', function(evt) { intersectionLayer.getSource().clear(); }); var olParser = new jsts.io.olParser(); var geojsonParser = new jsts.io.GeoJSONParser(); draw_inter.on('drawend', function(evt) { var poly1 = olParser.read(evt.feature.getGeometry()); var extent1 = evt.feature.getGeometry().getExtent(); var source = vector2.getSource(); var source2 = vector3.getSource(); //var source4 = vector4.getSource(); var features = source.getFeatures(); var features2 = source2.getFeatures(); // var features4 = source4.getFeatures(); features.forEach(function(feature) { if (!ol.extent.intersects(extent1, feature.getGeometry().getExtent())) { return; } var poly2 = olParser.read(feature.getGeometry()); var intersection = poly1.intersection(poly2); //alert(intersection); intersection = geojsonParser.write(intersection); if(intersection.type === 'GeometryCollection' && intersection.geometries.length === 0) { return; } else { intersectionLayer.getSource().addFeature(geojsonFormat.readFeature({ type: 'Feature', properties: {}, geometry: intersection })); //alert("flood"+feature.get('geometry')); } }); features2.forEach(function(feature) { if (!ol.extent.intersects(extent1, feature.getGeometry().getExtent())) { return; } var poly3 = olParser.read(feature.getGeometry()); var intersection = poly1.intersection(poly3); //alert(intersection); intersection = geojsonParser.write(intersection); if(intersection.type === 'GeometryCollection' && intersection.geometries.length === 0) { return; } else { alert("village"+feature.get('taluk')); /*intersectionLayer.getSource().addFeature(geojsonFormat.readFeature({ type: 'Feature', properties: {}, geometry: intersection })); /*layersize = intersectionLayer.getSource().getFeatures().length; alert(" size"+layersize); for(i=0;i 10000) { output = (Math.round(totalarea / 1000000 * 100) / 100) + ' ' + 'km2'; } else { output = (Math.round(totalarea * 100) / 100) + ' ' + 'm2'; } alert(output); }); </script>

@Kyeongan
Copy link

Kyeongan commented Sep 2, 2016

Amazing!

@Tracy2014
Copy link

Hi, this question is actually relates to the D3 graph stated here http://bl.ocks.org/mbostock/7607535. I'm very interested in using this graph. However, could you suggest a bit on how to include "hover" behaviour for every node (include parent and children nodes) please?

@xmyLydia
Copy link

Does anyone has the data called 'airport.csv' and 'flights.csv' because in the example :https://bl.ocks.org/mbostock/7608400 , these two files are used but there is no source, I want to figure out the format of these files and use my own data to replace them.

@abkunal
Copy link

abkunal commented Apr 3, 2019

Does anyone has the data called 'airport.csv' and 'flights.csv' because in the example :https://bl.ocks.org/mbostock/7608400 , these two files are used but there is no source, I want to figure out the format of these files and use my own data to replace them.

You can find it here
https://gist.github.com/mbostock/7608400

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment