The Bat-Cat

Part cat, part bat, slightly snail... completely a terror from the deep.

Log in to post a comment.

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

const quality = 1.5; // min=0.15, max=5, 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 = 2;

const eyeSize = 7.5;
const eyesY = -30;
const eyesX = 45;
const eyeDarkness = 0.66;

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) => eyesX + (-1+i)*4.5*eyeSize,
    (i) => eyesY + i*2.5,
    (i) => eyeSize - i*0.5,
    (i) => eyeDarkness,
)

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(1-(Math.random()*(-_ball.x-_ball.ballPos/(_ball.size))))>1
        ||
        Math.round(1-(Math.random()*(_ball.x-_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(15*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+x))))
            ) 
            * Math.random()/0.25*Math.abs(y)/halfCoord
        )>1
    );
}

function drawBackground(x, y) {
    turtle.step(backgroundStep(x, y, 3084, -61, 0.0023, 0.0091, 0.0244));
}
  
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;
}