Subdivision Grid ➖➗

Draw randomized grid, split cells into multiple rectangles and recurse.

#kdtree

Log in to post a comment.

const turtle = new Turtle();

const seed = 50; /// min=1, max=100, step=1
const baseShape = 0; // min=0, max=1, step=1 (Square, Hexagon)
const grid = 7; // min=3, max=21, step=2
const maxDepth = 4; // min=1, max=10, step=1
const directionMode = 0; // min=0, max=1, step=1 (Random, Modulo)
const splitMode = 0; // min=0, max=1, step=1 (Scattered, Ordered)
const splitChance = 0.84; // min=0, max=1, step=0.01
const splitMin = 2; // min=1, max=8, step=1
const splitMax = 3; // min=1, max=8, step=1
const randomDistortion1 = .4; // min=0, max=1, step=0.01
const randomDistortion2 = .03; // min=0, max=0.25, step=0.001
const shapeScaleMode = 0//min=0, max=2, step=1 (Both, On Outer Shape, On Splitted Shapes)
const shapeScale = 0.98; // min=0.75, max=1.25, step=0.001
const scale = 220 / grid;

let mod = 0;

function walk(i) {
    const x = (i % grid) - (grid/2|0);
    const y = (i/grid|0) - (grid/2|0);
    if (baseShape == 0) {
        const square = randomizePoints(createSquare(x, y), randomDistortion1);
        recurse(square, splitChance);
    } else if (baseShape == 1) {
        const hexagon = randomizePoints(createHexagon(x, y), randomDistortion1);
        const square1 = [hexagon[0],hexagon[1],hexagon[2],hexagon[3]];
        const square2 = [hexagon[3],hexagon[4],hexagon[5],hexagon[0]];
        recurse( square1, splitChance);
        recurse( square2, splitChance);
    }
    
    return i < grid * grid - 1;
}

function recurse(points, chance = 0.5, depth = 0) {
    const doRecurse = depth < maxDepth && chance > 0;
    const totalSplits = Math.round(lrp(splitMin,splitMax, rand()));
    const direction = directionMode == 0 ? rand() > 0.5 : mod++ % 3 == 0;
    const splittedShapeScale = (shapeScaleMode == 0 || shapeScaleMode == 2) ? shapeScale : 1.0;
    const shape = split(points, totalSplits, direction, splittedShapeScale);
    
    if (splitMode == 0) {
        if (doRecurse && rand() < chance) {
            shape.forEach(points => recurse(points, chance, depth + 1));
        } else {
            if (shapeScaleMode <= 1) {
                shape.forEach((points,i,l) => l[i] = randomizePoints(points, randomDistortion2));
                shape.forEach((points,i,l) => l[i] = scl4(points, shapeScale));
            }
            shape.forEach(points => drawPoints(points.map(point => scl2(point, scale)), turtle));
        }
    } else {
        shape.forEach(points => {
             if (doRecurse && rand() < chance) {
                recurse(points, chance, depth + 1);
            } else {
                if (shapeScaleMode <= 1) {
                    shape.forEach((points,i,l) => l[i] = randomizePoints(points, randomDistortion2));
                    shape.forEach((points,i,l) => l[i] = scl4(points, shapeScale));
                }
                drawPoints(points.map(point => scl2(point, scale)), turtle);
            }
        });
    }
}

function createSquare(x,y) {
    return [[x-.5,y-.5], [x+.5,y-.5], [x+.5,y+.5], [x-.5,y+.5]];
}

function createHexagon(x,y) {
    const h = .75, w = 3/8 * (h/(Math.sqrt(3)/4)), center = [x*2*w + (y%2)*w, y*3/2*h];
    return [[0,-h],[-w,-h/2],[-w,h/2],[0,h],[w,h/2],[w,-h/2]].map(p => add2(center, p));
}

function randomizePoints(points, scale = 1) {
    return points.map( point => add2(point, scl2( add2( [hash(dot2(point, [11, 13])), hash(dot2(point, [17, 19]))], [-.5,-.5]), scale)));
}

function drawPoints(points, turtle) {
    if (points.length > 0) turtle.jump(points[points.length-1]);
    points.forEach(point => turtle.goto(point));
}

function split(points, total = 5, vertical = true, scale = 1.0) {
    const [tl,tr,br,bl] = points;
    const shapes = [];
    for(let i=0;i<total;i++) {
        const t1 = (i + (1 - scale)) / total;
        const t2 = (i + scale) / total;
        
        if (vertical) {
            shapes.push([
                [lrp(tl[0], tr[0], t1), lrp(tl[1], tr[1], t1) ],
                [lrp(tl[0], tr[0], t2), lrp(tl[1], tr[1], t2) ],
                [lrp(bl[0], br[0], t2), lrp(bl[1], br[1], t2) ],
                [lrp(bl[0], br[0], t1), lrp(bl[1], br[1], t1) ],
            ]);
        } else {
            shapes.push([
                [lrp(tl[0], bl[0], t1), lrp(tl[1], bl[1], t1) ],
                [lrp(tl[0], bl[0], t2), lrp(tl[1], bl[1], t2) ],
                [lrp(tr[0], br[0], t2), lrp(tr[1], br[1], t2) ],
                [lrp(tr[0], br[0], t1), lrp(tr[1], br[1], t1) ],
            ]);
        }
    }
    return shapes;
}

// vec2 functions
function scl2(a,b)   { return [a[0]*b, a[1]*b]; }
function add2(a,b)   { return [a[0]+b[0], a[1]+b[1]]; }
function sub2(a,b)   { return [a[0]-b[0], a[1]-b[1]]; }
function dot2(a,b)   { return a[0]*b[0] + a[1]*b[1]; }
function len2(a)     { return Math.sqrt(a[0]**2 + a[1]**2); }
function nrm2(a)     { return scl(a, 1/len(a)); }
function lrp2(a,b,f) { return [a[0]*f+b[0]*(1-f), a[1]*f+b[1]*(1-f)]; }
function lrp(a,b,f) { return a*f + b*(1-f); }
function scl4(shape, scale=0.9) {
    const t1 = 1-scale;
    const t2 = scale;
    const [tl,tr,br,bl] = shape;
    return [
        [lrp(tl[0], br[0], t1), lrp(tl[1], br[1], t1) ],
        [lrp(tr[0], bl[0], t1), lrp(tr[1], bl[1], t1) ],
        [lrp(br[0], tl[0], t1), lrp(br[1], tl[1], t1) ],
        [lrp(bl[0], tr[0], t1), lrp(bl[1], tr[1], t1) ],
    ];
}
function add4(shape, val = 0) {
    const [tl,tr,br,bl] = shape;
    return [
        add2(tl,[val,val]),
        add2(tr,[-val,val]),
        add2(br,[-val,-val]),
        add2(bl,[val,-val])
    ];
}

// pseudo random methods
function hash(p) {
    p += seed;
    p = 1103515245 * (((p) >> 1) ^ (p));
    p = 1103515245 * (p ^ (p>>3));
    p = p ^ (p >> 16);
    return p / 1103515245 % 1;	
}

let rseed = seed;
function rand() {
    let r = 1103515245 * (((rseed) >> 1) ^ (rseed++));
    r = 1103515245 * (r ^ (r>>3));
    r = r ^ (r >> 16);
    return r / 1103515245 % 1;	
}