// SPDX-FileCopyrightText: Chris Pressey, the creator of this work, has dedicated it to the public domain.
// For more information, please refer to <https://unlicense.org/>
// SPDX-License-Identifier: Unlicense
SpinnerSanctum = function() {
this.init = function(cfg) {
this.canvas = cfg.canvas;
this.ctx = this.canvas.getContext('2d');
this.size = cfg.size;
this.numPoints = 50;
this.gridSize = 8;
this.backingCanvas1 = document.createElement('canvas');
this.backingCanvas1.width = this.size;
this.backingCanvas1.height = this.size;
this.backingCtx1 = this.backingCanvas1.getContext('2d');
this.backingCanvas2 = document.createElement('canvas');
this.backingCanvas2.width = this.size;
this.backingCanvas2.height = this.size;
this.backingCtx2 = this.backingCanvas2.getContext('2d');
this.reset();
};
this.drawOne = function(ctx) {
const size = this.size;
ctx.fillRect(0, 0, size, size);
const points = [];
for (let i = 0; i < this.numPoints; i++) {
let point;
const phase = i % 4;
switch(phase) {
case 0:
point = {
x: Math.random() * (size / 3),
y: size / 3 + Math.random() * (size / 3)
};
break;
case 1:
point = {
x: size / 3 + Math.random() * (size / 3),
y: size * (2/3) + Math.random() * (size / 3)
};
break;
case 2:
point = {
x: size * (2/3) + Math.random() * (size / 3),
y: size / 3 + Math.random() * (size / 3)
};
break;
case 3:
point = {
x: size / 3 + Math.random() * (size / 3),
y: Math.random() * (size / 3)
};
break;
}
points.push(point);
}
ctx.lineWidth = 4;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
// Draw Bezier curves between consecutive points
for (let i = 1; i < points.length; i++) {
const p1 = points[i - 1];
const p2 = points[i];
// By choosing a control point with a same column as one point and the
// same row as the other point, we create a quarter-ellipse shape. But
// we must choose the correct orientation for each step in progression.
let controlPoint;
const phase = i % 4;
if (phase === 0) {
controlPoint = {
x: p2.x,
y: p1.y
}
} else if (phase === 1) {
controlPoint = {
x: p1.x,
y: p2.y
}
} else if (phase === 2) {
controlPoint = {
x: p2.x,
y: p1.y
}
} else if (phase === 3) {
controlPoint = {
x: p1.x,
y: p2.y
}
}
ctx.moveTo(p1.x, p1.y);
// ctx.arc(p1.x, p1.y, 5, 0, 2 * Math.PI);
// ctx.moveTo(p1.x, p1.y);
ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, p2.x, p2.y);
}
ctx.stroke();
}
this.combineDrawings = function(ctx, backingCanvas1, backingCanvas2) {
const tileSize = this.size / this.gridSize;
for (let row = 0; row < this.gridSize; row++) {
for (let col = 0; col < this.gridSize; col++) {
const parity = (row + col) % 2;
const srcCanvas = parity === 0 ? backingCanvas1 : backingCanvas2;
ctx.drawImage(
srcCanvas,
col * tileSize, row * tileSize, tileSize, tileSize,
col * tileSize, row * tileSize, tileSize, tileSize
);
}
}
}
this.reset = function() {
this.backingCtx1.fillStyle = '#000000';
this.backingCtx1.strokeStyle = '#ffffff';
this.drawOne(this.backingCtx1);
this.backingCtx2.fillStyle = '#ffffff';
this.backingCtx2.strokeStyle = '#000000';
this.drawOne(this.backingCtx2);
this.combineDrawings(this.ctx, this.backingCanvas1, this.backingCanvas2);
}
}