|
function main(domSelector) { |
|
|
|
// Sphere data: list of [radius, cx, cy, cz, cw] |
|
var data = [ |
|
// Origin |
|
[1, 0, 0, 0, 0], |
|
// Hypercube corners |
|
[1, 1, 1, 1, 1], |
|
[1, 1, 1, 1, -1], |
|
[1, 1, 1, -1, 1], |
|
[1, 1, 1, -1, -1], |
|
[1, 1, -1, 1, 1], |
|
[1, 1, -1, 1, -1], |
|
[1, 1, -1, -1, 1], |
|
[1, 1, -1, -1, -1], |
|
[1, -1, 1, 1, 1], |
|
[1, -1, 1, 1, -1], |
|
[1, -1, 1, -1, 1], |
|
[1, -1, 1, -1, -1], |
|
[1, -1, -1, 1, 1], |
|
[1, -1, -1, 1, -1], |
|
[1, -1, -1, -1, 1], |
|
[1, -1, -1, -1, -1] |
|
]; |
|
|
|
|
|
var color = d3.scaleOrdinal(d3.schemeCategory20); |
|
|
|
var width = 460; |
|
var height = 460; |
|
|
|
var scaleRight = d3.scaleLinear().domain([-2, 2]).range([-0.45 * width, 0.45 * width]); |
|
var scaleUp = d3.scaleLinear().domain([-2, 2]).range([0.45 * height, -0.45 * height]); |
|
|
|
var svg = d3.select(domSelector).append('svg') |
|
.attr("width", 2 * width) |
|
.attr("height", height); |
|
|
|
|
|
function setup(svg, xName, yName, zName, wName) { |
|
var plot = svg.append('g') |
|
.attr("class", "plot"); |
|
|
|
// Draw axes |
|
var axes = plot.append('g').attr("class", "axes"); |
|
axes.append('g').call(d3.axisBottom(scaleRight)); |
|
axes.append('text') |
|
.attr("class", "label") |
|
.attr("x", scaleRight(2)).attr("y", -5) |
|
.text(xName); |
|
axes.append('g').call(d3.axisLeft(scaleUp)); |
|
axes.append('text') |
|
.attr("class", "label") |
|
.attr("x", 10).attr("y", scaleUp(2)).attr("dy", "0.32em") |
|
.text(yName); |
|
|
|
var title = plot.append("text").attr("class", "title").attr("y", scaleUp(2) - 10); |
|
|
|
function setTitle(z, w) { |
|
var f = d3.format(".3f"); |
|
title.text(xName + yName + "-Slice at " + zName + "=" + f(z) + ", " + wName + "=" + f(w)); |
|
} |
|
|
|
// Layer to draw spheres |
|
var spheres = plot.append("g").attr("class", "spheres"); |
|
|
|
// Slice indicator |
|
var slice = plot.append("g").attr("class", "slice"); |
|
var s = scaleRight(0.05) - 0; |
|
slice.append("circle").attr("r", 0.8 * s); |
|
slice.append("line").attr("x1", -s).attr("x2", s); |
|
slice.append("line").attr("y1", -s).attr("y2", s); |
|
|
|
function setSlice(x, y) { |
|
slice.attr("transform", "translate(" + scaleRight(x) + "," + scaleUp(y) + ")"); |
|
} |
|
|
|
// Overlay to handle drag events. |
|
var overlay = plot.append("rect") |
|
.attr("class", "overlay") |
|
.attr("x", -0.5 * width).attr("y", -0.5 * height) |
|
.attr("width", width).attr("height", height); |
|
|
|
return { |
|
"plot": plot, |
|
"overlay": overlay, |
|
"spheres": spheres, |
|
"axes": axes, |
|
"slice": slice, |
|
"setSlice": setSlice, |
|
"setTitle": setTitle |
|
}; |
|
} |
|
|
|
function drawPlot(plot, cxi, cyi, zi, wi, z, w) { |
|
plot.setTitle(z, w); |
|
// Update |
|
var spheres = plot.spheres.selectAll("circle") |
|
.data(data); |
|
// Enter |
|
spheres.enter() |
|
.append('circle') |
|
.attr('cx', function (d) {return scaleRight(d[cxi]);}) |
|
.attr('cy', function (d) {return scaleUp(d[cyi]);}) |
|
.style("stroke", function (d, i) {return color(i);}) |
|
// Update + enter |
|
.merge(spheres) |
|
.attr("r", function (d) { |
|
// (x-cx)^2 + (y-cy)^2 + (z-cz)^2 + (w-cw)^2 = r^2 |
|
var r2 = d[0] * d[0] - (z - d[zi]) * (z - d[zi]) - (w - d[wi]) * (w - d[wi]); |
|
var r = Math.sqrt(Math.max(0, r2)); |
|
return scaleRight(r) - scaleRight(0) |
|
}) |
|
; |
|
|
|
} |
|
|
|
var xyPlot = setup(svg, "x", "y", "z", "w"); |
|
xyPlot.plot.attr("transform", "translate(" + 0.5 * width + "," + 0.5 * height + ")"); |
|
var zwPlot = setup(svg, "z", "w", "x", "y"); |
|
zwPlot.plot.attr("transform", "translate(" + 1.5 * width + "," + 0.5 * height + ")"); |
|
|
|
function drawAtXY(x, y) { |
|
drawPlot(zwPlot, 3, 4, 1, 2, x, y); |
|
xyPlot.setSlice(x, y); |
|
} |
|
|
|
function drawAtZW(z, w) { |
|
drawPlot(xyPlot, 1, 2, 3, 4, z, w); |
|
zwPlot.setSlice(z, w); |
|
} |
|
|
|
|
|
xyPlot.overlay.call(d3.drag() |
|
.on("drag end", function () { |
|
var where = d3.mouse(this); |
|
drawAtXY(scaleRight.invert(where[0]), scaleUp.invert(where[1])) |
|
}) |
|
); |
|
|
|
zwPlot.overlay.call(d3.drag() |
|
.on("drag end", function () { |
|
var where = d3.mouse(this); |
|
drawAtZW(scaleRight.invert(where[0]), scaleUp.invert(where[1])) |
|
}) |
|
); |
|
|
|
|
|
drawAtXY(0.1, 0.2); |
|
drawAtZW(0.3, 0.4); |
|
|
|
} |