Rectangles expand, one side at a time, until they hit each other.
Log in to post a comment.
let NUM_RECTS = 1000; // min=0, max=1000, step=1 let CENTER_RAD = 1; let SHRINK = 1; // min=-10, max=10, step=1 let HIT_SIDES_CHANCE = 0.2; // min=0, max=1, step=0.1 let OUTER_PADDING = 10; // min=0, max=50, step=1 let SEED_PADDING = 30; // min=0, max=50, step=1 let MIN_RAD = 2; // min=0, max=20, step=1 let MAX_RAD = 10; // min=0, max=20, step=1 let VERTICAL_CHANCE = 0.66; // min=0, max=1, step=0.1 // leave this 1 for square let THIN_STRETCH_FACTOR = 2; // min=1, max=10, step=1 // booleans let DO_EXPAND = 1; // min=0, max=1, step=1 let DRAW_CENTERS = 0; // min=0, max=1, step=1 let DRAW_FRAME = 1; // min=0, max=1, step=1 let DRAW_X = 0; // min=0, max=1, step=1 let HATCH_CHANCE = 0.8; // min=0, max=1, step=0.1 let HATCH_SPACING = 1.5; // min=1, max=5, step=0.5 let SPARSE_HATCH_CHANCE = 0.15; // min=0, max=1, step=0.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 Turtle(); turtle.pendown(); let rand = (min, max) => Math.random() * (max - min) + min; let randInt = (min, max) => Math.floor(rand(min, max)); let rangeOverlaps = (a0, a1, b0, b1) => (a0 < b1 && a1 > b0) || (b0 < a1 && b1 > a0); let Line = (x0, y0, x1, y1) => { let line = { x0: x0, x1: x1, y0: y0, y1: y1, }; line.draw = () => { turtle.jump(line.x0, line.y0); turtle.goto(line.x1, line.y1); }; return line; }; let Rect = (x0, x1, y0, y1) => { let r = { xc: (x0 + x1) / 2, yc: (y0 + y1) / 2, x0: x0, x1: x1, y0: y0, y1: y1, }; r.draw = () => { if (DRAW_FRAME) { turtle.jump(r.x0, r.y0); turtle.goto(r.x1, r.y0); turtle.goto(r.x1, r.y1); turtle.goto(r.x0, r.y1); turtle.goto(r.x0, r.y0); } if (DRAW_X) { turtle.jump(r.x0, r.y0); turtle.goto(r.x1, r.y1); turtle.jump(r.x1, r.y0); turtle.goto(r.x0, r.y1); } if (DRAW_CENTERS) { turtle.jump(r.xc, r.yc - CENTER_RAD); turtle.circle(CENTER_RAD); } if (Math.random() < HATCH_CHANCE) { let h = r.y1 - r.y0; let spacing = HATCH_SPACING; if (Math.random() < SPARSE_HATCH_CHANCE) { spacing *= 2; } for (let xx = r.x0; xx <= r.x1 + h; xx += spacing) { let line = Line(xx, r.y0, xx - h, r.y1); if (line.x1 < r.x0) { line.x1 = r.x0; line.y1 = xx - r.x0 + r.y0; } if (line.x0 > r.x1) { line.x0 = r.x1; line.y0 = xx - r.x1 + r.y0; } line.draw(); } } }; r.intersects = (other) => ( rangeOverlaps(r.x0, r.x1, other.x0, other.x1) && rangeOverlaps(r.y0, r.y1, other.y0, other.y1) ); r.expand = (amt) => { r.x0 -= amt; r.y0 -= amt; r.x1 += amt; r.y1 += amt; }; r.isValid = () => r.x0 < r.x1 && r.y0 < r.y1; return r; }; let rects = []; // place initial rects and delete overlapping ones for (let ii = 0; ii < NUM_RECTS; ii++) { let limit = 100 - SEED_PADDING; let x = randInt(-limit, limit); let y = randInt(-limit, limit); let xr = randInt(MIN_RAD, MAX_RAD); let yr = randInt(MIN_RAD, MAX_RAD); if (Math.random() < VERTICAL_CHANCE) { xr *= THIN_STRETCH_FACTOR; } else { yr *= THIN_STRETCH_FACTOR; } let rect = Rect(x-xr, x+xr, y-yr, y+yr); let intersects = false; for (let jj = 0; jj < rects.length; jj++) { if (rects[jj].intersects(rect)) { intersects = true; break; } } if (!intersects) { rects.push(rect); } } console.log('' + rects.length + ' rects'); // expand rects to hit neighbors if (DO_EXPAND) { for (let ii = 0; ii < rects.length; ii++) { let rect = rects[ii]; let newVal = 0; let border = 100 - OUTER_PADDING; let hitBorder = Math.random() < HIT_SIDES_CHANCE; // expand to the right newVal = border; for (let other of rects) { if (rect === other) { continue; } if (rect.x1 <= other.x0 && rangeOverlaps(rect.y0, rect.y1, other.y0, other.y1)) { newVal = Math.min(newVal, other.x0); } } rect.x1 = Math.min(rect.x1, border); if (hitBorder || newVal !== border) { rect.x1 = newVal; } // expand to the left newVal = -border; for (let other of rects) { if (rect === other) { continue; } if (rect.x0 >= other.x1 && rangeOverlaps(rect.y0, rect.y1, other.y0, other.y1)) { newVal = Math.max(newVal, other.x1); } } rect.x0 = Math.max(rect.x0, -border); if (hitBorder || newVal !== -border) { rect.x0 = newVal; } // expand down newVal = border; for (let other of rects) { if (rect === other) { continue; } if (rect.y1 <= other.y0 && rangeOverlaps(rect.x0, rect.x1, other.x0, other.x1)) { newVal = Math.min(newVal, other.y0); } } rect.y1 = Math.min(rect.y1, border); if (hitBorder || newVal !== border) { rect.y1 = newVal; } // expand up newVal = -border; for (let other of rects) { if (rect === other) { continue; } if (rect.y0 >= other.y1 && rangeOverlaps(rect.x0, rect.x1, other.x0, other.x1)) { newVal = Math.max(newVal, other.y1); } } rect.y0 = Math.max(rect.y0, -border); if (hitBorder || newVal !== -border) { rect.y0 = newVal; } } } for (let r of rects) { r.expand(-SHRINK); } rects = rects.filter(r => r.isValid()); // The walk function will be called until it returns false. function walk(ii) { rects[ii].draw(); return ii < rects.length-1; }