Log in to post a comment.

const plottable = 0; //min=0 max=1 step=1 (No, Yes)
const rMin = 1.5; //min=1 max=5 step=.5
const rMax = 8; //min=5 max=10 step=.5
const bigR = 80; //min=40 max=90 step=5
const margin = 1;
const grayScaleSteps = 20; //min=2 max=40 step=1
const rTailIncrease = 1.03; //min=.8 max=1.2 step=.01
const distDev100 = .5; //min=0 max=2 step=.1

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(plottable == 1? -1: -1/grayScaleSteps);

// Global code will be evaluated once.
let baleSize = grayScaleSteps;
const bales = Array.apply(null,{length: baleSize}).map(b => new Bale(baleSize--));

let cp = new CirclePacker(bigR);

// The walk function will be called until it returns false.
function walk(i) {
    let c = cp.get(100, (rMin + ((rMax - rMin) * Math.random())) / bigR, margin / bigR);
    if(c != null) {
        for(let j = 0; j < grayScaleSteps; j += (plottable == 0? 1: grayScaleSteps /5)) {
            let r = c[4] * (plottable == 1? 1: Math.pow(rTailIncrease, j));
            bales[j].jump(Math.cos(c[0]) * c[1] * Math.pow(1 + (distDev100/100), j), Math.sin(c[0]) * c[1]  * Math.pow(1 + (distDev100/100), j) - r);
            bales[j].circle(r);
        }
    }
    return i < 500;
}

function CirclePacker(radius) {
    class CirclePacker {
        pts = [] //a, r, x, y, radius
        constructor(radius) {
            this.r = radius;
        }
        get(candidates = 10, radiusRatio = .5, marginRatio = .02) {
            let cs = Array.apply(null,{length: candidates}).map(b => [
                Math.PI * 2 * Math.random(),
                Math.sqrt(Math.random()) * this.r
            ]);
            cs = cs.map(i => [...i, Math.cos(i[0]) * i[1], Math.sin(i[0]) * i[1], 0] );
                                //i: cA, cR, x, y, r
            for(let csi = 0; csi < cs.length; csi++) {
                //radius = minimum(max allowed radius, max possible radius to fit in this)
                cs[csi][4] = Math.min(this.r * radiusRatio, this.r - (cs[csi][2]**2+cs[csi][3]**2)**.5);
                for(let ptsi = 0; ptsi < this.pts.length; ptsi++) {
                    cs[csi][4] = Math.min(cs[csi][4], (((this.pts[ptsi][2] - cs[csi][2])**2 + (this.pts[ptsi][3] - cs[csi][3])**2)**.5 - this.pts[ptsi][4]));
                }
                cs[csi][4] -= this.r * marginRatio;
            }
            
            cs.sort((a,b) => a[4] < b[4]? 1: -1);
            if(cs[0][4] < this.r * marginRatio) {
                return null;
            }
            this.pts.push(cs[0]);

            return cs[0];
        }
    }
    return new CirclePacker(radius);
}

////////////////////////////////////////////////////////////////
// Bale utility code - Created by Jurgen Westerhof 2022
// https://turtletoy.net/turtle/7269af8a23
// Abusing the opacity, usage:
//      Canvas.setpenopacity(1/baleSize);
//      const bales = Array.apply(null,{length: baleSize}).map(b => new Bale(baleSize--);
// Then use bales[x] wherever you would use a turtle object to 'draw'
// in 'color' x (i.e Polygon hatching with a bale object and .15 interspacing)
////////////////////////////////////////////////////////////////
function Bale(n) {
    class Bale {
        constructor(n) { this.turtles = Array.apply(null,{length: n}).map(i => new Turtle()); }
        
        back(e)         { this.turtles.map(t => t.back(e)); return this; }
        backward(e)     { this.turtles.map(t => t.backward(e)); return this; }
        bk(e)           { this.turtles.map(t => t.bk(e)); return this; }
        fd(e)           { this.turtles.map(t => t.fd(e)); return this; }
        forward(e)      { this.turtles.map(t => t.forward(e)); return this; }

        left(e)         { this.turtles.map(t => t.left(e)); return this; }
        lt(e)           { this.turtles.map(t => t.lt(e)); return this; }
        right(e)        { this.turtles.map(t => t.right(e)); return this; }
        rt(e)           { this.turtles.map(t => t.rt(e)); return this; }

        seth(e)         { this.turtles.map(t => t.seth(e)); return this; }
        setheading(e)   { this.turtles.map(t => t.setheading(e)); return this; }

        setx(e)         { this.turtles.map(t => t.setx(e)); return this; }
        sety(e)         { this.turtles.map(t => t.sety(e)); return this; }

        setpos(x, y)        { this.turtles.map(t => t.setpos(x, y)); return this; }
        setposition(x, y)   { this.turtles.map(t => t.setposition(x, y)); return this; }

        toradians(e)    { this.turtles.map(t => t.toradians(e)); return this; }
        degrees(e)      { this.turtles.map(t => t.degrees(e)); return this; }

        goto(x, y)      { this.turtles.map(t => t.goto(x, y)); return this; }
        jmp(x, y)       { this.turtles.map(t => t.jmp(x, y)); return this; }
        jump(x, y)      { this.turtles.map(t => t.jump(x, y)); return this; }

        circle(radius, extent, steps) { this.turtles.map(t => t.circle(radius, extent, steps)); return this; }

        clone()         { let b = new Bale(this.turtle.length); this.turtles.map((t, k) => b.turtles[k] = t.clone()); return b; }

        h()             { return this.turtles[0].h(); }
        heading()       { return this.turtles[0].heading(); }
        
        home()          { this.turtles.map(t => t.home()); return this; }
        
        isdown()        { return this.turtles[0].isdown(); }

        pos()           { return this.turtles[0].pos(); }
        position()      { return this.turtles[0].position(); }

        pd()            { this.turtles.map(t => t.pd()); return this; }
        pendown()       { this.turtles.map(t => t.pendown()); return this; }
        penup()         { this.turtles.map(t => t.penup()); return this; }
        pu()            { this.turtles.map(t => t.pu()); return this; }
        down()          { this.turtles.map(t => t.down()); return this; }
        up()            { this.turtles.map(t => t.up()); return this; }

        radians()       { this.turtles.map(t => t.radians()); return this; }

        x()             { return this.turtles[0].x(); }
        xcor()          { return this.turtles[0].xcor(); }
        y()             { return this.turtles[0].y(); }
        ycor()          { return this.turtles[0].ycor(); }
    }
    return new Bale(n);
}