Kaleidoscoped new land

Based on codepen.io/ge1doot/pen/vymqvp

Log in to post a comment.

// Forked from "our new land" by ge1doot
// https://turtletoy.net/turtle/b2fdeed2d8

const mirrors = 8; //min=2 max=30 step=1

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(0.5);

// Global code will be evaluated once.
const turtle = new Kaleidoscope(0, 0, mirrors);
//const turtle = new Turtle();
turtle.penup();

class Noise {
	// http://mrl.nyu.edu/~perlin/noise/
	constructor(setup) {
		this.p = new Uint8Array(512);
		this.octaves = setup.octaves || 1;
		const p = new Uint8Array(256);
		for (let i = 0; i < 256; i++) p[i] = i;
		for (let i = 255; i > 0; i--) {
			const n = Math.floor((i + 1) * Math.random());
			const q = p[i];
			p[i] = p[n];
			p[n] = q;
		}
		for (let i = 0; i < 512; i++) {
			this.p[i] = p[i & 255];
		}
	}
	lerp(t, a, b) {
		return a + t * (b - a);
	}
	grad2d(i, x, y) {
		const v = (i & 1) === 0 ? x : y;
		return (i & 2) === 0 ? -v : v;
	}
	noise2d(x2d, y2d) {
		const X = Math.floor(x2d) & 255;
		const Y = Math.floor(y2d) & 255;
		const x = x2d - Math.floor(x2d);
		const y = y2d - Math.floor(y2d);
		const fx = (3 - 2 * x) * x * x;
		const fy = (3 - 2 * y) * y * y;
		const p0 = this.p[X] + Y;
		const p1 = this.p[X + 1] + Y;
		return this.lerp(
			fy,
			this.lerp(
				fx,
				this.grad2d(this.p[p0], x, y),
				this.grad2d(this.p[p1], x - 1, y)
			),
			this.lerp(
				fx,
				this.grad2d(this.p[p0 + 1], x, y - 1),
				this.grad2d(this.p[p1 + 1], x - 1, y - 1)
			)
		);
	}
	noise(x, y) {
		let e = 1,
			k = 1,
			s = 0;
		for (let i = 0; i < this.octaves; ++i) {
			e *= 0.5;
			s += e * (1 + this.noise2d(k * x, k * y)) / 2;
			k *= 2;
		}
		return s;
	}
}
const perlin = new Noise({ octaves: 2 });
let y = 100;
const line = Array.from({ length: 200 }, () => 100);

// The walk function will be called until it returns false.
function walk(i) {
    y -= 0.3;
	turtle.goto(-100, y);
	turtle.down();
	let v = false;
	for (let x = -100; x < 100; x++) {
		let z = y +	0.25 * (
		    (2048 * perlin.noise(x * 0.02, 6 + y * 0.04)) & (16 + 32 + 0 + 128 + 256)
		);
		if (z > -100) v = true;
		if (z > line[x]) z = line[x] - 0.01;
		line[x] = z;
		turtle.goto(x, z);
	}
	turtle.up();
	return v;
}


function Kaleidoscope(x, y, mirrors = 4) {
    
    function rot2(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; }
    function trans2(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; }
    function segment_intersect2(a,b,d,c) {
        const e=(c[1]-d[1])*(b[0]-a[0])-(c[0]-d[0])*(b[1]-a[1]);
        if(0==e)return false;
        c=((c[0]-d[0])*(a[1]-d[1])-(c[1]-d[1])*(a[0]-d[0]))/e;
        d=((b[0]-a[0])*(a[1]-d[1])-(b[1]-a[1])*(a[0]-d[0]))/e;
        return 0<=c&&1>=c&&0<=d&&1>=d?[a[0]+c*(b[0]-a[0]),a[1]+c*(b[1]-a[1])]:false;
    }
    
    function pointInTriangle(pt, ...v) {
        const signFn = (p1, p2, p3) => (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
        const signs = v.map((v, i, arr) => signFn(pt, v, arr[(i+1)%arr.length]));
        return !(signs.reduce((p, c) => p || c < 0, false) && signs.reduce((p, c) => p || c > 0, false));
    }

    class Kaleidoscope extends Turtle {
        constructor(x, y, n = 4) {
            super(x, y);
            
            this.boundaries = [[0,0], [0, 1000], trans2(rot2(2*Math.PI/(n*2)), [0,1000])];
            
            const rotations = Array.from({length: n}).map((v, i) => {
                return (pt) => trans2(rot2(i * 2 * Math.PI / n), pt);
            });
            const mirrors = rotations.map(r => (pt) => trans2([-1, 0, 0, 1], r(pt)));
    
            this.transforms = [...rotations, ...mirrors];
    
            this.cur = this.pos();
            this.curIn = this.inSegment(this.cur);
    
            this.turtle = new Turtle(x, y);
        }
        inSegment(pt) {
            return pointInTriangle(pt, ...this.boundaries);
        }
        goto(x, y) {
            const target = Array.isArray(x)? x: [x, y];
            const isDown = this.isdown();
    
            const targetIn = this.turtle === undefined? false: this.inSegment(target);
            if (isDown) {
                (() => {
                    const bOne = segment_intersect2(this.cur, target, this.boundaries[0], this.boundaries[1]);
                    const bTwo = segment_intersect2(this.cur, target, this.boundaries[0], this.boundaries[2]);
                    debugger;
                    let from = this.cur;
                    let to = target;
                    if(!this.curIn && !targetIn) {
                        if(bOne === false && bTwo === false) return;
                        from = bOne;
                        to = bTwo;
                    } else if(!this.curIn) {
                        from = bOne === false? bTwo: bOne;
                    } else if(!targetIn) {
                        to = bOne === false? bTwo: bOne;
                    }
                    this.transforms.forEach(t => {
                        this.turtle.jump(t(from));
                        this.turtle.goto(t(to));
                    });
                })();
            }
            
            this.cur = target;
            this.curIn = targetIn
    
            this.up();
            super.goto(x, y);
            if(isDown) this.down();
        }
    }
    return new Kaleidoscope(x, y, mirrors)
}