Skip to content

Instantly share code, notes, and snippets.

@tanyaschlusser
Last active August 29, 2015 14:02
Show Gist options
  • Save tanyaschlusser/12587612dc6e47970c20 to your computer and use it in GitHub Desktop.
Save tanyaschlusser/12587612dc6e47970c20 to your computer and use it in GitHub Desktop.
Solar System

Positions are exact on the day Sputnik launched (from the JPL calculator) and then diverge; I use a series expansion to approximate the solution to Kepler's formulation for position as a function of time that's accurate to a couple of hundredths of radians (OK for animation.)

The rings are accurate (alpha is set to their albedo plus a small constant that makes Jupiter's and Uranus's rings visible).

Use the vertical slider to change perspective, the top left slider to move in time, and the top right one to scale the planets' radii relative to the sun. The date range is from first flight to first contact ...

<head>
<title>Solar System</title>
<meta charset "UTF-8"/>
<link rel="stylesheet" type="text/css" href="solarSystemStyle.css" />
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<h1> Planets of the Solar System </h1>
<div id="OtherControls">
<div id="dateScale"></div>
<div id="buttons">
<button id="startbtn"> Resume </button>
<button id="fasterbtn">Faster</button>
<button id="pausebtn">Pause</button>
<button id="slowerbtn">Slower</button>
</div>
<div id="planetScale">
<!--Planet scale relative to Sun:
<output name="planetScale">2500</output>
<input type="range" min="100" max="5000" value="2500" step="100"></input>-->
</div>
</div>
<div id="SolarSystem"></div>
<div id="PolarAngle"></div>
<div id="Description">
<!-- previous location of planetScale -->
<table><tr><th colspan="2"> Planet details </table>
<ul>
<li> Sun diameter (white part) is the same scale as the orbits
<li> Orbit shape and planet size: <a href="http://ssd.jpl.nasa.gov/?planet_phys_par">JPL</a>
<li> Initial positions: <a href="http://ssd.jpl.nasa.gov/horizons.cgi?s_time=1#top">JPL</a>
<li> Axial tilt direction: <a href="http://www.cso.caltech.edu/outreach/log/NIGHT_DAY/inclination.htm">Caltech's CSO</a>
<li> Ring details: <a href="http://nssdc.gsfc.nasa.gov/planetary/factsheet/"> NASA fact sheets</a>
<li> Inspiration: <a href="https://www.fourmilab.ch/cgi-bin/Solar/action">John Walker's site</a>
<li> Enabling library: <a href="http://d3js.org">Mike Bostock's D3.js</a>
<li> Equations: <a href="http://en.wikipedia.org/wiki/Kepler's_laws_of_planetary_motion">
Wikipedia</a> (or <a href="http://www.braeunig.us/space/orbmech.htm">deeper</a>)
<li> Please link back!
</ul>
</div>
<script>
//<![CDATA[
var Cos=Math.cos, Sin=Math.sin, Pow=Math.pow;
var above = true,
asteroids = {"distance": 308171614, "width":181013424, "thickness": 42000000}, // wikipedia
currentZoom = 1,
dateFormatter = d3.time.format("%d %b %Y"),
dateVal = new Date(),
dt = 0.25,
kmPerAu = 149597871,
paused = false,
planetScale = 2000,
phi = 0;
sun = {"name":"Sun", "r":"1390000", "p": "25.4"},
t0 = 2436115.811111111, // Start when Sputnik was lanched...10/4/1957 7:28 PM
t = 2436115.811111111;
/* Position on Julian Date: 2436115.811111111
N = Mean Motion (degrees per day)
Tp = Perihelion Julian Date
M = Mean Anomaly (degrees)
TA = True Anomaly (degrees)
*/
var positions = {
"Mercury": {"N":4.092357023756561E+00, "Tp":2436107.650786827784, "M":3.339496039681397e+01, "TA":4.963264004656930e+01},
"Venus": {"N":1.602158559766484e+00, "Tp":2436009.280737817287, "M":1.706785494479548e+02, "TA":1.708036749106210e+02},
"Earth": {"N":9.863658540521377e-01, "Tp":2436208.075655013323, "M":2.695556802441351e+02, "TA":2.670444452891538e+02},
"Mars": {"N":5.239967637371190e-01, "Tp":2436394.139919182751, "M":2.141566053157155e+02, "TA":2.086886458959732e+02},
"Jupiter": {"N":8.310148115902946e-02, "Tp":2433970.537481698208, "M":1.782754160956198e+02, "TA":1.784341219838373e+02},
"Saturn": {"N":3.330865825573438e-02, "Tp":2431345.952658073977, "M":1.588775851404409e+02, "TA":1.609118518753897e+02},
"Uranus": {"N":1.167547963800946e-02, "Tp":2439354.989795456640, "M":3.221810352270507e+02, "TA":3.184800868678590e+02},
"Neptune": {"N":5.917334134481460e-03, "Tp":2414043.170068426523, "M":1.306111922800350E+02, "TA":1.308575374464771E+02}};
var definitions = {
"a": "Semimajor axis (km)",
"e": "Orbit Eccentrity",
"eqi": "Axial tilt (deg)",
"i": "Orbit Inclination to Ecliptic (deg)",
"p": "Rotation Period (Earth days)",
"r": "Mean Equatorial Radius (km)",
"s": "Mean Orbit Velocity (km/h)" };
/*--------------------------------------------------------- Functions --- */
function getTA(planet, e) {
// Return the angle (in radians) as a function of the current time (global t)
// http://en.wikipedia.org/wiki/Kepler's_laws_of_planetary_motion
//
// M = Mean Motion = N t; t = 0 at perihelion (shortest distance)
var M = positions[planet].N * (t - positions[planet].Tp);
M = M < 0 ? 360 + (M % 360) : M % 360;
M = M * Math.PI / 180;
/* E = Eccentric Anomaly (nonlinear equation)
M = E - epsilon sin(E)
(Approximate with a series expansion about E = M)
M = M - epsilon sin(M)
+ (E - M) * (1 - epsilon cos(M))
+ 1/2 epsilon (E-M)^2 sin(M) + Order((E-M)^3) = O( epsilon^3 sin^3(M) )
## Subtract M; rearrange
0 = (E-M)^2 + (E-M) * 2 * (1 - epsilon cos(M)) / (epsilon sin(M)) - 2
## If M is within an eighth of a degree of a multiple of 180, then just say E = M
*/
E = M
if (Math.abs(M % Math.PI) > .125) {
var bb = (1 - e * Cos(M)) / (e * Sin(M));
E += (bb > 0) ? -bb + Math.sqrt(bb*bb + 2) : -bb - Math.sqrt(bb*bb + 2);
}
// TA = True Anomaly; tan^2 TA/2 = (1 + eps)/(1-eps) tan^2 E / 2
var TA = 2 * Math.atan(Math.sqrt( (1 + e) / (1 - e) * Pow(Math.tan(E / 2) , 2)));
if ((E < 0) || (E > Math.PI)) {
TA = 2* Math.PI - TA;
}
return TA;
}
function getRadius(TA, a, e) {
// Return the radius for an ellipse with focus at 0,0 and perapsis at 0 degrees.
return a / kmPerAu * (1 - e * e) / (1 + e * Cos(TA));
}
function getHalfRingPath(X, Y, tilt, radius, heightscale, sweep, ringwidth, thickness) {
// Return a path string for a half ellipse.
// Parameters 'ringwidth' and 'thickness' are optional
var r = radius, S = Sin(tilt), C = Cos(tilt);
var dy = -r * S;
var dx = -r * C;
thickness = (typeof thickness == "undefined") ? 0 : thickness/2;
thickness *= (sweep == 0) ? -1 : 1;
path = [ "M", X - dx, Y - dy - thickness, // start pos
"A", r, r * heightscale, // ellipse radii
tilt * 180 / Math.PI, 1, sweep, // ellipse rotation, large arc, sweep direction
X + dx, Y + dy - thickness]; // end pos
if (typeof ringwidth == "undefined" || ringwidth == 0)
return path.join(" ");
sweep = (sweep == 0) ? 1 : 0;
r += ringwidth;
var dy = r * S;
var dx = r * C;
if (thickness != 0) {
path = path.concat(
"L", X - dx, Y - dy - thickness, // start pos
"L", X - dx, Y - dy + thickness,
"A", r, r * heightscale, // radii
tilt*180/Math.PI, 1, sweep,
X + dx, Y + dy + thickness, // end pos
"L", X + dx, Y + dy - thickness, // start pos
"z"); // close path
} else {
path = path.concat(
"L", X - dx, Y - dy, // start pos
"A", r, r * heightscale, // radii
tilt*180/Math.PI, 1, sweep,
X + dx, Y + dy, // start pos
"z"); // close path
}
return path.join(" ");
}
function addGradientStops(selection, offsets, colors, opacities) {
// Append stops with the stated offsets, colors, opacities to the selection.
var c;
for (i=0; i < offsets.length; i++) {
c = typeof colors == 'string' ? colors : colors[i];
selection.append("stop")
.attr("offset", offsets[i] + "%")
.style({"stop-color":c, "stop-opacity":opacities[i]});
}
}
function addRings(selection, position) {
// Add path container for rings to the selection; front or back.
var rings = selection
.append("g")
.attr("name", function(d){ return "rings";})
.selectAll("path")
.data(function(d) {return d.Rings;})
.enter().append("path")
.attr("class", function(d) {
var pattern = /Gap/g;
return pattern.test(d.name) ? "rings gap " + position : "rings matter " + position;})
.attr("name", function(d) { return d.name;})
.property("position", position);
// Have to bump the albedo or all rings but Saturn's are invisible here...
rings.filter(".matter")
.style("fill-opacity", function(d) { return 0.042 + d.albedo;})
rings.append("title").html(function(d) { return d.name;});
}
function updateDateSlider(t) {
var pos = (t - t0) / 100;
d3.select("#dateScale .slider").attr({"x":pos - 1.5, "position": pos});
dateVal.setTime((t - 2440587.5) * 86400000 );
d3.select("#dateVal").attr("x", pos).text(dateFormatter(dateVal));
d3.select("#dt").text(Math.round(dt*100)/100 + " days / step");
}
function updateTable(dat) {
// Create or update the data display in the Description panel.
var selected_entries = d3.entries(dat).filter(
function(d) {
if (d.key.length < 5) {
return definitions.hasOwnProperty(d.key) ? true : false;
}
return (d.key == "Rings") ? false : true;});
if (d3.select("th").value != dat.name) {
var table = d3.select("table").selectAll("tr")
.data(selected_entries , function(d) { return (d == null) ? null : d.key;});
table.enter().append("tr").call(function(selection) {
selection.append("td").attr("class", "key");
selection.append("td").attr("class", "value");});
table.exit().remove();
table.selectAll("td")
.text(function(d){
if (this.classList.contains("key")) { return (d.key.length < 5) ? definitions[d.key] : d.key;}
d.value = dat[d.key];
return d.value.toLocaleString();});
d3.select("table").insert("tr", "tr").append("th").text(dat.name).attr("colspan", 2);
}
}
/*--------------------------------------------------------- Slider for date scale --- */
// UTC in seconds = ($julianDay - 2440587.5) * 86400;
d3.select("#dateScale").append("svg")
.attr({"viewBox":"-260 -12 710 30", "preserveAspectRatio":"xMidYMid meet"})
.call(function(tInput) {
tInput.append("line")
.attr({"x1":"-196.5", "x2":"385.33", "y1":"-9", "y2":"-9"})
.style({"stroke-width":3, "stroke":"royalblue"});
dateVal.setTime((t0 - 19650 - 2440587.5) * 86400000 );
tInput.append("svg:text").attr({"x":-199, "y":-7})
.style({"font-size":11, "alignment-baseline":"middle", "text-anchor":"end", "fill":"lightblue"})
.text(dateFormatter(dateVal));
dateVal.setTime((t0 + 38534 - 2440587.5) * 86400000 );
tInput.append("svg:text").attr({"x":389, "y":-7})
.style({"font-size":11, "alignment-baseline":"middle", "text-anchor":"start", "fill":"lightblue"})
.text(dateFormatter(dateVal));
tInput.append("svg:text").attr({"id":"dt", "x":448, "y":10})
.style({"font-size":11, "alignment-baseline":"middle","text-anchor":"end", "fill":"lightblue"})
.text(Math.round(dt*100)/100 + " days / step");
tInput.append("rect")
.attr({"class":"slider", "position":0, "x":-2.5, "y":-10.5, "width":5, "height":8})
.style("stroke-width", "2px")
.call( d3.behavior.drag()
.origin(function() { return {"x":this.position, "y":0};})
.on("drag",
function (d) {
var pos = Math.round(d3.select(this).attr("position"));
pos = Math.max(-196.5, Math.min(385.34, pos + d3.event.dx));
t = t0 + pos * 100;
redraw();
}));
dateVal.setTime((t - 2440587.5) * 86400000 );
tInput.append("svg:text").attr({"id":"dateVal", "x":0, "y":8})
.style({"font-size":11, "alignment-baseline":"middle",
"text-anchor":"middle", "fill":"lightblue"})
.text(dateFormatter(dateVal));
});
/*--------------------------------------------------------- Slider for polar angle --- */
d3.select("#PolarAngle").append("svg")
.attr({"viewBox":"-25 -20 35 220", "preserveAspectRatio":"xMinYMin meet"})
.call(function(phiInput) {
phiInput.append("line")
.attr({"x1":"3", "x2":"3", "y1":"-5", "y2":"185"})
.style({"stroke-width":1.5, "stroke":"royalblue"});
for (var y=0; y <= 180; y+=10) {
phiInput
.append("svg:text").attr({"x":-3, "y":y})
.style({"font-size":6, "alignment-baseline":"middle", "text-anchor":"end", "fill":"lightblue"})
.text( y + "\u00B0");
}
phiInput
.append("svg:text").attr({"x":-21, "y":-10})
.style({"font-size":6, "fill":"lightblue"})
.text("Polar Angle");
phiInput
.append("polygon")
.attr({"class":"slider", "points":"0,0 4,3.42 4,-3.42", "position":0})
.call( d3.behavior.drag()
.origin(function() { return {"x":0, "y":this.position};})
.on("drag",
function (d) {
var pos = Math.round(d3.select(this).attr("position"));
pos = Math.max(0, Math.min(180, pos + d3.event.dy));
var points = [0, pos, 4, pos + 3.42, 4, pos - 3.42];
d3.select(this).attr({"points": points.join(", "), "position": pos})
above = pos <= 90 ? true : false;
phi = pos * Math.PI / 180;
redraw();}));
});
/*-------------------------------------------------------- Slider for planet scale --- */
d3.select("#planetScale").append("svg")
.attr({"viewBox":"-16 -12 142 22", "preserveAspectRatio":"xMidYMid meet"})
.call(function(pInput) {
pInput.append("line")
.attr({"x1":0.2, "x2":100, "y1":-9, "y2":-9})
.style({"stroke-width":3, "stroke":"royalblue"});
pInput.append("svg:text").attr({"x":-1.5, "y":-7})
.style({"font-size":9, "alignment-baseline":"middle", "text-anchor":"end", "fill":"lightblue"})
.text("10:1");
pInput.append("svg:text").attr({"x":102, "y":-7})
.style({"font-size":9, "alignment-baseline":"middle", "text-anchor":"start", "fill":"lightblue"})
.text("5000:1");
pInput.append("rect")
.attr({"class":"slider", "position":planetScale / 50,
"x":planetScale / 50 - 2, "y":-12, "width":4, "height":7})
.style("stroke-width", "1px")
.call( d3.behavior.drag()
.origin(function() { return {"x":this.position, "y":0};})
.on("drag",
function (d) {
var pos = Math.round(d3.select(this).attr("position"));
pos = Math.max(0.2, Math.min(100, pos + d3.event.dx));
planetScale = Math.round(pos * 5) * 10;
d3.select(this).attr({"x":planetScale / 50 - 2, "position":planetScale / 50});
d3.select("#scaleVal")
.text("Planet scale : sun = " + Math.round(planetScale) + ":1");
redraw();
}));
pInput.append("svg:text").attr({"id":"scaleVal", "x":-14, "y":5})
.style({"font-size":9, "alignment-baseline":"middle", "text-anchor":"start", "fill":"lightblue"})
.text("Planet scale : sun = " + Math.round(planetScale) + ":1");
});
/*-------------------------------------------------------- Main (planet) section --- */
var system = d3.select("#SolarSystem").append("svg")
.attr({"viewBox":"-32 -32 64 64", "preserveAspectRatio":"xMidYMid meet"})
.append("g")
.call(d3.behavior.zoom().center([0, 0]).scaleExtent([0.5, 70]).on("zoom", zoom))
.append("g");
d3.json("orbitData.json", function (orbits) {
orbits.forEach(function(p) {
p._sin = {
"eqi":Sin(Math.PI * p.eqi / 180),
"i": Sin(Math.PI * p.i / 180) };
p._cos = {
"eqi":Cos(Math.PI * p.eqi / 180),
"i": Cos(Math.PI * p.i / 180) };
p._tan = {
"eqi":Math.tan(Math.PI * p.eqi / 180)};
if (p.name == "Earth") {
updateTable(p);
}
});
addGradientStops(
system.append("defs").selectAll("radialGradient")
.data(orbits)
.enter().append("radialGradient")
.attr("id", function(d) { return "lighting-" + d.name;})
.attr("name", function(d) { return d.name;})
.attr("r", "120%"),
[0, 42, 100], "black", [0, 0.3, 1]);
system.attr("id", "main")
.append("rect")
.attr({"x":"-32", "y":"-32", width:"64", height:"64"})
.style("fill", "url(#main-gradient)");
/* Gradient for shading the sky; centered around the sun. */
addGradientStops(
system.select("defs")
.insert("radialGradient")
.attr("id", "main-gradient")
.attr({"r":"42%", "fx":"50%", "fy":"50%", "cx":"50%", "cy":"50%"}),
[0, 20, 50, 100], "blue", [0.5, 0.1, 0.05, 0]);
/* Gradient for halo around the sun. */
addGradientStops(
system.select("defs")
.insert("radialGradient")
.attr("id", "sun-halo")
.attr({"r":"50%", "fx":"50%", "fy":"50%", "cx":"50%", "cy":"50%"}),
[32, 34, 36, 45, 55, 100],
["white", "white", "yellow", "yellow", "orange", "orange"],
[1, 0.2, 0.8, 0.42, 0.46, 0]);
/* Filter for clouds. */
system.select("defs")
.insert("filter")
.attr("id", "clouds").call(function(clouds) {
clouds
.append("feTurbulence")
.attr( {"baseFrequency": "0.001,0.042", "numOctaves": 3, "stitchTiles": "stitch"});
clouds
.append("feComponentTransfer").call(function(inner) {
['R', 'G', 'B'].map(function(band) {
inner.append("feFunc" + band).attr( {"type":"linear", "slope":12});});
});
clouds
.append("feComposite")
.attr({"operator":"arithmetic", "k2":0.2, "k3":0.5, "in":"SourceGraphic"});
clouds
.append("feComposite")
.attr({"operator":"in", "in2":"SourceGraphic"});
});
/* Planet content -- orbits and other data. */
var planets = system.selectAll("g")
.data(orbits)
.enter().append("g")
.attr("class", "group")
.attr("name", function(d) { return d.name;})
.on("mouseover", updateTable);
planets.append("ellipse")
.attr("class", "orbit")
.attr("name", function(d) {return d.name;})
.attr({"rx":0, "ry":0})
.attr("cx", function(d) {return (d.e * d.a * d._cos.i) / kmPerAu;})
.attr("cy", 0)
.style("stroke-width", 0.125);
planets.filter(function(d){ return d.Rings != "No";}).call(addRings, "back")
planets.append("circle")
.attr({"class":"planet", "r":0})
.attr("id", function(d) { return d.name;})
.attr("name", function(d) { return d.name;})
.style({"stroke-width":0.142});
planets.append("path")
.attr({"class":"equator"})
.attr("name", function(d) { return d.name;})
.style("stroke-width", 0.142);
planets.filter(function(d){return d.r > 7000;}).append("circle")
.attr({"class":"planet clouds", "r":0})
.attr("name", function(d) { return d.name;})
.style({"filter":"url(#clouds)"});
planets.append("circle")
.attr({"class":"planet light", "r":0})
.attr("name", function(d) { return d.name;})
.style({"fill":function(d) { return "url(#lighting-" + d.name + ")";}});
planets.append("circle")
.attr({"class":"daymark", "r":0.12 / kmPerAu})
.attr("name", function(d) { return d.name;});
planets.append("svg:text")
.attr({"class":"planetName", "x":0, "y":0})
.attr("name", function(d) { return d.name;})
.style("font-size", function(d) { return 1.25;})
.text(function(d) { return d.name;});
planets.filter(function(d){ return d.Rings != "No";}).call(addRings, "front");
system.insert("g").attr({"class":"group", "name":"Sun"})
.call(function(sungroup) {
sungroup
.append("circle")
.attr({"id":"Sun", "cx":"0", "cy":"0"})
.attr("r", 3 * sun.r / kmPerAu )
.style("fill", "url(#sun-halo)");
sungroup
.append("svg:text")
.attr({"class":"planetName", "x":sun.r / kmPerAu, "y": -sun.r / kmPerAu})
.attr("name", "Sun")
.style("font-size", 1.25)
.text("Sun");
});
system.insert("g")
.attr({"class":"group", "name":"Asteroid Belt"})
.append("path").datum(asteroids)
.attr({"class":"Asteroid-belt front"})
.property("position", "front")
.style({"fill":"black", "fill-opacity":0.08, "stroke":"none"})
.append("title").html("Asteroid Belt");
system.insert("g", ".group[name=Jupiter]")
.attr({"class":"group", "name":"Asteroid Belt"})
.append("path").datum(asteroids)
.attr({"class":"Asteroid-belt back"})
.property("position", "back")
.style({"fill":"black", "fill-opacity":0.08, "stroke":"none"})
.append("title").html("Asteroid Belt");
redraw();
} // END create
) // END d3.json
function zoom() {
currentZoom = d3.event.scale;
system.attr("transform", "translate(" + d3.event.translate + ")scale(" + currentZoom + ")");
system.selectAll(".orbit").style("stroke-width", 0.125 / currentZoom);
system.selectAll(".planetName").style("font-size", 1.25 / currentZoom);
system.selectAll(".planet").style("stroke-width", 0.142 / currentZoom);
system.selectAll(".equator").style({"stroke-width":0.142 / currentZoom});
redraw();
}
function redraw() {
var _sin = {"phi": Sin(phi)}, _cos = {"phi": Cos(phi)};
system.selectAll(".group").filter(function(d){ return d != null;}).each(function(g) {
var a, current, r, retrograde, R, tilt, TA, X, Y;
current = d3.select(this);
retrograde = g.eqi > 90;
TA = getTA(g.name, g.e);
_sin.TA = Sin(TA);
_cos.TA = Cos(TA);
_sin.t = Sin(t / g.p);
_cos.t = Cos(t / g.p);
tilt = Math.atan2(g._tan.eqi, -_sin.phi);
r = g.r * planetScale / kmPerAu;
R = getRadius(TA, g.a, g.e);
/* Perihelion is left of the sun. X axis points left; Y axis points down. */
X = - R * g._cos.i * _cos.TA;
Y = R * (_sin.TA * _cos.phi - g._sin.i * _cos.TA * _sin.phi);
current.selectAll(".orbit")
.attr("rx", function(d) {
return d.a * Math.sqrt(Pow(d._cos.i,2) + Pow(d._sin.i,2)*Pow(_sin.phi,2) )/ kmPerAu;})
.attr("ry", function(d) {
return Math.sqrt(Pow(d.a, 2) * (1 - Pow(d.e, 2)) * Pow(_cos.phi,2)) / kmPerAu;})
.attr("transform", function(d) {
return "rotate(" + (Math.atan2(d._sin.i*_sin.phi, d._cos.i) * 180 / Math.PI) +
"," + d.e * d.a * Math.sqrt(
Pow(d._cos.i,2) + Pow(d._sin.i,2)*Pow(_sin.phi,2) )/ kmPerAu +
",0)";});
current.selectAll(".planet")
.attr({"r":r, "cx":X, "cy":Y});
current.selectAll(".daymark")
.attr("r", function(d) { return (r < 0.12) ? r / 10 : 0.1 / currentZoom;})
.attr("cx", function(d) {
return X + _cos.t * d._cos.eqi * r;})
.attr("cy", function(d) {
return Y - (_sin.t * _cos.phi + d._sin.eqi * _sin.phi * _cos.t) * r;})
.style("fill", function(d) {
var crossprod =
_sin.phi * (- _sin.t * _cos.phi - d._sin.eqi * _sin.phi * _cos.t )
- d._tan.eqi * ( - _cos.t * d._cos.eqi );
var sweep = (retrograde == ! above) ? 1 : 0;
return (crossprod > 0) == (sweep == 0) ? "none" : "darkgray";});
current.selectAll(".equator")
.attr("d",
function(d) {
var ry, dx, dy, path, sweep;
ry = r * _cos.phi * d._cos.eqi;
sweep = (retrograde == above) ? 1 : 0;
dy = -r * Sin(tilt);
dx = -r * Cos(tilt);
path = ["M",
X-dx, Y-dy, // start pos
"A", r , ry, // radii
tilt*180/Math.PI, 1, sweep,
// ellipse rotation, large arc, sweep direction
X+dx, Y+dy]; // end pos
return path.join(" ");
});
current.selectAll(".rings")
.attr("d",
function(d) {
var sweep;
if (this.classList.contains("front")) { sweep = (retrograde == above) ? 1 : 0;
} else { sweep = (retrograde == above) ? 0 : 1; }
return getHalfRingPath(X, Y, tilt,
d.distance * planetScale / kmPerAu,
_cos.phi * g._cos.eqi,
sweep,
d.width * planetScale / kmPerAu);
});
current.selectAll(".planetName")
.attr("x", function(d) { return X + planetScale * 0.8 * d.r / kmPerAu;})
.attr("y", function(d) { return Y + planetScale * 1.1 * d.r / kmPerAu;});
d3.select("#lighting-" + g.name)
.attr("fx", function(d) { return (50 + 45 * _cos.TA ) + "%";})
.attr("fy", function(d) { return (50 - 45 * _sin.TA * _cos.phi) + "%";})
.attr("cx", function(d) { return (50 + 25 * _cos.TA ) + "%";})
.attr("cy", function(d) { return (50 - 25 * _sin.TA * _cos.phi) + "%";});
});
d3.selectAll(".Asteroid-belt")
.attr("d",
function(d) {
return getHalfRingPath(0, 0, 0,
d.distance / kmPerAu,
_cos.phi,
(this.classList.contains("front") == above) ? 1 : 0,
(d.width > 100 ? d.width : 100) / kmPerAu,
d.thickness * Sin(phi) / kmPerAu);});
updateDateSlider(t);
}
d3.select("#startbtn").on("click", function() { paused = false;});
d3.select("#pausebtn").on("click", function() { paused = true;});
d3.select("#fasterbtn").on("click", function() { dt *= 1.1; redraw();});
d3.select("#slowerbtn").on("click", function() { dt *= 0.9; redraw();});
d3.timer(function() {
if (! paused && t < 2516022.5) {
t += dt;
redraw();
}
});
//]]>
</script>
</body>
</html>
[
{"Min/Max Surface Temp (degC)": "-173/427", "name": "Mercury", "Density (g/cm3)": 5.427, "Mass (trillion kg)": 330104000000, "Moons": "None", "Major Atmospheric Constituents": "No atmosphere", "Mean Orbit Velocity (km/h)": 170503, "Rings": "No", "a": 57909227, "Escape Velocity (km/h)": 15300, "r": 2439.7, "i": 7.0, "Equatorial Surface Gravity (m/s2)": 3.7, "p": 58.646, "e": 0.20563593, "eqi": 0},
{"Min/Max Surface Temp (degC)": "462", "name": "Venus", "Density (g/cm3)": 5.243, "Mass (trillion kg)": 4867320000000, "Moons": "None", "Major Atmospheric Constituents": "Carbon Dioxide Nitrogen", "Mean Orbit Velocity (km/h)": 126074, "Rings": "No", "a": 108209475, "Escape Velocity (km/h)": 37296, "r": 6051.8, "i": 3.39, "Equatorial Surface Gravity (m/s2)": 8.87, "p": -243.018, "e": 0.00677672, "eqi": 177.3, "retrograde rotation":true},
{"Min/Max Surface Temp (degC)": "-88/58 (min/max)", "name": "Earth", "Density (g/cm3)": 5.513, "Mass (trillion kg)": 5972190000000, "Moons": 1, "Major Atmospheric Constituents": "Nitrogen Oxygen", "Mean Orbit Velocity (km/h)": 107218, "Rings": "No", "a": 149598262, "Escape Velocity (km/h)": 40284, "r": 6371.00, "i": 0.00005, "Equatorial Surface Gravity (m/s2)": 9.80665, "p": 0.99726968, "e": 0.01671123, "eqi": 23.4393 },
{"Min/Max Surface Temp (degC)": "-87 to -5", "name": "Mars", "Density (g/cm3)": 3.934, "Mass (trillion kg)": 641693000000, "Moons": 2, "Major Atmospheric Constituents": "Carbon Dioxide Nitrogen Argon", "Mean Orbit Velocity (km/h)": 86677, "Rings": "No", "a": 227943824, "Escape Velocity (km/h)": 18108, "r": 3389.5, "i": 1.85, "Equatorial Surface Gravity (m/s2)": 3.71, "p": 1.026, "e": 0.0933941, "eqi": 25.2},
{"Min/Max Surface Temp (degC)": "Gas giant; no surface", "name": "Jupiter", "Density (g/cm3)": 1.326, "Mass (trillion kg)": 1898130000000000, "Moons": 67, "Major Atmospheric Constituents": "Hydrogen Helium", "Mean Orbit Velocity (km/h)": 47002, "Rings": [{"name":"Halo", "distance":100000, "width":22800, "albedo":0.0001}, {"name":"Main", "distance":122800, "width":6400, "albedo":0.015}, {"name":"Gossamer", "distance":129200, "width":85000, "albedo":0.00005}], "a": 778340821, "Escape Velocity (km/h)": 216720, "r": 69911, "i": 1.304, "Equatorial Surface Gravity (m/s2)": 24.79, "p": 0.41354, "e": 0.04838624, "eqi": 3.1},
{"Min/Max Surface Temp (degC)": "Gas giant; no surface", "name": "Saturn", "Density (g/cm3)": 0.687, "Mass (trillion kg)": 568319000000000, "Moons": 62, "Major Atmospheric Constituents": "Hydrogen Helium", "Mean Orbit Velocity (km/h)": 34701, "Rings": [
{"name": "D", "distance": 66970, "width": 7500, "albedo": 0.2 }, {"name": "C", "distance": 74490, "width": 17500, "albedo": 0.21 }, {"name": "Columbo Gap", "distance": 77800, "width": 100 }, {"name": "Maxwell Gap", "distance": 87500, "width": 270 }, {"name": "Bond Gap", "distance": 88690, "width": 30 }, {"name": "Dawes Gap", "distance": 90200, "width": 20 }, {"name": "B", "distance": 91980, "width": 25500, "albedo": 0.5 }, {"name": "Cassini Division", "distance": 117500, "width": 4700, "albedo": 0.3 }, {"name": "Huygens Gap", "distance": 117680, "width": 285 }, {"name": "Herschel Gap", "distance": 118183, "width": 102 }, {"name": "Russell Gap", "distance": 118597, "width": 33 }, {"name": "Jeffreys Gap", "distance": 118931, "width": 38 }, {"name": "Kuiper Gap", "distance": 119403, "width": 3 }, {"name": "Leplace Gap", "distance": 119848, "width": 238 }, {"name": "Bessel Gap", "distance": 120305, "width": 10 }, {"name": "Barnard Gap", "distance": 120305, "width": 13 }, {"name": "A", "distance": 122050, "width": 14600, "albedo": 0.4 }, {"name": "Encke Gap", "distance": 133570, "width": 325 }, {"name": "Keeler Gap", "distance": 136530, "width": 35 }, {"name": "Roche Division", "distance": 136770, "width": 2600, "albedo":0.2 }, {"name": "F", "distance": 140224, "width": 30, "albedo": 0.6 }, {"name": "G", "distance": 166000, "width": 8000, "albedo": 0.05 }, {"name": "E", "distance": 180000, "width": 300000, "albedo": 0.001}
], "a": 1426666422, "Escape Velocity (km/h)": 129924, "r": 58232, "i": 2.49, "Equatorial Surface Gravity (m/s2)": 10.4, "p": 0.444, "e": 0.05386179, "eqi": 26.7},
{"Min/Max Surface Temp (degC)": "Gas giant; no surface", "name": "Uranus", "Density (g/cm3)": 1.270, "Mass (trillion kg)": 86810300000000, "Moons": 27, "Major Atmospheric Constituents": "Hydrogen Helium Methane", "Mean Orbit Velocity (km/h)": 24477, "Rings": [
{"name": "Zeta (1986 U2R)", "distance": 39600, "width": 3500, "albedo": 0.03 }, {"name": "6", "distance": 41840, "width": "2", "albedo": 0.03 }, {"name": "5", "distance": 42230, "width": 2.5, "albedo": 0.03 }, {"name": "4", "distance": 42580, "width": 2.5, "albedo": 0.03 }, {"name": "Alpha", "distance": 44720, "width": 9.5, "albedo": 0.03 }, {"name": "Beta", "distance": 45670, "width": 9.5, "albedo": 0.03 }, {"name": "Eta", "distance": 47190, "width": 1, "albedo": 0.03 }, {"name": "Gamma", "distance": 47630, "width": 2.5, "albedo": 0.03 }, {"name": "Delta", "distance": 48290, "width": 6, "albedo": 0.03 }, {"name": "Lambda (1986 U1R)", "distance": 50024, "width": 2.5, "albedo": 0.03 }, {"name": "Epsilon", "distance": 51140, "width": 60, "albedo": 0.038 }, {"name": "Nu (R/2003 U 2)", "distance": 67300, "width": 3800, "albedo": 0.001}, {"name": "Mu (R/2003 U 1)", "distance": 97700, "width": 17000, "albedo": 0.001}
], "a": 2870658186, "Escape Velocity (km/h)": 76968, "r": 25362, "i": 0.77, "Equatorial Surface Gravity (m/s2)": 8.87, "p": -0.718, "e": 0.04725744, "eqi": 97.8, "retrograde rotation":true},
{"Min/Max Surface Temp (degC)": "Gas giant; no surface", "name": "Neptune", "Density (g/cm3)": 1.638, "Mass (trillion kg)": 102410000000000, "Moons": 14, "Major Atmospheric Constituents": "Hydrogen Helium Methane", "Mean Orbit Velocity (km/h)": 19566, "Rings": [
{"name": "1989 N3R Galle", "distance": 41900, "width": 15, "albedo": 0.015}, {"name": "1989 N2R Leverrier", "distance": 53200, "width": 15, "albedo": 0.015}, {"name": "Lassel", "distance": 55400, "width": 1, "albedo": 0.015}, {"name": "Arago", "distance": 57600, "width": 1, "albedo": 0.001}, {"name": "1989 N1R Adams", "distance": 57600, "width": 1, "albedo": 0.015}, {"name": "Liberte ('Leading' Adams arc)", "distance": 62930, "width": 42, "albedo": 0.001}, {"name": "Egalite ('Equidistant' Adams arc)", "distance": 62900, "width": 1, "albedo": 0.04}, {"name": "Fraternite ('Following' Adams arc)", "distance": "62900", "width": 1, "albedo": 0.001}, {"name": "Courage (arc)", "distance": "62900", "width": 1, "albedo": 0.001}
], "a": 4498396441, "Escape Velocity (km/h)": 84816, "r": 24622, "i": 1.77, "Equatorial Surface Gravity (m/s2)": 11.15, "p": 0.671, "e": 0.00859048, "eqi": 28.3}
]
a:link {color: mediumblue;}
a:visited {color: darkslateblue;}
a:hover {color: blue;}
a:active {color: royalblue;}
body { background: black; color: dimgray; font-size: 80%; font-family: Times;}
button { background: lightblue; width: 6.25vw;
border: aliceblue; border-radius: 1px;
font-size: 55%; font-family: Times; font-weight: lighter;}
button:hover { background: aliceblue;}
button:active { background: royalblue;}
div { float: left;}
h1 { color: lightgray; font-size: 150%; font-weight: lighter;}
li { list-style-type: none;}
table { color: darkgray;}
th { border: 3px solid black; font-size: 85%; font-weight: lighter;}
td { border: 0px solid black; font-size: 65%;}
ul { padding: 0;}
input[type=range] {
-webkit-appearance: none;
background-color: royalblue; height: 8px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
background-color: royalblue;
border: 1.5px solid lightblue;
width: 6px;
height: 15px;
}
.hidden{ display: none;}
.slider { fill: royalblue; stroke: lightblue;}
.slider:hover { fill: deepskyblue; stroke:white;}
#buttons { width: 13.5%; padding-left: 0.25%;}
#dateScale { width: 65%;}
#dateVal { pointer-events: none;}
#Description { width: 28%; padding-top: 3%; padding-left: 2.5%;}
#OtherControls { width: 100%; height:6%;}
#planetScale{ width: 17%; padding-left: 1%; padding-right: 0.25%; color: dimgray;}
#planetScale input { width: 70%; height: 5px}
#PolarAngle { width: 6%; height: 90%; padding-top: 3%;}
#SolarSystem { width: 60%; height: 0; padding-bottom: 60%;}
svg { width: 100%; height:100%;}
ellipse { fill: none; stroke: rgba(255,255,255,0.15); }
.clouds { pointer-events: none; }
.daymark { fill: gray; stroke: none; }
.equator { fill: none; stroke: darkgray; stroke-linecap:butt; stroke-opacity:0.4; }
.light { pointer-events: none; }
.planetName { fill: gray; }
.matter { stroke: none; fill: papayawhip; }
.gap { stroke: none; fill: black; fill-opacity: 0.5; }
.group:not(:hover) .planetName { display: none; }
#Mercury { fill: hsl(20, 10%, 50%);}
.group:hover #Mercury { fill: hsl(20, 20%, 60%);}
#Venus { fill: hsl(50, 42%, 65%);}
.group:hover #Venus { fill: hsl(50, 55%, 79%);}
#Earth { fill: hsl(255, 80%, 50%);}
.group:hover #Earth { fill: hsl(255, 90%, 60%);}
#Mars { fill: hsl(18, 85%, 60%);}
.group:hover #Mars { fill: hsl(18, 95%, 70%);}
#Jupiter { fill: hsl(42, 25%, 55%);}
.group:hover #Jupiter { fill: hsl(42, 35%, 65%);}
#Saturn { fill: hsl(58, 40%, 55%); }
.group:hover #Saturn { fill: hsl(58, 50%, 65%);}
#Uranus { fill: hsl(190, 90%, 50%);}
.group:hover #Uranus { fill: hsl(190, 100%, 60%);}
#Neptune { fill: hsl(225, 90%, 50%); }
.group:hover #Neptune { fill: hsl(225, 100%, 60%);}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment