Skyscrapers

Chains of non-overlapping squares arranged in different patterns

Log in to post a comment.

// 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();
let seed = 1000; // min=1 max=1000000 step=1
const bits = 20; 
const samples = 150000; 
const mod = 1<<bits;
const boarder_gap = 10; // min = -20 max = 20 step = 1
const square1_size_min = 0; // min = 0 max = 40 step = 1
const square2_size_min = 0; // min = 0 max = 40 step = 1
const square1_size_max = 2; // min = 0 max = 40 step = 1
const square2_size_max = 3; // min = 0 max = 40 step = 1
const chain_length_min = 0; // min = 0 max = 100 step = 1
const chain_length_max = 20; // min = 0 max = 100 step = 1
const chain_size_min = 20; // min = 0 max = 20 step = 1
const chain_size_max = 100; // min = 20 max = 100 step = 1
const angle = 0; // min = 0 max = 360 step = 1
const num_attempted_chains = 1500; // min = 0 max = 3000 step = 1
const rot = 0.55; // min = 0 max = 2 step = 0.01
const organization = 2; //min=0 max=2 step=1 (Random, Linear, Circular)
let sc_list = [];

class Square{
    constructor(sx, sy, sl) {
        this.sx = sx;
        this.sy = sy;
        this.sl = sl;
    }
    
    draw() {
        turtle.penup();
        turtle.goto(this.sx + this.sl/2, this.sy + this.sl/2);
        turtle.pendown();
        turtle.goto(this.sx - this.sl/2, this.sy + this.sl/2);
        turtle.goto(this.sx - this.sl/2, this.sy - this.sl/2);
        turtle.goto(this.sx + this.sl/2, this.sy - this.sl/2);
        turtle.goto(this.sx + this.sl/2, this.sy + this.sl/2);
    }
    
    intersects(other) {
        
        // if rectangle has area 0, no overlap
        if (this.sl == 0 || other.sl == 0)
            return false;
     
        // If one rectangle is on left side of other
        if (this.sx - this.sl / 2 > other.sx + other.sl / 2 || this.sx + this.sl / 2 < other.sx - other.sl / 2) {
            return false;
        }
 
        // If one rectangle is above other
        if (this.sy - this.sl / 2 > other.sy + other.sl / 2 || this.sy + this.sl / 2 < other.sy - other.sl / 2) {
            return false;
        }
 
        return true;
    }
}

class SquareChain {
    constructor(s1, s2, num_square) {
        this.s1 = s1;
        this.s2 = s2;
        this.num_square = num_square;
        this.list_square = [];
        for(let i = 0; i <= this.num_square; i++) {
            let sq = new Square(
            interp(this.s1.sx, this.s2.sx, i, this.num_square),
            interp(this.s1.sy, this.s2.sy, i, this.num_square),
            interp(this.s1.sl, this.s2.sl, i, this.num_square))
            this.list_square.push(sq);
        }
    }
    
    draw() {
        this.list_square.forEach((sq) => sq.draw());
    }
    
    intersects(other) {
        for(let i = 0; i < this.list_square.length; i++) {
            for(let j = 0; j < other.list_square.length; j++) {
                if(this.list_square[i].intersects(other.list_square[j])) {
                    return true;
                }
            }
        }
        return false;
    }
    
}

////////////////////////////////////////////////////////////////
// Pseudorandom number generator. Created by Reinder Nijhoff 2024
// https://turtletoy.net/turtle/a2274fd1fe
////////////////////////////////////////////////////////////////
function random() { // returns a number [0, 1[
    let r = 1103515245 * (((seed+=12345) >> 1) ^ seed);
    r = 1103515245 * (r ^ (r >> 3));
    r = r ^ (r >> 16);
    return (r % mod) / mod;
}

function interp(x1, x2, current_step, steps) {
    let ratio = (steps - current_step) / steps;
    return(x1 * ratio + x2 * (1 - ratio));
}

// The walk function will be called until it returns false.
function walk(i) {
    let sc;
    let x1 = random() * (200 - 2 * boarder_gap) - 100 + boarder_gap;
    let y1 = random() * (200 - 2 * boarder_gap) - 100 + boarder_gap;
    let size1 = random() * (square1_size_max - square1_size_min) + square1_size_min;
    let chain_length = random() * (chain_length_max - chain_length_min) + chain_length_min;
    let theta = 0;
    if(organization == 0) {
        theta = random() * 2 * Math.PI;
    }
    else if (organization == 1){
        theta = angle * 2 * Math.PI / 360;
    }
    else {
        theta = Math.atan(y1/x1);
        if(x1 > 0) {
            theta += Math.PI;
        }
        theta += rot;
    }
    let x2 = x1 + chain_length * Math.cos(theta);
    let y2 = y1 + chain_length * Math.sin(theta);
    // let x2 = random() * (200 - 2 * boarder_gap) - 100 + boarder_gap;
    // let y2 = random() * (200 - 2 * boarder_gap) - 100 + boarder_gap;
    let size2 = random() * (square2_size_max - square2_size_min) + square2_size_min;
    let steps = Math.floor(random() * (chain_size_max - chain_size_min) + chain_size_min);
    let s1 = new Square(x1, y1, size1);
    let s2 = new Square(x2, y2, size2);
    sc = new SquareChain(s1, s2, steps);

    
    let overlap = false;
    for(let j = 0; j < sc_list.length; j++) {
        console.log(sc_list[j]);
        overlap = overlap || sc.intersects(sc_list[j]);
    }
    if(!overlap) {
        console.log(sc);
        sc.draw();
        sc_list.push(sc);
    }
    return i < num_attempted_chains;
}