Architecture 003

Only one problem when you change from white to black lines it randomly redraws the grid.
"Vibe Code" using ChatGPT (it works but took 2 hours of prompting)
Inspired by: machine.arm sketch 149
instagram.com/p/dawupp4ybsn/

Log in to post a comment.

// Architecture 002 — Nested Rectangles + colored connecting lines
// 10 November 2025

const opacity = 1;
Canvas.setpenopacity(opacity);

const turtle = new Turtle();

// ===== PARAMETERS =====
const parentMinX = -95;
const parentMaxX = 95;
const parentMinY = -95;
const parentMaxY = 95;

const canvasW = parentMaxX - parentMinX;
const canvasH = parentMaxY - parentMinY;

const baseCols = 8;
const baseRows = 8;
const mergeChance = 0.45;

const parentCellMinWidth = 20;
const parentCellMaxWidth = 80;
const parentCellMinHeight = 15;
const parentCellMaxHeight = 60;

const minInset = 0.1;
const maxInset = 0.3;

const innerMinWidth = 5;
const innerMaxWidth = 60;
const innerMinHeight = 5;
const innerMaxHeight = 60;

const innerPaddingX = 10;
const innerPaddingY = 10;

const connectorLines = 15; // min=1 max=50 step=1

// ===== DRAW MODE =====
const drawmode = 2; // min=0, max=2, step=1, (Black Lines, White Lines, Both)

// ===== GRID SETUP =====
function randomProportions(n) {
    let vals = Array.from({ length: n }, () => 0.5 + Math.random());
    const sum = vals.reduce((a, b) => a + b, 0);
    return vals.map(v => v / sum);
}

const colWeights = randomProportions(baseCols);
const rowWeights = randomProportions(baseRows);
const colWidths = colWeights.map(w => w * canvasW);
const rowHeights = rowWeights.map(h => h * canvasH);

let colX = [parentMinX];
for (let i = 0; i < baseCols; i++) colX.push(colX[i] + colWidths[i]);
let rowY = [parentMinY];
for (let j = 0; j < baseRows; j++) rowY.push(rowY[j] + rowHeights[j]);

let filled = Array.from({ length: baseRows }, () => Array(baseCols).fill(false));

// ===== DRAW CELLS =====
for (let r = 0; r < baseRows; r++) {
    for (let c = 0; c < baseCols; c++) {
        if (filled[r][c]) continue;

        // Merge cells
        let mergeW = 1;
        let mergeH = 1;
        if (Math.random() < mergeChance && c < baseCols - 1 && !filled[r][c + 1]) mergeW = 2;
        if (Math.random() < mergeChance && r < baseRows - 1 && !filled[r + 1][c]) mergeH = 2;
        if (c + mergeW > baseCols) mergeW = 1;
        if (r + mergeH > baseRows) mergeH = 1;

        // Constrain size
        const tempX2 = colX[c + mergeW];
        const tempY2 = rowY[r + mergeH];
        const tempWidth = tempX2 - colX[c];
        const tempHeight = tempY2 - rowY[r];
        if (tempWidth < parentCellMinWidth || tempWidth > parentCellMaxWidth) mergeW = 1;
        if (tempHeight < parentCellMinHeight || tempHeight > parentCellMaxHeight) mergeH = 1;

        // Mark cells filled
        for (let rr = r; rr < r + mergeH; rr++) {
            for (let cc = c; cc < c + mergeW; cc++) {
                filled[rr][cc] = true;
            }
        }

        // Parent rectangle
        const x1 = colX[c];
        const x2 = colX[c + mergeW];
        const y1 = rowY[r];
        const y2 = rowY[r + mergeH];
        rectangle(x1, y1, x2, y2);

        // ===== CHILD RECTANGLE =====
        const w = x2 - x1;
        const h = y2 - y1;

        const insetFrac = minInset + Math.random() * (maxInset - minInset);
        const maxInnerWByInset = w * (1 - insetFrac);
        const maxInnerHByInset = h * (1 - insetFrac);

        const maxInnerW = Math.min(innerMaxWidth, maxInnerWByInset, w - 2 * innerPaddingX);
        const maxInnerH = Math.min(innerMaxHeight, maxInnerHByInset, h - 2 * innerPaddingY);

        const randInnerW = innerMinWidth + Math.random() * Math.max(0, maxInnerW - innerMinWidth);
        const randInnerH = innerMinHeight + Math.random() * Math.max(0, maxInnerH - innerMinHeight);

        const ix1 = x1 + innerPaddingX + Math.random() * (w - randInnerW - 2 * innerPaddingX);
        const iy1 = y1 + innerPaddingY + Math.random() * (h - randInnerH - 2 * innerPaddingY);
        const ix2 = ix1 + randInnerW;
        const iy2 = iy1 + randInnerH;

        rectangle(ix1, iy1, ix2, iy2);

        // ===== CONNECT CHILD TO PARENT =====
        connectChildToParent(x1, y1, x2, y2, ix1, iy1, ix2, iy2, connectorLines, drawmode);
    }
}

// ===== FUNCTIONS =====
function rectangle(xA, yA, xB, yB) {
    turtle.penup();
    turtle.goto(xA, yA);
    turtle.pendown();
    turtle.goto(xB, yA);
    turtle.goto(xB, yB);
    turtle.goto(xA, yB);
    turtle.goto(xA, yA);
}

// Connect child rectangle to parent edges with colored lines
function connectChildToParent(px1, py1, px2, py2, cx1, cy1, cx2, cy2, lines, drawmode) {
    // TOP EDGE → White
    if (drawmode === 1 || drawmode === 2) {
        for (let i = 0; i <= lines; i++) {
            const t = i / lines;
            const childX = cx1 + t * (cx2 - cx1);
            turtle.penup();
            turtle.goto(childX, cy1);
            turtle.pendown();
            const parentX = px1 + t * (px2 - px1);
            turtle.goto(parentX, py1);
        }
    }

    // RIGHT EDGE → White
    if (drawmode === 1 || drawmode === 2) {
        for (let i = 0; i <= lines; i++) {
            const t = i / lines;
            const childY = cy1 + t * (cy2 - cy1);
            turtle.penup();
            turtle.goto(cx2, childY);
            turtle.pendown();
            const parentY = py1 + t * (py2 - py1);
            turtle.goto(px2, parentY);
        }
    }

    // BOTTOM EDGE → Black
    if (drawmode === 0 || drawmode === 2) {
        for (let i = 0; i <= lines; i++) {
            const t = i / lines;
            const childX = cx1 + t * (cx2 - cx1);
            turtle.penup();
            turtle.goto(childX, cy2);
            turtle.pendown();
            const parentX = px1 + t * (px2 - px1);
            turtle.goto(parentX, py2);
        }
    }

    // LEFT EDGE → Black
    if (drawmode === 0 || drawmode === 2) {
        for (let i = 0; i <= lines; i++) {
            const t = i / lines;
            const childY = cy1 + t * (cy2 - cy1);
            turtle.penup();
            turtle.goto(cx1, childY);
            turtle.pendown();
            const parentY = py1 + t * (py2 - py1);
            turtle.goto(px1, parentY);
        }
    }
}