### Metaball Contour Lines

A small and very unoptimized utility turtle that can be used to calculate contour lines. The ContourLines function will return an array of unsorted line-segments.

#contourlines #utility

```// Metaball Contour Lines. Created by Reinder Nijhoff 2020 - @reindernijhoff
//
// https://turtletoy.net/turtle/104c4775c5
//

const turtle = new Turtle();

const ball = () => [-75+Math.random()*150, -75+Math.random()*150, 50+100*Math.random()];
const metaballs = [ball(), ball(), ball(), ball(), ball(), ball()];
const zFunc = p => metaballs.reduce((a, c) => a+c[2]/(1+Math.sqrt((c[0]-p[0])**2+(c[1]-p[1])**2)), 0);

function walk(i) {
const lines = ContourLines(i, 10/(1+i), zFunc);
lines.forEach(line => {
turtle.jump(line[0]);
turtle.goto(line[1]);
});
return i < 25;
}

////////////////////////////////////////////////////////////////
// Contour Lines utility code. Created by Reinder Nijhoff 2020
// https://turtletoy.net/turtle/104c4775c5
////////////////////////////////////////////////////////////////
function ContourLines(z, step, zFunc) {
const intersectSegmentZ = (z, v1, v2) => {
if (v1[2] === v2[2]) return false;
const t = (z - v1[2]) / (v2[2] - v1[2]);
if (t <= 0 || t > 1) return false;
return [v1[0]+(v2[0]-v1[0])*t, v1[1]+(v2[1]-v1[1])*t];
}
const intersectTriangleZ = (z, p1, p2, p3) => {
const p = [];
const v1 = intersectSegmentZ(z, p1, p2);
const v2 = intersectSegmentZ(z, p2, p3);
const v3 = intersectSegmentZ(z, p3, p1);
if (v1 && v2) p.push([v1, v2]);
if (v1 && v3) p.push([v1, v3]);
if (v2 && v3) p.push([v2, v3]);
return p;
}
const result = [];
for (let x = -100; x <= 100; x += step) {
for (let y = -100; y <= 100; y += step) {
const corners = [[x, y], [x+step, y], [x+step, y+step], [x, y+step]];
corners.forEach( c => c[2] = zFunc(c) );
const c3 = [x+step/2, y+step/2, zFunc([x+step/2, y+step/2])];
for (let i=0; i<4; i++) {
result.push(...intersectTriangleZ(z, corners[i], corners[(i+1) & 3], c3));
}
}
}
return result;
}```