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