Planet Necklace

A pendant fit for a Titan.

Log in to post a comment.

// Forked from "Multiball Exhibition" by maraz
// https://turtletoy.net/turtle/51730d3ea5

const quality = 2.25; // min=0.15, max=9, step=0.15
const letterbox = 0; // min=0.0, max=1.0, step=0.01

const canvasSize = 1024;
const coordSize = 200;
const step = coordSize/canvasSize/quality;
const halfCanvas = canvasSize/2;
const halfCoord = coordSize/2;
const topCoord = -halfCoord+letterbox*halfCoord;
const bottomCoord = halfCoord - letterbox*halfCoord;

const nBalls = 23;

const ballMinSize = 1;
const ballMaxSize = 40;
const ballYGap = 5;
const ballsY = -80.5;
const ballsX = 61;
const ballsMinDX = -60;
const ballsMaxDX = 15;

const ballDarkest = 0.6;
const ballLightest = 1;

const ballSeries = (n, xFn, yFn, sizeFn, darknessFn) => {
    let balls = []
    for (let i = 0; i <= n; i++) {
        balls.push({
            i,
            size: sizeFn(i),
            xPos: xFn(i),
            yPos: yFn(i),
            darkness: darknessFn(i),
        })
    }
    return balls.reverse();
}

const balls = ballSeries(nBalls, 
    (i) => ballsX
        +((i+1)%2) * i**2.7/nBalls/64*ballsMinDX
        +(i%2)     * i**2.7/nBalls/64*ballsMaxDX,
    (i) => ballsY
        +((i+1)%2) * (Math.sin(Math.PI*0.76+Math.PI*i**2/nBalls*0.0102)*i**2/nBalls*2)*ballYGap
        +(i%2)     * (Math.sin(Math.PI*0.29+Math.PI*i**2/nBalls*0.0102)*i**2/nBalls*2)*ballYGap,
    (i) => ballMinSize+((1+i)/nBalls)**2*(ballMaxSize-ballMinSize),
    (i) => ballLightest+(i)/nBalls*(ballDarkest-ballLightest),
)

Turtle.prototype.step = function(draw = false) {
    draw && this.pendown();
    this.forward(step);
    draw && this.penup();
}

// This gives us antialiasing for quality > 1.
Canvas.setpenopacity(-1+0.0875*(quality-1));
const turtle = new Turtle();
turtle.penup();
turtle.goto(-halfCoord, topCoord);
turtle.seth(0);

const getCirclePos = (x, y) => Math.sqrt(x*x+y*y);

const checkCircle = (circle, x, y) => {
    const ax = Math.abs(x);
    const ay = Math.abs(y);
    if (ay < circle.size && ax < circle.size) {
        const circlePos = getCirclePos(x, y);
        if (circlePos < circle.size) {
            return circlePos;
        }
    }
    return false;
}

const checkBalls = (x, y) => {
    for (const ball of balls) {
        const _x = x - ball.xPos;
        const _y = y - ball.yPos;
        const ballPos = checkCircle(ball, _x, _y);
        if (ballPos != false) {
            return { ...ball, x: _x, y: _y, ballPos };
        }
    }
    return false;
}

const ballStep = (_ball) => {
    return (
        (Math.round(7*Math.random()) == 0)
        ||
        Math.round(1.25-(Math.random()*(-_ball.x/(_ball.size))))>1
        ||
        Math.round(0.875-(Math.random()*(_ball.x/(_ball.size))))>1
        ||
        Math.round(1.25-(Math.random()*(_ball.ballPos/(_ball.size))))<1
    ) 
    && Math.round(_ball.darkness * Math.random())==0
}

const backgroundStep = (_x, _y, xoff, yoff, xt, yt, dt) => {
    const x = _x + xoff;
    const y = _y + yoff;
    return (
        (Math.round(7*Math.random()) == 0)
        ||
        Math.round( 
            Math.abs(-0.25 + (halfCoord/2+y)/halfCoord*8
                *Math.sin(xt*(100+y) + Math.cos(dt*(100+y)) + Math.cos(dt*(100+x)))
                *Math.cos(yt*(100+x) + Math.sin(dt*(100+x)) + Math.sin(dt*(100+y) + Math.cos(1.92*dt*(100+y))))
            ) 
            * Math.random()/0.25*Math.abs(y)/halfCoord
        )==3
    );
}

function drawBackground(x, y) {
    turtle.step(backgroundStep(x, y, 4836, -174, 0.045, 0.0063, 0.029));
}

function drawBall(_ball) {
    turtle.step(ballStep(_ball));
}

const lineMultiplier = 1/quality;
let stillMultiplying = 0;
let yStep = step/lineMultiplier;

function walk(i) {
    const x = turtle.x();
    const y = turtle.y();
    if (x > halfCoord) {
        if (quality < 1) {
            if (stillMultiplying >= lineMultiplier) {
                stillMultiplying = 0;
            } 
            stillMultiplying = stillMultiplying + 1;
            turtle.goto(-halfCoord, y+yStep);
        }
        else {
            turtle.goto(-halfCoord, y+step);
        }
        return true;
    }
    const maybeBall = checkBalls(x, y-yStep*stillMultiplying);
    if (maybeBall) {
        drawBall(maybeBall);
    }
    else {
        drawBackground(x, y);               
    }
    return y < bottomCoord;
}