Kaleidoscoped fractal terrain

fractal terrain

Log in to post a comment.

// Forked from "fractal terrain" by ge1doot
// https://turtletoy.net/turtle/20906ce329

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

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

// Global code will be evaluated once.
const turtle = new Kaleidoscope(0, 0, mirrors);
//const turtle = new Turtle();
turtle.penup();
/////////////////////////////////////
const size = 512; // try 256 or 128
const water = 4; // 0 for no water
let randomLevel = 60;
let seed = (Math.random() * 100000) | 0;
/////////////////////////////////////
console.log("seed: " + seed);
function random () {
	seed = (seed * 16807) % 2147483647;
	return (seed - 1) / 2147483646;
}
const hmap = [];
for (let i = 0; i <= size; i++) hmap[i] = new Float32Array(size + 1);
const nbits = size.toString(2).length - 1;
const rnd = () => randomLevel * (-1 + 2 * random());
let t = 1;
let x = size / 2;
for (let s = 1; s <= nbits; s++) {
    for (let v = 0; v <= size; v += 2 * x) {
        for (let n = 1; n <= t; n += 2) {
            hmap[n * x][v] = ((hmap[(n - 1) * x][v] + hmap[(n + 1) * x][v]) / 2) + rnd();
            hmap[v][n * x] = ((hmap[v][(n - 1) * x] + hmap[v][(n + 1) * x]) / 2) + rnd();
        }
    }
    for (let n = 1; n <= t; n += 2) {
        for (let m = 1; m <= t; m += 2) {
            hmap[n * x][m * x] = (0.25 * (
                hmap[n * x + x][m * x] + 
                hmap[n * x - x][m * x] + 
                hmap[n * x][m * x + x] + 
                hmap[n * x][m * x - x])
            ) + rnd();
        }
    }
    t = 2 * t + 1;
    x /= 2;
    randomLevel /= 2;
}
for (let w = 0; w <= size; w++) {
    for (let z = 0; z <= size; z++) {
        if (hmap[w][z] < 0) hmap[w][z] = 0;
    }
}
const line = new Float32Array(size + 1);
const r = 200 / size;
let pen = false;
for (let w = 0; w <= size; w++) {
    let k = 0;
    pen = false;
    for (let z = 0; z <= size; z++) {
        let xe = r * z;
        let ye = r * 0.66 * w + hmap[z][w] * 1;
        if (ye <= line[z] || (hmap[z][w] === 0 && w / water !== ((w / water)|0))) {
            if (pen === true) {
                turtle.up();
                pen = false;
            }
        } else {
            if (pen === false) {
                pen = true;
            }
            turtle.goto(-100 + xe, 100 - ye);
            turtle.down();
            line[z] = ye;
        }
    }
    turtle.up();
}


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)
}