Works as the fork KD Tree 🌲, but after gathering all the rectangles an optimization pass is done which checks if there is a same size rect is next or below and merges them into one.
Log in to post a comment.
// Forked from "KD Tree 🌲" by markknol // https://turtletoy.net/turtle/182d91df0d const turtle = new Turtle(); const seed = 1.0; // min=1, max=1000, step=1 const balance1 = 1.0; /// min=0.0, max=1.0, step=0.01 const balance2 = 0.75; // min=0, max=1, step=0.01 const balance3 = 0.0; // min=0, max=1, step=0.01 const sizeX = 185; // min=50, max=200, step=5 const sizeY = 185; // min=50, max=200, step=5 const minSize = 1.5; // min=1, max=50, step=0.5 const spacing = 1.5; // min=0, max=10, step=0.5 const optimizations = 3; // min=0, max=6, step=1 const shuffleBeforeOptimize = 0 // min=0, max=1, step=1 (no,yes) const startPos = [-sizeX/2, -sizeY/2]; const areas = [ [0, 0, sizeX, sizeY], ]; let rects = []; function walk(i) { if (i === 0) { for (const area of areas.slice()) { subdivide(area); } } let area = areas.shift(); const a = [area[0], area[1]]; const b = [(area[0] + area[2]), (area[1] + area[3])]; let s = spacing*.5; add2(a, startPos); add2(b, startPos); add2(a, [s,s]); add2(b, [-s,-s]); s = spacing * 2/3; rects.push([a[0],a[1], b[0]-a[0], b[1]-a[1]]); if (!areas.length) { for(let ii=0;ii<optimizations;ii++) { rects = optimizeRects(rects); } rects.forEach(rect=>drawRect(rect)); } return areas.length; } let rseed = seed; function rand() { let r = 1103515245 * (((rseed) >> 1) ^ (rseed++)); r = 1103515245 * (r ^ (r>>3)); r = r ^ (r >> 16); return r / 1103515245 % 1; } function optimizeRects(rects) { if (shuffleBeforeOptimize) shuffle(rects); for (let rect1 of rects) { for (let rect2 of rects) { if (rect1 !== rect2) { if (rand() > 0.25 && rect1[1]==rect2[1] && rect1[3]==rect2[3] && rect1[0]+rect1[2]+spacing == rect2[0] ) { // next with same height rects.remove(rect2); rect1[2] += spacing + rect2[2]; continue; } else if (rand() > 0.25 && rect1[0]==rect2[0] && rect1[2]==rect2[2] && rect1[1]+rect1[3]+spacing == rect2[1] ) { // below with same width rects.remove(rect2); rect1[3] += spacing + rect2[3]; } else if (rand() > 0.25 && rect1[0]==rect2[0] && rect1[2]==rect2[2] && rect1[1]-spacing == rect2[1]+rect2[3] ) { // up with same width rects.remove(rect2); rect1[3] += spacing + rect2[3]; rect1[1] -= spacing + rect2[3]; continue; } else if (rand() > 0.25 && rect1[1]==rect2[1] && rect1[3]==rect2[3] && rect1[0]-spacing == rect2[0]+rect2[2] ) { // up with same width rects.remove(rect2); rect1[2] += spacing + rect2[2]; rect1[0] -= spacing + rect2[2]; continue; } } } } return rects; } function drawRect(rect) { let [x,y,w,h] = rect; turtle.jump(x,y) turtle.goto(x+w,y) turtle.goto(x+w,y+h) turtle.goto(x,y+h) turtle.goto(x,y) } Array.prototype.remove = function(item) { var idx; while ((idx = this.indexOf(item)) !== -1) { this.splice(idx, 1); } return this; } function subdivide(area) { const b1 = 1 - balance1; let r = 0.5 - (b1 / 2) + rand() * b1 / 2; let verticalSplit = rand() > 0.5; if (rand() < balance2) verticalSplit = area[2] < area[3] const area1 = verticalSplit ? [area[0], area[1], area[2], area[3]*r] : [area[0], area[1], area[2]*r, area[3]]; const area2 = !verticalSplit ? [area[0] + area[2]*r, area[1], area[2]*(1-r), area[3]] : [area[0], area[1] + area[3]*r, area[2], area[3]*(1-r)]; areas.remove(area); areas.push(area1); areas.push(area2); const minWidth = Math.max(minSize + spacing, spacing); const minHeight = Math.max(minSize + spacing, spacing) if ((area1[2] > minWidth) && (area1[3] > minHeight)) { if (rand() > Math.pow(balance3, 3)) subdivide(area1); } if ((area2[2] > minWidth) && (area2[3] > minHeight)) { if (rand() > Math.pow(balance3, 3)) subdivide(area2); } } function add2(p, v) { p[0] += v[0]; p[1] += v[1]; return p } function shuffle(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(rand() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } }