Published
Edited
Dec 24, 2019
10 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function* drawing() {
replay;
const EPSILON = Math.pow(2, -52);
context.clearRect(0,0,context.canvas.width,context.canvas.height);
context2.clearRect(0,0,context2.canvas.width,context2.canvas.height);
drawCircles(context);
drawTriangles(context);
//object to hold all contours for all levels
let allContours = {};
//holds all contour segments which will be joined into contour lines
let levSegs;
//quadtrees
let quadstart, quadend;
//contour levels to compute and plot
let contourLevels = linspace(-1,1,11);

for (let i = 0 ; i < contourLevels.length ; ++i) {
allContours[contourLevels[i]]=[];
levSegs = createSegments(contourLevels[i]);
//if no segments for contour level, continue to next level
if (levSegs.length == 0) continue;
//create two quadtrees, one generated using segment starting point and the other using segment ending point
quadstart = d3.quadtree(levSegs, function(d) { return d.p1.x ;}, function(d) { return d.p1.y; });
quadend = d3.quadtree(levSegs, function(d) { return d.p2.x ;}, function(d) { return d.p2.y; });
//initialize contour line
let contour = [];
let testSeg;
//loop through all segments
for (let j = 0; j < levSegs.length ; ++j) {
//if segment has already been tested for inclusion into contour, continue
if (levSegs[j].visited == true) continue;
contour.push(levSegs[j].p1);
allContours[contourLevels[i]].push(contour);
testSeg = levSegs[j];
testSeg.visited = true;

//find segment with starting point closest to this segment ending point
let closest = quadstart.find(testSeg.p2.x, testSeg.p2.y);
//if found segment sufficiently close (location should be equal, but floating point error means we can't compare using equality) and not already tested for inclusion into contour
while (Math.abs(closest.p1.x - testSeg.p2.x) < EPSILON && Math.abs(closest.p1.y - testSeg.p2.y) < EPSILON && !closest.visited) {
closest.visited = true;
contour.push(closest.p1);
testSeg = closest;
closest = quadstart.find(testSeg.p2.x, testSeg.p2.y);
//replace contour with updated contour
allContours[contourLevels[i]][allContours[contourLevels[i]].length-1] = contour;
//render updated contour
yield renderContour(context2, allContours);
}
//check if contour needs to be closed
if (Math.abs(testSeg.p2.x - contour[0].x) < EPSILON && Math.abs(testSeg.p2.y - contour[0].y) < EPSILON) {
contour.push(contour[0]);
contour.push(contour[1]);
allContours[contourLevels[i]][allContours[contourLevels[i]].length-1] = contour;
yield renderContour(context2, allContours);
}
//reverse contour to test the other direction
let reversedContour = contour.reverse();
testSeg = levSegs[j];
closest = quadend.find(testSeg.p1.x, testSeg.p1.y);
while (Math.abs(closest.p2.x - testSeg.p1.x) < EPSILON && Math.abs(closest.p2.y - testSeg.p1.y) < EPSILON && !closest.visited) {
closest.visited = true;
reversedContour.push(closest.p1);
testSeg = closest;
closest = quadend.find(testSeg.p1.x, testSeg.p1.y);
allContours[contourLevels[i]][allContours[contourLevels[i]].length-1] = reversedContour;
yield renderContour(context2, allContours);
}
//check if contour needs to be closed
if (Math.abs(testSeg.p2.x - reversedContour[0].x) < EPSILON && Math.abs(testSeg.p2.y - reversedContour[0].y) < EPSILON) {
reversedContour.push(reversedContour[0]);
reversedContour.push(reversedContour[1]);
allContours[contourLevels[i]][allContours[contourLevels[i]].length-1] = reversedContour;
yield renderContour(context2, allContours);
}
//reset contour
contour = [];
}
}
//renderContour(context2,allContours);
yield allContours;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more