There are x amount of rectangles. Each iteration they are moved towards one or more attraction points until they collide with some other rectangle. The more iterations, the more likely they will "fit" better. There's also an option to allow variance in sizing to create some interesting outputs.
Note; if sizes are set too high and there are too many rectangles, it wont resolve pretty.
Log in to post a comment.
const seed = 1 // min=1, max=100, step=1 const totalAttractionPoints = 0 // min=0, max=5, step=1 const totalRectangles = 100 // min=5, max=1000, step=1 const totalIterations = 50 // min=0, max=300, step=1 const minSize = 2 // min=1, max=30, step=1 const maxSize = 20 // min=2, max=30, step=1 const canChangeWidth = 0 // min=0, max=1, step=1 (No,Yes) const canChangeHeight = 0 // min=0, max=1, step=1 (No,Yes) const useSpace = 1 // min=0, max=1, step=1 (No,Yes) const random = new Random(seed) const turtle = new Turtle() const grid = Math.ceil(Math.sqrt(totalRectangles)) const attractionPoints = [...Array(totalAttractionPoints).keys()].map(i => [-75 + random.next() * 150, -75 + random.next() * 150]) const rects = [...Array(totalRectangles).keys()].map(i => [ -100 + (i % grid) * (200 / grid) | 0, -100 + (i / grid | 0) * (200 / grid) | 0, (minSize + random.next() * (maxSize - minSize)) | 0, (minSize + random.next() * (maxSize - minSize)) | 0 ]) const debug = 0 // min=0, max=1, step=1 (No,Yes) if (debug) { if (!attractionPoints.length) (turtle.jump([0,-25]), turtle.circle(25)) attractionPoints.forEach(p => (turtle.jump([p[0], p[1] - 25]), turtle.circle(25))) } const _ = [...Array(totalIterations).keys()].forEach(iteration => { // shuffle order of doing stuff random.shuffle(rects); const centerAtStart = getCenter(rects) rects.forEach(currentRect => { const attractionPoint = attractionPoints.length ? attractionPoints[random.next() * attractionPoints.length | 0] : getCenter(rects) let tries = 0 const prev = [0,0,0,0] while (tries++ < 50) { copyRectFromTo(currentRect, prev) // move maybe horizontal or vertical if (random.next() > 0.5) { if (currentRect[0] < attractionPoint[0]) currentRect[0] += 1 else currentRect[0] -= 1 } if (random.next() > 0.5) { if (currentRect[1] < attractionPoint[1]) currentRect[1] += 1 else currentRect[1] -= 1 } // change size maybe horizontal or vertical if (canChangeWidth && random.next() > 0.75) { currentRect[2] = Math.min(maxSize, Math.max(minSize, currentRect[2] + (random.next() > 0.5 ? -1 : 1))) } if (canChangeHeight && random.next() > 0.75) { currentRect[3] = Math.min(maxSize, Math.max(minSize, currentRect[3] + (random.next() > 0.5 ? -1 : 1))) } // test if collide for (let rect of rects) { if (rect != currentRect) { if (rectToRect(currentRect, rect)) { // put back to point where it didnt collide with something copyRectFromTo(prev, currentRect) // stop iteration return } } } } }) }) function walk() { drawRect(rects.shift()) return rects.length } function rectToRect(r1, r2) { const [r1x,r1y,r1w,r1h] = r1 const [r2x,r2y,r2w,r2h] = r2 if (!(r1x > r2x + r2w || r1x + r1w < r2x || r1y > r2y + r2h || r1y + r1h < r2y)) { // r1 bottom edge past r2 top return true } return false } function copyRectFromTo(from, to) { to[0] = from[0] to[1] = from[1] to[2] = from[2] to[3] = from[3] } function getCenter(rects) { const center = [0,0] const total = rects.length for(let rect of rects) { center[0] += (rect[0] + rect[2] / 2) / total center[1] += (rect[1] + rect[3] / 2) / total } return center } function drawRect(rect) { let [x,y,w,h] = rect if (!useSpace) { x -= 1 y -= 1 w += 1 h += 1 } turtle.jump(x,y) turtle.goto(x+w,y) turtle.goto(x+w,y+h) turtle.goto(x,y+h) turtle.goto(x,y) } // Seeded random - Mulberry32 function Random(seed) { class Random { constructor(seed) { this.seed = seed } next() { var t = this.seed += 0x6D2B79F5 t = Math.imul(t ^ t >>> 15, t | 1) t ^= t + Math.imul(t ^ t >>> 7, t | 61) return ((t ^ t >>> 14) >>> 0) / 4294967296 } shuffle(arr) { for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(this.next() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } return arr } } return new Random(seed) }