Squaring the circle

IFS with random weight transitions

Log in to post a comment.

// Forked from "Pythagorean Tree" by matigekunstintelligentie
// https://turtletoy.net/turtle/67654b5bd9

const shape = 4; // min=0, max=15, step=1 (Square, Bubble, Blur, Linear, Circle, Sin, Exponential, Swirl, Horseshoe, Polar, Handkerchief, Heart, Disc, Spiral, Diamond, Julia)
const x_post = 0.5; // min=-100, max=100, step=1
const y_post = 0.5; // min=-100, max=100, step=1
const seed = 420; // min=-1000, max=1000, step=1

// Math.random does not have a seed in JS?
function random() {
    seed = (1664525 * seed + 1013904223) % 4294967296;
    return (seed >>> 0) / 4294967296;
}

Canvas.setpenopacity(-0.05);

const turtle = new Turtle();

let x = 0;
let y = 0;

let function_set = [[0.5, 0, 0, 0.5, 0, 0],
                    [0.5, 0, 0, 0.5, 0.5, 0],
                    [0.5, 0, 0, 0.5, -0.5, 0],
                    [0.5, 0, 0, 0.5, 0., 0.5],
                    [0.5, 0, 0, 0.5, 0., -0.5]];
                    
let transition_matrix = Array.from({ length: 5 }, () => {
    let row = Array.from({ length: 5 }, () => random());
    let sum = row.reduce((acc, val) => acc + val, 0);
    return row.map(val => val / sum);
});

let shapes = ["Square", "Bubble", "Blur", "Linear", "Circle", "Sin", "Exponential", "Swirl", "Horseshoe", "Polar", "Handkerchief", "Heart", "Disc", "Spiral", "Diamond", "Julia"];
let function_transforms = [shapes[shape], "Linear", "Linear", "Linear", "Linear"];

function linear(x,y){
    return [x, y];
}

function square(x, y){
    var r1 = Math.random();
    var r2 = Math.random();
    return [(r1 - 0.5), (r2 - 0.5)];
}

function bubble(x, y){
    var r = 1.0;
    var multiplier = 4.0/(Math.pow(r,2.0) + 4.0);
    return [multiplier*x, multiplier*y];
}

function blur(x, y){
    var r1 = Math.random();
    var r2 = Math.random();
    return [r1*Math.cos(2*Math.PI*r2), r1*Math.sin(2*Math.PI*r2)];
}

function circle(x, y){
    var t = 2*Math.PI*Math.random();
    var u = Math.random() + Math.random();
    var r = u;
    if(u>1.0){
        r = 2-u;
    }
    return [r*Math.cos(t), r*Math.sin(t)];
}

function sin(x, y) {
    return [Math.sin(x), Math.sin(y)];
}

function exponential(x, y) {
    let exp = Math.exp(x - 1);
    return [exp * Math.cos(Math.PI * y), exp * Math.sin(Math.PI * y)];
}

function swirl(x, y) {
    let r2 = x * x + y * y;
    return [x * Math.sin(r2) - y * Math.cos(r2), x * Math.cos(r2) + y * Math.sin(r2)];
}

function horseshoe(x, y) {
    let r = Math.sqrt(x * x + y * y);
    return [(x - y) * (x + y) / r, 2 * x * y / r];
}

function polar(x, y) {
    let r = Math.sqrt(x * x + y * y);
    let theta = Math.atan2(y, x);
    return [theta / Math.PI, r - 1];
}

function handkerchief(x, y) {
    let r = Math.sqrt(x * x + y * y);
    let theta = Math.atan2(y, x);
    return [r * Math.sin(theta + r), r * Math.cos(theta - r)];
}

function heart(x, y) {
    let r = Math.sqrt(x * x + y * y);
    let theta = Math.atan2(y, x);
    return [r * Math.sin(theta * r), -r * Math.cos(theta * r)];
}

function disc(x, y) {
    let r = Math.sqrt(x * x + y * y);
    let theta = Math.atan2(y, x);
    return [theta / Math.PI * Math.sin(Math.PI * r), theta / Math.PI * Math.cos(Math.PI * r)];
}

function spiral(x, y) {
    let r = Math.sqrt(x * x + y * y);
    let theta = Math.atan2(y, x);
    return [(1 / r) * (Math.cos(theta) + Math.sin(r)), (1 / r) * (Math.sin(theta) - Math.cos(r))];
}

function diamond(x, y) {
    let r = Math.sqrt(x * x + y * y);
    let theta = Math.atan2(y, x);
    return [Math.sin(theta) * Math.cos(r), Math.cos(theta) * Math.sin(r)];
}

function julia(x, y) {
    let r = Math.sqrt(x * x + y * y);
    let theta = Math.atan2(y, x);
    let omega = Math.random() < 0.5 ? theta / 2 : theta / 2 + Math.PI;
    return [Math.sqrt(r) * Math.cos(omega), Math.sqrt(r) * Math.sin(omega)];
}

let prev = 0;

function walk(i) {
    turtle.jump(x * 75 + x_post, - y * 75 + y_post);
    turtle.circle(.1);

    let r = Math.random();
    let p_total = 0;

    for (let j = 0; j < function_set.length; j++) {
        let f = function_set[j];
        p_total += transition_matrix[prev][j];

        if (r < p_total) {
            // Use precomputed function lookup
            let next;
            switch (function_transforms[j]) {
                case "Square": next = square(x, y); break;
                case "Bubble": next = bubble(x, y); break;
                case "Blur": next = blur(x, y); break;
                case "Circle": next = circle(x, y); break;
                case "Sin": next = sin(x, y); break;
                case "Exponential": next = exponential(x, y); break;
                case "Swirl": next = swirl(x, y); break;
                case "Horseshoe": next = horseshoe(x, y); break;
                case "Polar": next = polar(x, y); break;
                case "Handkerchief": next = handkerchief(x, y); break;
                case "Heart": next = heart(x, y); break;
                case "Disc": next = disc(x, y); break;
                case "Spiral": next = spiral(x, y); break;
                case "Diamond": next = diamond(x, y); break;
                case "Julia": next = julia(x, y); break;
                default: next = linear(x, y);
            }

            // Apply transformation matrix
            x = next[0] * f[0] + next[1] * f[1] + f[4];
            y = next[0] * f[2] + next[1] * f[3] + f[5];

            break;
        }
    }

    return i < 1000000;
}