Contour Lines Island

This turtle uses the Metaball Contour Lines utility code to visualize the heightfield of a small island (created using Simplex Noise). Todo: add compass.

#contourlines #island #simplex #noise

Log in to post a comment.

// Contour Lines Island. Created by Reinder Nijhoff 2020 - @reindernijhoff
//
// https://turtletoy.net/turtle/09e8dda306
//

const turtle = new Turtle();
const seed = 1; // min=1, max=100, step=1
const noise = SimplexNoise(seed);
const heightFieldCache = [];
const heightFieldLookUp = p => heightFieldCache[p[0]*2+200][p[1]*2+200];

function walk(i) {
    const lines = ContourLines(i*.275+0.1, 1, heightFieldLookUp);
    lines.forEach(line => {
        turtle.jump(line[0]);
        turtle.goto(line[1]);
    });
    return i < 35;
}

// Cache heightfield as an optimization
function heightField(p) {
    let x = p[0]/75, y = p[1]/75;
    let height = noise.noise2D([x,y]);
    height += .50 * noise.noise2D([x*2+3.4,y*2-56.1]);
    height += .25 * Math.abs(1-noise.noise2D([x*4+21.2,y*4+.5]));
    height += .15 * Math.abs(1-noise.noise2D([x*8+421.12,y*8+21.3]));
    height += 1-2.5*((p[0]/100)**2 + (p[1]/100)**2);
    return Math.max(0,height) ** 2;
}
for (let x = -100; x <= 101; x+=.5) {
    heightFieldCache[x*2+200] = [];
	for (let y = -100; y <= 101; y+=.5) {
        heightFieldCache[x*2+200][y*2+200] = heightField([x,y]);
	}
}

////////////////////////////////////////////////////////////////
// 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;
}

////////////////////////////////////////////////////////////////
// Simplex Noise utility code. Created by Reinder Nijhoff 2020
// https://turtletoy.net/turtle/6e4e06d42e
// Based on: http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
////////////////////////////////////////////////////////////////
function SimplexNoise(seed = 1) {
	const grad = [  [1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0],
	            	[1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1],
            		[0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1] ];
	const perm = new Uint8Array(512);
            		
	const F2 = (Math.sqrt(3) - 1) / 2, F3 = 1/3;
	const G2 = (3 - Math.sqrt(3)) / 6, G3 = 1/6;

	const dot2 = (a, b) => a[0] * b[0] + a[1] * b[1];
	const sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]];
	const dot3 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
	const sub3 = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];

	class SimplexNoise {
		constructor(seed = 1) {
			for (let i = 0; i < 512; i++) {
				perm[i] = i & 255;
			}
			for (let i = 0; i < 255; i++) {
				const r = (seed = this.hash(i+seed)) % (256 - i)  + i;
				const swp = perm[i];
				perm[i + 256] = perm[i] = perm[r];
				perm[r + 256] = perm[r] = swp;
			}
		}
		noise2D(p) {
			const s = dot2(p, [F2, F2]);
			const c = [Math.floor(p[0] + s), Math.floor(p[1] + s)];
			const i = c[0] & 255, j = c[1] & 255;
			const t = dot2(c, [G2, G2]);

			const p0 = sub2(p, sub2(c, [t, t]));
			const o  = p0[0] > p0[1] ? [1, 0] : [0, 1];
			const p1 = sub2(sub2(p0, o), [-G2, -G2]);
			const p2 = sub2(p0, [1-2*G2, 1-2*G2]);
			
			let n =  Math.max(0, 0.5-dot2(p0, p0))**4 * dot2(grad[perm[i+perm[j]] % 12], p0);
			    n += Math.max(0, 0.5-dot2(p1, p1))**4 * dot2(grad[perm[i+o[0]+perm[j+o[1]]] % 12], p1);
		    	n += Math.max(0, 0.5-dot2(p2, p2))**4 * dot2(grad[perm[i+1+perm[j+1]] % 12], p2);
			
			return 70 * n;
		}
		hash(i) {
            i = 1103515245 * ((i >> 1) ^ i);
            const h32 = 1103515245 * (i ^ (i>>3));
            return h32 ^ (h32 >> 16);
		}
	}
	return new SimplexNoise(seed);
}