Arcade excitement on display.
Log in to post a comment.
// Forked from "Rasterball" by maraz
// https://turtletoy.net/turtle/ebef974b5d
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 squareSize = 3.275;
const perspective = 8.5;
const xshift = -87;
const yspace = 5;
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 balls = [{
size: 75,
xPos: 93,
},
{
size: 46,
xPos: 44.75,
},
{
size: 29.5,
xPos: 7,
},
{
size: 21.75,
xPos: -22.75,
},
{
size: 17.75,
xPos: -55,
},
{
size: 16,
xPos: -84,
}]
const shadows = balls.map(b => ({
shadow: true,
size: b.size*0.75,
xPos: b.xPos,
yPos: b.size*0.4-1.25/b.size*halfCoord,
}))
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 skyStep = (xl, yl) => Math.abs(yl/3)%2!=0 && Math.abs(xl/3)%2!=0
function sky(x, y, perspective) {
const sq = squareSize - 1.5*squareSize*perspective*y/halfCoord/2;
const xl = Math.round(x/sq);
const yl = Math.round(perspective*y/sq);
turtle.step(skyStep(xl, yl));
}
const checkCircle = (circle, x, y) => {
const ax = Math.abs(x);
const ay = Math.abs(y);
if (ay < circle.size && ax < circle.size) {
const circlePos = Math.sqrt(x*x+y*y);
if (circlePos < circle.size) {
return circlePos;
}
}
return false;
}
const checkShadows = (x, y) => {
for (const shadow of shadows) {
const _x = x - shadow.xPos - xshift;
const _y = y - shadow.yPos - yspace;
const shadowPos = checkCircle(shadow, _x, _y);
if (shadowPos != false) {
return { ...shadow, x: _x, y: _y, shadowPos };
}
}
return false;
}
const groundStep = (xq, yq) => ((xq+1)%2==0 && (yq+1)%2==0) || (xq%2==0 && yq%2==0);
const groundShadowStep = (shadow) => Math.round((Math.random() + shadow.shadowPos/shadow.size)/2)
function ground(x, y, perspective) {
const sq = 2*squareSize - 2*squareSize*perspective*y/halfCoord/2;
const xq = Math.round(x/sq);
const yq = Math.round(perspective*y/sq);
const shadow = checkShadows(x, y);
if (shadow) {
turtle.step(groundShadowStep(shadow, xq, yq) && groundStep(xq, yq));
} else {
turtle.step(groundStep(xq, yq));
}
}
const checkBalls = (x, y) => {
for (const ball of balls) {
const _x = x - ball.xPos;
const ballPos = checkCircle(ball, _x, y);
if (ballPos != false) {
return { ...ball, x: _x, y, ballPos };
}
}
return false;
}
const skyReflectionStep = (xq, yq) => Math.abs(yq/11)%2!=0 && Math.abs((xq+12))%5!=0
const fadeStep = (y, intensity, length = intensity*5) => !!
Math.round(
Math.log10(
(length - Math.abs(y))
* Math.random()
)
) % intensity
// Conditions for drawing the pattern.
const checkerStep = (_ball) => {
const sq = _ball.size/10*squareSize - _ball.size/10.6*squareSize*(_ball.ballPos/_ball.size);
const xq = Math.round(_ball.x*(1.5)/sq);
const yq = Math.round(_ball.y*(1.5)/sq);
return (
// Draw the upper part normally.
(_ball.y < _ball.size * 0.8)
||
// For lower part, create a noise shadow.
// Increased probability here means lighter shade.
Math.round(1-(Math.random()*((_ball.ballPos - 0.81*_ball.size)/(_ball.size*0.25))))==1
)
// Drawing the pattern.
&& groundStep(xq, yq)
}
function ball(_ball) {
const sq = _ball.size/10*squareSize - _ball.size/10.5*squareSize*(_ball.ballPos/_ball.size);
const xq = Math.round(_ball.x*0.5/sq);
const yq = Math.round(_ball.y*2/sq);
// Sky reflections.
if (yq+yspace*(0.12+0.002*_ball.size) < 0 && _ball.y < 0) {
turtle.step(skyReflectionStep(xq, yq));
}
// Reflection of night sky outside the exhibition.
else if (_ball.y < 0)
turtle.step(!fadeStep(yq, 2));
// Horizon line.
else if (_ball.y === 0)
turtle.step(false);
// Reflection of ground outside the exhibition.
else if (_ball.y > 0 && yq-yspace*(0.12+0.002*_ball.size) < 0)
turtle.step(!fadeStep(yq, 5));
// Checkerboard pattern on the balls.
else {
turtle.step(checkerStep(_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 = y < 75 && y > -75 && checkBalls(x, y-yStep*stillMultiplying);
if (maybeBall) {
ball(maybeBall);
}
else {
if (y < 0) {
sky(x+xshift, y+yspace, perspective);
} else {
ground(x+xshift, y-yspace, -perspective);
}
}
return y < bottomCoord;
}