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