Separate model from view
This commit is contained in:
parent
867a0cdd77
commit
a2eae95e5b
|
@ -2,61 +2,158 @@
|
|||
const {$, doAfterLoad} = window.fwdekker;
|
||||
|
||||
|
||||
/**
|
||||
* A two-dimensional point.
|
||||
*/
|
||||
class Point {
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new `Point`.
|
||||
*
|
||||
* @param x the X coordinate of the `Point`
|
||||
* @param y the Y coordinate of the `Point`
|
||||
*/
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if and only if this `Point` is exactly the same as `other`.
|
||||
*
|
||||
* @param other the `Point` to check equality against
|
||||
* @return `true` if and only if this `Point` is exactly the same as `other`
|
||||
*/
|
||||
equals(other: Point): boolean {
|
||||
return this.x === other.x && this.y === other.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A segment that is part of a [Line].
|
||||
*/
|
||||
class LineSegment {
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new [LineSegment].
|
||||
*
|
||||
* @param x the relative horizontal translation of this `LineSegment`
|
||||
* @param y the relative vertical translation of this `LineSegment`
|
||||
*/
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if and only if this `LineSegment` is exactly the same as `other`.
|
||||
*
|
||||
* @param other the `LineSegment` to check equality against
|
||||
* @return `true` if and only if this `LineSegment` is exactly the same as `other`
|
||||
*/
|
||||
equals(other: LineSegment): boolean {
|
||||
return this.x === other.x && this.y === other.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A line that is made up of [LineSegment]s.
|
||||
*/
|
||||
class Line {
|
||||
segments: LineSegment[];
|
||||
thickness: number;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new `Line`.
|
||||
*
|
||||
* @param thickness the relative thickness of the line
|
||||
*/
|
||||
constructor(thickness: number) {
|
||||
this.segments = [];
|
||||
this.thickness = thickness;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if and only if this `Line` is exactly the same as `other`.
|
||||
*
|
||||
* @param other the `Line` to check equality against
|
||||
* @return `true` if and only if this `Line` is exactly the same as `other`
|
||||
*/
|
||||
equals(other: Line): boolean {
|
||||
return this.segments.every((_, i) => this.segments[i].equals(other.segments[i]))
|
||||
&& this.thickness === other.thickness;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
doAfterLoad(() => $("main").classList.remove("hidden"));
|
||||
|
||||
|
||||
doAfterLoad(() => {
|
||||
$("main").classList.remove("hidden");
|
||||
|
||||
// State
|
||||
const startTime = Math.floor(Date.now() / 100);
|
||||
let lastDrawTime = 0;
|
||||
const linePartAngles: number[] = [];
|
||||
|
||||
// Draw
|
||||
const canvas = $("#art");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const draw = () => {
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const center = [canvas.width / 2, canvas.height / 2];
|
||||
// State
|
||||
const lines: Line[] = [];
|
||||
lines.push(new Line(10));
|
||||
setInterval(() => {
|
||||
const angle = (random_normal() - 0.5) * Math.PI * 2;
|
||||
lines[0].segments.push(new LineSegment(Math.cos(angle) * 25, Math.sin(angle) * 25));
|
||||
}, 1000);
|
||||
|
||||
// Center dot
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.arc(center[0] - 5, center[1] - 5, 10, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
|
||||
// Lines
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "#ffffff";
|
||||
ctx.moveTo(center[0], center[1]);
|
||||
let lastPos = [center[0], center[1]];
|
||||
let totalAngle = 0;
|
||||
for (const linePartAngle of linePartAngles) {
|
||||
totalAngle += linePartAngle;
|
||||
lastPos = [lastPos[0] + Math.cos(totalAngle) * 25, lastPos[1] + Math.sin(totalAngle) * 25];
|
||||
ctx.lineTo(lastPos[0], lastPos[1]);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
const secondsSinceStart = Math.floor(Date.now() / 100) - startTime;
|
||||
if (secondsSinceStart > lastDrawTime) {
|
||||
lastDrawTime = secondsSinceStart;
|
||||
linePartAngles.push((random_normal() - 0.5) * Math.PI * 2);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(draw);
|
||||
};
|
||||
window.requestAnimationFrame(draw);
|
||||
|
||||
// Listen to resize
|
||||
// Resize
|
||||
const resize = () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
};
|
||||
window.addEventListener("resize", resize, false);
|
||||
resize();
|
||||
|
||||
// Draw
|
||||
const draw = () => {
|
||||
const center = new Point(canvas.width / 2, canvas.height / 2);
|
||||
|
||||
// Background
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Center dot
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.arc(center.x - 5, center.y - 5, 10, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
|
||||
// Lines
|
||||
ctx.strokeStyle = "#ffffff";
|
||||
for (const line of lines) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(center.x, center.y);
|
||||
|
||||
let lastPos = new Point(center.x, center.y);
|
||||
for (const segment of line.segments) {
|
||||
lastPos = new Point(lastPos.x + segment.x, lastPos.y + segment.y);
|
||||
ctx.lineTo(lastPos.x, lastPos.y);
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(draw);
|
||||
};
|
||||
window.requestAnimationFrame(draw);
|
||||
});
|
||||
|
||||
|
||||
// Taken from https://stackoverflow.com/a/49434653/
|
||||
function random_normal(): number {
|
||||
let u = 0;
|
||||
|
|
Loading…
Reference in New Issue