introduce prettierrc formatting

This commit is contained in:
John Gatward
2026-03-31 22:19:53 +01:00
parent 415b76532a
commit 7c58ce135c
100 changed files with 85618 additions and 11596 deletions

View File

@@ -6,8 +6,5 @@
"main.5cae1eb5.css.map": "/projects/cellular_automata/static/css/main.5cae1eb5.css.map",
"main.608a23ae.js.map": "/projects/cellular_automata/static/js/main.608a23ae.js.map"
},
"entrypoints": [
"static/css/main.5cae1eb5.css",
"static/js/main.608a23ae.js"
]
}
"entrypoints": ["static/css/main.5cae1eb5.css", "static/js/main.608a23ae.js"]
}

View File

@@ -1 +1,31 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/projects/cellular_automata/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/projects/cellular_automata/logo192.png"/><link rel="manifest" href="/projects/cellular_automata/manifest.json"/><title>React App</title><script defer="defer" src="/projects/cellular_automata/static/js/main.608a23ae.js"></script><link href="/projects/cellular_automata/static/css/main.5cae1eb5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/projects/cellular_automata/favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link
rel="apple-touch-icon"
href="/projects/cellular_automata/logo192.png"
/>
<link rel="manifest" href="/projects/cellular_automata/manifest.json" />
<title>React App</title>
<script
defer="defer"
src="/projects/cellular_automata/static/js/main.608a23ae.js"
></script>
<link
href="/projects/cellular_automata/static/css/main.5cae1eb5.css"
rel="stylesheet"
/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

View File

@@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -1,2 +1,173 @@
#root,body,html{margin:0;min-height:100%}body{background:radial-gradient(circle at top,#eef4ff 0,#f8fbff 55%,#eef2f7 100%);color:#0f172a;font-family:Segoe UI,sans-serif}.cell{border:1px solid #1f2937;box-sizing:border-box;margin:0;padding:0}.dead{background:#000}.alive{background:#fff}.board{border:1px solid #94a3b8;cursor:crosshair;display:inline-block;-webkit-user-select:none;user-select:none}.row{display:flex}.horizontal-slider{margin:auto;width:100%}.example-thumb{background:#fff;border:5px solid #3774ff;border-radius:100%;box-shadow:0 0 2px 0 #00000070;cursor:pointer;display:block;position:absolute;z-index:100}.example-thumb.active{background-color:grey}.example-track{background:#ddd;position:relative}.example-track.example-track-0{background:#83a9ff}.horizontal-slider .example-track{height:4px;top:20px}.horizontal-slider .example-thumb{height:10px;line-height:38px;outline:none;top:12px;width:10px}.radioGroup{grid-gap:.45rem;display:grid;gap:.45rem}.radioOption{align-items:center;color:#1e293b;cursor:pointer;display:flex;font-size:.92rem;gap:.5rem}.radioOption input{accent-color:#2f7df6;margin:0}.title{margin:2rem 0 1.25rem;text-align:center}.title h1{color:#0f172a;font-size:2.2rem;letter-spacing:.03em;margin:0}.title p{color:#475569;font-size:.95rem;margin:.35rem 0 0}.body{align-items:flex-start;display:flex;gap:1.25rem;justify-content:center;margin:0 auto 1.75rem;max-width:980px;padding:0 1rem}.gameBoard{padding:.6rem}.controlPanel,.gameBoard{background:#fff;border:1px solid #d5deea;border-radius:12px;box-shadow:0 12px 28px #0f172a1a}.controlPanel{color:#1e293b;padding:1rem;width:270px}.panelSection{margin-bottom:1rem}.panelSection h3{color:#334155;font-size:.95rem;margin:0 0 .5rem}.buttonRow{grid-gap:.5rem;display:grid;gap:.5rem;grid-template-columns:repeat(3,1fr);margin-bottom:1.1rem}.controlButton{background:#f8fafc;border:1px solid #cbd5e1;border-radius:8px;color:#1e293b;cursor:pointer;font-weight:500;padding:.45rem .35rem}.controlButton:hover{background:#eef2f8}.sliderContainer{grid-gap:.25rem;display:grid;gap:.25rem}.sliderContainer span{color:#334155;font-size:.95rem}.fpsValue{color:#64748b;font-size:.85rem;margin:.3rem 0 0}
/*# sourceMappingURL=main.5cae1eb5.css.map*/
#root,
body,
html {
margin: 0;
min-height: 100%;
}
body {
background: radial-gradient(
circle at top,
#eef4ff 0,
#f8fbff 55%,
#eef2f7 100%
);
color: #0f172a;
font-family:
Segoe UI,
sans-serif;
}
.cell {
border: 1px solid #1f2937;
box-sizing: border-box;
margin: 0;
padding: 0;
}
.dead {
background: #000;
}
.alive {
background: #fff;
}
.board {
border: 1px solid #94a3b8;
cursor: crosshair;
display: inline-block;
-webkit-user-select: none;
user-select: none;
}
.row {
display: flex;
}
.horizontal-slider {
margin: auto;
width: 100%;
}
.example-thumb {
background: #fff;
border: 5px solid #3774ff;
border-radius: 100%;
box-shadow: 0 0 2px 0 #00000070;
cursor: pointer;
display: block;
position: absolute;
z-index: 100;
}
.example-thumb.active {
background-color: grey;
}
.example-track {
background: #ddd;
position: relative;
}
.example-track.example-track-0 {
background: #83a9ff;
}
.horizontal-slider .example-track {
height: 4px;
top: 20px;
}
.horizontal-slider .example-thumb {
height: 10px;
line-height: 38px;
outline: none;
top: 12px;
width: 10px;
}
.radioGroup {
grid-gap: 0.45rem;
display: grid;
gap: 0.45rem;
}
.radioOption {
align-items: center;
color: #1e293b;
cursor: pointer;
display: flex;
font-size: 0.92rem;
gap: 0.5rem;
}
.radioOption input {
accent-color: #2f7df6;
margin: 0;
}
.title {
margin: 2rem 0 1.25rem;
text-align: center;
}
.title h1 {
color: #0f172a;
font-size: 2.2rem;
letter-spacing: 0.03em;
margin: 0;
}
.title p {
color: #475569;
font-size: 0.95rem;
margin: 0.35rem 0 0;
}
.body {
align-items: flex-start;
display: flex;
gap: 1.25rem;
justify-content: center;
margin: 0 auto 1.75rem;
max-width: 980px;
padding: 0 1rem;
}
.gameBoard {
padding: 0.6rem;
}
.controlPanel,
.gameBoard {
background: #fff;
border: 1px solid #d5deea;
border-radius: 12px;
box-shadow: 0 12px 28px #0f172a1a;
}
.controlPanel {
color: #1e293b;
padding: 1rem;
width: 270px;
}
.panelSection {
margin-bottom: 1rem;
}
.panelSection h3 {
color: #334155;
font-size: 0.95rem;
margin: 0 0 0.5rem;
}
.buttonRow {
grid-gap: 0.5rem;
display: grid;
gap: 0.5rem;
grid-template-columns: repeat(3, 1fr);
margin-bottom: 1.1rem;
}
.controlButton {
background: #f8fafc;
border: 1px solid #cbd5e1;
border-radius: 8px;
color: #1e293b;
cursor: pointer;
font-weight: 500;
padding: 0.45rem 0.35rem;
}
.controlButton:hover {
background: #eef2f8;
}
.sliderContainer {
grid-gap: 0.25rem;
display: grid;
gap: 0.25rem;
}
.sliderContainer span {
color: #334155;
font-size: 0.95rem;
}
.fpsValue {
color: #64748b;
font-size: 0.85rem;
margin: 0.3rem 0 0;
}
/*# sourceMappingURL=main.5cae1eb5.css.map*/

File diff suppressed because one or more lines are too long

View File

@@ -1,13 +1,13 @@
class cLine {
constructor(a, b, col = color(180, 180, 180, 140), w = 2) {
this.a = a;
this.b = b;
this.col = col;
this.w = w;
}
drawLine() {
stroke(this.col);
strokeWeight(this.w);
line(this.a.x, this.a.y, this.b.x, this.b.y);
}
constructor(a, b, col = color(180, 180, 180, 140), w = 2) {
this.a = a;
this.b = b;
this.col = col;
this.w = w;
}
drawLine() {
stroke(this.col);
strokeWeight(this.w);
line(this.a.x, this.a.y, this.b.x, this.b.y);
}
}

View File

@@ -1,44 +1,52 @@
class cPoint {
constructor(x, y, kind = 'control') {
this.x = x;
this.y = y;
this.kind = kind;
this.r = 9;
}
set changeX(nx) { this.x = nx; }
set changeY(ny) { this.y = ny; }
get changeX() { return this.x; }
get changeY() { return this.y; }
constructor(x, y, kind = 'control') {
this.x = x;
this.y = y;
this.kind = kind;
this.r = 9;
}
set changeX(nx) {
this.x = nx;
}
set changeY(ny) {
this.y = ny;
}
get changeX() {
return this.x;
}
get changeY() {
return this.y;
}
isHit(mx, my, r = HIT_R) {
return dist(mx, my, this.x, this.y) <= r;
isHit(mx, my, r = HIT_R) {
return dist(mx, my, this.x, this.y) <= r;
}
drawPoint(hovered = false, selected = false, styleOverride = null) {
strokeWeight(2);
if (styleOverride) {
stroke(...styleOverride.stroke);
fill(...styleOverride.fill);
} else {
if (this.kind === 'end') {
stroke(0, 220, 90, 240);
fill(40, 240, 120, 200);
} else if (this.kind === 'control') {
stroke(240, 140, 0, 240);
fill(255, 180, 40, 200);
} else {
stroke(140, 160);
fill(190, 160);
}
}
drawPoint(hovered = false, selected = false, styleOverride = null) {
strokeWeight(2);
if (styleOverride) {
stroke(...styleOverride.stroke);
fill(...styleOverride.fill);
} else {
if (this.kind === 'end') {
stroke(0, 220, 90, 240);
fill(40, 240, 120, 200);
} else if (this.kind === 'control') {
stroke(240, 140, 0, 240);
fill(255, 180, 40, 200);
} else {
stroke(140, 160);
fill(190, 160);
}
}
if (selected) {
stroke(255, 255, 0);
strokeWeight(3);
} else if (hovered) {
stroke(255);
}
circle(this.x, this.y, this.r * 2);
if (selected) {
stroke(255, 255, 0);
strokeWeight(3);
} else if (hovered) {
stroke(255);
}
circle(this.x, this.y, this.r * 2);
}
}

View File

@@ -1,6 +1,8 @@
let p = [], v = [];
let p = [],
v = [];
let lines = [];
let theta = (3 / 2) * Math.PI, t = 0;
let theta = (3 / 2) * Math.PI,
t = 0;
let paused = false;
let showLabels = false;
let showInterPoints = true;
@@ -13,227 +15,236 @@ const HIT_R = 10;
const MAX_TRAIL = 1000;
function setup() {
createCanvas(1000, 800);
initCubicCurve(true);
createCanvas(1000, 800);
initCubicCurve(true);
}
function draw() {
background(32);
background(32);
stroke(120, 120, 120, 180);
strokeWeight(2);
noFill();
beginShape();
vertex(p[0].x, p[0].y);
vertex(p[1].x, p[1].y);
vertex(p[2].x, p[2].y);
vertex(p[3].x, p[3].y);
endShape();
stroke(120, 120, 120, 180);
strokeWeight(2);
noFill();
beginShape();
vertex(p[0].x, p[0].y);
vertex(p[1].x, p[1].y);
vertex(p[2].x, p[2].y);
vertex(p[3].x, p[3].y);
endShape();
if (!paused) {
if (theta > TWO_PI) theta = 0;
theta += 0.01;
t = (sin(theta) + 1) / 2; // 0..1
}
if (!paused) {
if (theta > TWO_PI) theta = 0;
theta += 0.01;
t = (sin(theta) + 1) / 2; // 0..1
}
lerpCubicCurve();
lerpCubicCurve();
for (let i = 0; i < lines.length; i++) {
lines[i].drawLine();
}
for (let i = 0; i < lines.length; i++) {
lines[i].drawLine();
}
drawCurve();
drawPointsAndLabels();
drawHUD();
drawCurve();
drawPointsAndLabels();
drawHUD();
}
function drawCurve() {
if (v.length > MAX_TRAIL) v.splice(0, v.length - MAX_TRAIL);
if (v.length > MAX_TRAIL) v.splice(0, v.length - MAX_TRAIL);
stroke(0, 255, 0, 200);
strokeWeight(5);
noFill();
beginShape();
for (let i = 0; i < v.length; i++) {
vertex(v[i].x, v[i].y);
}
endShape();
stroke(0, 255, 0, 200);
strokeWeight(5);
noFill();
beginShape();
for (let i = 0; i < v.length; i++) {
vertex(v[i].x, v[i].y);
}
endShape();
}
function drawPointsAndLabels() {
push();
noStroke();
fill(255, 70, 70, 230);
circle(p[9].x, p[9].y, 14);
pop();
for (let i = 0; i < 4; i++) {
const hovered = (i === hoverIndex);
const selected = (i === selectedIndex);
p[i].drawPoint(hovered, selected);
if (showLabels) {
noStroke();
fill(255);
textSize(14);
textAlign(LEFT, BOTTOM);
text(`P${i}`, p[i].x + 10, p[i].y - 10);
}
}
push();
noStroke();
fill(255, 70, 70, 230);
circle(p[9].x, p[9].y, 14);
pop();
for (let i = 0; i < 4; i++) {
const hovered = i === hoverIndex;
const selected = i === selectedIndex;
p[i].drawPoint(hovered, selected);
if (showLabels) {
noStroke(); fill(220);
textSize(12); textAlign(LEFT, BOTTOM);
text(`P4`, p[4].x + 8, p[4].y - 8);
noStroke();
fill(255);
textSize(14);
textAlign(LEFT, BOTTOM);
text(`P${i}`, p[i].x + 10, p[i].y - 10);
}
}
if (showInterPoints) {
const styleMid = {
stroke: [80, 180, 255, 100],
fill: [80, 180, 255, 50]
};
for (let i = 4; i <= 8; i++) {
p[i].drawPoint(false, false, styleMid);
if (showLabels) {
noStroke(); fill(200);
textSize(12); textAlign(LEFT, BOTTOM);
text(`P${i}`, p[i].x + 8, p[i].y - 8);
}
}
if (showLabels) {
noStroke();
fill(220);
textSize(12);
textAlign(LEFT, BOTTOM);
text(`P4`, p[4].x + 8, p[4].y - 8);
}
if (showInterPoints) {
const styleMid = {
stroke: [80, 180, 255, 100],
fill: [80, 180, 255, 50],
};
for (let i = 4; i <= 8; i++) {
p[i].drawPoint(false, false, styleMid);
if (showLabels) {
noStroke();
fill(200);
textSize(12);
textAlign(LEFT, BOTTOM);
text(`P${i}`, p[i].x + 8, p[i].y - 8);
}
}
}
}
function drawHUD() {
noStroke();
fill(255);
textSize(14);
textAlign(LEFT, TOP);
const hud = [
`t = ${t.toFixed(3)}`,
`Space = ${paused ? 'Resume' : 'Pause'}`,
`H = Toggle points 5..8 | L = Toggle labels | R = Reset`,
`Drag a handle (P0..P3). Hold Shift to move the entire curve.`
];
for (let i = 0; i < hud.length; i++) {
text(hud[i], 12, 12 + i * 18);
}
noStroke();
fill(255);
textSize(14);
textAlign(LEFT, TOP);
const hud = [
`t = ${t.toFixed(3)}`,
`Space = ${paused ? 'Resume' : 'Pause'}`,
`H = Toggle points 5..8 | L = Toggle labels | R = Reset`,
`Drag a handle (P0..P3). Hold Shift to move the entire curve.`,
];
for (let i = 0; i < hud.length; i++) {
text(hud[i], 12, 12 + i * 18);
}
}
function initCubicCurve(resetPositions = true) {
p.length = 0;
v.length = 0;
lines.length = 0;
theta = (3 / 2) * Math.PI;
t = 0;
p.length = 0;
v.length = 0;
lines.length = 0;
theta = (3 / 2) * Math.PI;
t = 0;
if (resetPositions) {
p[0] = new cPoint(width * 0.15, height * 0.75, 'end');
p[1] = new cPoint(width * 0.35, height * 0.20, 'control');
p[2] = new cPoint(width * 0.65, height * 0.20, 'control');
p[3] = new cPoint(width * 0.85, height * 0.75, 'end');
} else {
for (let i = 0; i < 4; i++) {
if (!p[i]) p[i] = new cPoint(width * (0.15 + i * 0.2), height / 2, i === 0 || i === 3 ? 'end' : 'control');
}
if (resetPositions) {
p[0] = new cPoint(width * 0.15, height * 0.75, 'end');
p[1] = new cPoint(width * 0.35, height * 0.2, 'control');
p[2] = new cPoint(width * 0.65, height * 0.2, 'control');
p[3] = new cPoint(width * 0.85, height * 0.75, 'end');
} else {
for (let i = 0; i < 4; i++) {
if (!p[i])
p[i] = new cPoint(
width * (0.15 + i * 0.2),
height / 2,
i === 0 || i === 3 ? 'end' : 'control'
);
}
}
// Derived points
p[4] = new cPoint(0, 0, 'derived');
p[5] = new cPoint(0, 0, 'derived');
p[6] = new cPoint(0, 0, 'derived');
p[7] = new cPoint(0, 0, 'derived');
p[8] = new cPoint(0, 0, 'derived');
p[9] = new cPoint(0, 0, 'derived');
// Derived points
p[4] = new cPoint(0, 0, 'derived');
p[5] = new cPoint(0, 0, 'derived');
p[6] = new cPoint(0, 0, 'derived');
p[7] = new cPoint(0, 0, 'derived');
p[8] = new cPoint(0, 0, 'derived');
p[9] = new cPoint(0, 0, 'derived');
lines[0] = new cLine(p[4], p[5], color(160, 160, 160, 200), 2);
lines[1] = new cLine(p[5], p[6], color(160, 160, 160, 200), 2);
lines[0] = new cLine(p[4], p[5], color(160, 160, 160, 200), 2);
lines[1] = new cLine(p[5], p[6], color(160, 160, 160, 200), 2);
lines[2] = new cLine(p[7], p[8], color(200, 200, 200, 220), 3);
lines[2] = new cLine(p[7], p[8], color(200, 200, 200, 220), 3);
}
function lerpCubicCurve() {
// Level 1
p[4].changeX = (1 - t) * p[0].x + t * p[1].x;
p[4].changeY = (1 - t) * p[0].y + t * p[1].y;
// Level 1
p[4].changeX = (1 - t) * p[0].x + t * p[1].x;
p[4].changeY = (1 - t) * p[0].y + t * p[1].y;
p[5].changeX = (1 - t) * p[1].x + t * p[2].x;
p[5].changeY = (1 - t) * p[1].y + t * p[2].y;
p[5].changeX = (1 - t) * p[1].x + t * p[2].x;
p[5].changeY = (1 - t) * p[1].y + t * p[2].y;
p[6].changeX = (1 - t) * p[2].x + t * p[3].x;
p[6].changeY = (1 - t) * p[2].y + t * p[3].y;
p[6].changeX = (1 - t) * p[2].x + t * p[3].x;
p[6].changeY = (1 - t) * p[2].y + t * p[3].y;
// Level 2
p[7].changeX = (1 - t) * p[4].x + t * p[5].x;
p[7].changeY = (1 - t) * p[4].y + t * p[5].y;
// Level 2
p[7].changeX = (1 - t) * p[4].x + t * p[5].x;
p[7].changeY = (1 - t) * p[4].y + t * p[5].y;
p[8].changeX = (1 - t) * p[5].x + t * p[6].x;
p[8].changeY = (1 - t) * p[5].y + t * p[6].y;
p[8].changeX = (1 - t) * p[5].x + t * p[6].x;
p[8].changeY = (1 - t) * p[5].y + t * p[6].y;
// Final on-curve
p[9].changeX = (1 - t) * p[7].x + t * p[8].x;
p[9].changeY = (1 - t) * p[7].y + t * p[8].y;
// Final on-curve
p[9].changeX = (1 - t) * p[7].x + t * p[8].x;
p[9].changeY = (1 - t) * p[7].y + t * p[8].y;
v.push(new cPoint(p[9].x, p[9].y, 'derived'));
v.push(new cPoint(p[9].x, p[9].y, 'derived'));
}
function mouseMoved() {
hoverIndex = -1;
for (let i = 0; i < 4; i++) {
if (p[i].isHit(mouseX, mouseY)) {
hoverIndex = i;
break;
}
hoverIndex = -1;
for (let i = 0; i < 4; i++) {
if (p[i].isHit(mouseX, mouseY)) {
hoverIndex = i;
break;
}
}
}
function mousePressed() {
prevMouse.x = mouseX;
prevMouse.y = mouseY;
prevMouse.x = mouseX;
prevMouse.y = mouseY;
selectedIndex = -1;
for (let i = 0; i < 4; i++) {
if (p[i].isHit(mouseX, mouseY)) {
selectedIndex = i;
break;
}
selectedIndex = -1;
for (let i = 0; i < 4; i++) {
if (p[i].isHit(mouseX, mouseY)) {
selectedIndex = i;
break;
}
}
draggingAll = keyIsDown(SHIFT) && selectedIndex === -1;
draggingAll = keyIsDown(SHIFT) && selectedIndex === -1;
}
function mouseDragged() {
const dx = mouseX - prevMouse.x;
const dy = mouseY - prevMouse.y;
const dx = mouseX - prevMouse.x;
const dy = mouseY - prevMouse.y;
if (selectedIndex >= 0) {
p[selectedIndex].changeX = mouseX;
p[selectedIndex].changeY = mouseY;
v.length = 0;
} else if (draggingAll) {
for (let i = 0; i < 4; i++) {
p[i].changeX = p[i].x + dx;
p[i].changeY = p[i].y + dy;
}
v.length = 0;
if (selectedIndex >= 0) {
p[selectedIndex].changeX = mouseX;
p[selectedIndex].changeY = mouseY;
v.length = 0;
} else if (draggingAll) {
for (let i = 0; i < 4; i++) {
p[i].changeX = p[i].x + dx;
p[i].changeY = p[i].y + dy;
}
v.length = 0;
}
prevMouse.x = mouseX;
prevMouse.y = mouseY;
return false;
prevMouse.x = mouseX;
prevMouse.y = mouseY;
return false;
}
function mouseReleased() {
selectedIndex = -1;
draggingAll = false;
selectedIndex = -1;
draggingAll = false;
}
function keyPressed() {
if (key === ' ') {
paused = !paused;
} else if (key === 'R' || key === 'r') {
initCubicCurve(true);
} else if (key === 'H' || key === 'h') {
showInterPoints = !showInterPoints;
} else if (key === 'L' || key === 'l') {
showLabels = !showLabels;
}
if (key === ' ') {
paused = !paused;
} else if (key === 'R' || key === 'r') {
initCubicCurve(true);
} else if (key === 'H' || key === 'h') {
showInterPoints = !showInterPoints;
} else if (key === 'L' || key === 'l') {
showLabels = !showLabels;
}
}

View File

@@ -1,191 +1,191 @@
<!doctype html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
</head>
<style>
body {
font-family: Arial, sans-serif;
background: #fafafa;
margin: 20px;
text-align: center;
}
#container {
max-width: 900px;
margin: 0 auto;
}
h1,
h2,
p {
margin: 10px 0;
}
</style>
<div id="container">
<h1>Ellipse Construction</h1>
<p>
Inspired by a Richard Feyman's
<a href="https://en.wikipedia.org/wiki/Feynman's_Lost_Lecture"
>lost lecture</a
>
&
<a href="https://www.youtube.com/watch?v=xdIjYBtnvZU"
>3blue1brown's video</a
>.
</p>
<p>A geometric proof as to why planetary orbits are ellipitcal.</p>
<h2 id="PIbox"></h2>
<h3 id="percentage"></h3>
</div>
<script>
class dot {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
var ux, uy;
var points = [];
var numericValue = 15;
function setup() {
createCanvas(800, 800);
background(41);
}
function draw() {
translate(width / 2, height / 2);
background(41);
stroke(75, 75, 215);
strokeWeight(3);
noFill();
ellipse(0, 0, width - 0.0625 * width, height - 0.0625 * height);
stroke(215, 215, 15);
strokeWeight(7);
point(0, 0);
if (mouseY > 0 && mouseY < height && mouseX > 0 && mouseX < width) {
if (mouseIsPressed == true && mouseButton == LEFT) {
ux = mouseX - width / 2;
uy = mouseY - height / 2;
point(ux, uy);
getCirclePoints();
drawLines(ux, uy);
drawTLines(ux, uy);
} else if (mouseIsPressed == true && mouseButton == RIGHT) {
ux = mouseX - width / 2;
uy = mouseY - height / 2;
point(ux, uy);
getCirclePoints();
drawTLines(ux, uy);
}
}
}
function getCirclePoints() {
var r = (width - 0.0625 * width) / 2;
var step = 1 / numericValue;
var index = 0;
for (var i = 0; i < TWO_PI; i += step) {
var cx = r * Math.sin(i);
var cy = r * Math.cos(i);
points[index] = new dot(cx, cy);
index++;
}
}
function drawLines(startX, startY) {
strokeWeight(0.4);
stroke(255, 100);
for (var i = 0; i < points.length; i++) {
line(startX, startY, points[i].x, points[i].y);
//findMidpoint(startX, startY, points[i].x, points[i].y);
}
}
function drawTLines(startX, startY) {
strokeWeight(0.4);
stroke(255);
for (var i = 0; i < points.length; i++) {
findMidpoint(startX, startY, points[i].x, points[i].y);
}
}
function findMidpoint(x1, y1, x2, y2) {
//find center
var cx = (x1 + x2) / 2;
var cy = (y1 + y2) / 2;
//move line to the center on the origin
x1 -= cx;
y1 -= cy;
x2 -= cx;
y2 -= cy;
//rotate both points
xtemp = x1;
ytemp = y1;
x1 = -ytemp;
y1 = xtemp;
xtemp = x2;
ytemp = y2;
x2 = -ytemp;
y2 = xtemp;
//move the center point back to where it was
x1 += cx;
y1 += cy;
x2 += cx;
y2 += cy;
stroke(255, 0, 0);
line(x1, y1, x2, y2);
stroke(255);
}
function genLines() {
var pointOk = false;
do {
ux = random(width);
uy = random(height);
if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
pointOk = true;
}
} while (!pointOk);
point(ux, uy);
getCirclePoints();
drawLines(ux, uy);
drawTLines(ux, uy);
}
function genTLines() {
var pointOk = false;
do {
ux = random(width);
uy = random(height);
if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
pointOk = true;
}
} while (!pointOk);
point(ux, uy);
getCirclePoints();
drawTLines(ux, uy);
}
function getDist(x1, x2, y1, y2) {
return Math.sqrt((x1 - x2) ^ (2 + (y1 - y2)) ^ 2);
}
</script>
</html>
<!doctype html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
</head>
<style>
body {
font-family: Arial, sans-serif;
background: #fafafa;
margin: 20px;
text-align: center;
}
#container {
max-width: 900px;
margin: 0 auto;
}
h1,
h2,
p {
margin: 10px 0;
}
</style>
<div id="container">
<h1>Ellipse Construction</h1>
<p>
Inspired by a Richard Feyman's
<a href="https://en.wikipedia.org/wiki/Feynman's_Lost_Lecture"
>lost lecture</a
>
&
<a href="https://www.youtube.com/watch?v=xdIjYBtnvZU"
>3blue1brown's video</a
>.
</p>
<p>A geometric proof as to why planetary orbits are ellipitcal.</p>
<h2 id="PIbox"></h2>
<h3 id="percentage"></h3>
</div>
<script>
class dot {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
var ux, uy;
var points = [];
var numericValue = 15;
function setup() {
createCanvas(800, 800);
background(41);
}
function draw() {
translate(width / 2, height / 2);
background(41);
stroke(75, 75, 215);
strokeWeight(3);
noFill();
ellipse(0, 0, width - 0.0625 * width, height - 0.0625 * height);
stroke(215, 215, 15);
strokeWeight(7);
point(0, 0);
if (mouseY > 0 && mouseY < height && mouseX > 0 && mouseX < width) {
if (mouseIsPressed == true && mouseButton == LEFT) {
ux = mouseX - width / 2;
uy = mouseY - height / 2;
point(ux, uy);
getCirclePoints();
drawLines(ux, uy);
drawTLines(ux, uy);
} else if (mouseIsPressed == true && mouseButton == RIGHT) {
ux = mouseX - width / 2;
uy = mouseY - height / 2;
point(ux, uy);
getCirclePoints();
drawTLines(ux, uy);
}
}
}
function getCirclePoints() {
var r = (width - 0.0625 * width) / 2;
var step = 1 / numericValue;
var index = 0;
for (var i = 0; i < TWO_PI; i += step) {
var cx = r * Math.sin(i);
var cy = r * Math.cos(i);
points[index] = new dot(cx, cy);
index++;
}
}
function drawLines(startX, startY) {
strokeWeight(0.4);
stroke(255, 100);
for (var i = 0; i < points.length; i++) {
line(startX, startY, points[i].x, points[i].y);
//findMidpoint(startX, startY, points[i].x, points[i].y);
}
}
function drawTLines(startX, startY) {
strokeWeight(0.4);
stroke(255);
for (var i = 0; i < points.length; i++) {
findMidpoint(startX, startY, points[i].x, points[i].y);
}
}
function findMidpoint(x1, y1, x2, y2) {
//find center
var cx = (x1 + x2) / 2;
var cy = (y1 + y2) / 2;
//move line to the center on the origin
x1 -= cx;
y1 -= cy;
x2 -= cx;
y2 -= cy;
//rotate both points
xtemp = x1;
ytemp = y1;
x1 = -ytemp;
y1 = xtemp;
xtemp = x2;
ytemp = y2;
x2 = -ytemp;
y2 = xtemp;
//move the center point back to where it was
x1 += cx;
y1 += cy;
x2 += cx;
y2 += cy;
stroke(255, 0, 0);
line(x1, y1, x2, y2);
stroke(255);
}
function genLines() {
var pointOk = false;
do {
ux = random(width);
uy = random(height);
if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
pointOk = true;
}
} while (!pointOk);
point(ux, uy);
getCirclePoints();
drawLines(ux, uy);
drawTLines(ux, uy);
}
function genTLines() {
var pointOk = false;
do {
ux = random(width);
uy = random(height);
if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
pointOk = true;
}
} while (!pointOk);
point(ux, uy);
getCirclePoints();
drawTLines(ux, uy);
}
function getDist(x1, x2, y1, y2) {
return Math.sqrt((x1 - x2) ^ (2 + (y1 - y2)) ^ 2);
}
</script>
</html>

View File

@@ -2,46 +2,51 @@
/* eslint-disable */
export class SimulationWasm {
free(): void;
[Symbol.dispose](): void;
add_boid(): void;
boid_buffer_len(): number;
boid_buffer_ptr(): number;
boid_buffer_stride(): number;
get_align_mult(): number;
get_boid_count(): number;
get_cohesion_mult(): number;
get_separation_mult(): number;
constructor(width: number, height: number, num_boids: number);
remove_boid(): void;
resize(width: number, height: number): void;
set_align_mult(value: number): void;
set_cohesion_mult(value: number): void;
set_separation_mult(value: number): void;
step(): void;
free(): void;
[Symbol.dispose](): void;
add_boid(): void;
boid_buffer_len(): number;
boid_buffer_ptr(): number;
boid_buffer_stride(): number;
get_align_mult(): number;
get_boid_count(): number;
get_cohesion_mult(): number;
get_separation_mult(): number;
constructor(width: number, height: number, num_boids: number);
remove_boid(): void;
resize(width: number, height: number): void;
set_align_mult(value: number): void;
set_cohesion_mult(value: number): void;
set_separation_mult(value: number): void;
step(): void;
}
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly __wbg_simulationwasm_free: (a: number, b: number) => void;
readonly simulationwasm_add_boid: (a: number) => void;
readonly simulationwasm_boid_buffer_len: (a: number) => number;
readonly simulationwasm_boid_buffer_ptr: (a: number) => number;
readonly simulationwasm_boid_buffer_stride: (a: number) => number;
readonly simulationwasm_get_align_mult: (a: number) => number;
readonly simulationwasm_get_boid_count: (a: number) => number;
readonly simulationwasm_get_cohesion_mult: (a: number) => number;
readonly simulationwasm_get_separation_mult: (a: number) => number;
readonly simulationwasm_new: (a: number, b: number, c: number) => number;
readonly simulationwasm_remove_boid: (a: number) => void;
readonly simulationwasm_resize: (a: number, b: number, c: number) => void;
readonly simulationwasm_set_align_mult: (a: number, b: number) => void;
readonly simulationwasm_set_cohesion_mult: (a: number, b: number) => void;
readonly simulationwasm_set_separation_mult: (a: number, b: number) => void;
readonly simulationwasm_step: (a: number) => void;
readonly __wbindgen_export: (a: number) => void;
readonly memory: WebAssembly.Memory;
readonly __wbg_simulationwasm_free: (a: number, b: number) => void;
readonly simulationwasm_add_boid: (a: number) => void;
readonly simulationwasm_boid_buffer_len: (a: number) => number;
readonly simulationwasm_boid_buffer_ptr: (a: number) => number;
readonly simulationwasm_boid_buffer_stride: (a: number) => number;
readonly simulationwasm_get_align_mult: (a: number) => number;
readonly simulationwasm_get_boid_count: (a: number) => number;
readonly simulationwasm_get_cohesion_mult: (a: number) => number;
readonly simulationwasm_get_separation_mult: (a: number) => number;
readonly simulationwasm_new: (a: number, b: number, c: number) => number;
readonly simulationwasm_remove_boid: (a: number) => void;
readonly simulationwasm_resize: (a: number, b: number, c: number) => void;
readonly simulationwasm_set_align_mult: (a: number, b: number) => void;
readonly simulationwasm_set_cohesion_mult: (a: number, b: number) => void;
readonly simulationwasm_set_separation_mult: (a: number, b: number) => void;
readonly simulationwasm_step: (a: number) => void;
readonly __wbindgen_export: (a: number) => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;
@@ -54,7 +59,9 @@ export type SyncInitInput = BufferSource | WebAssembly.Module;
*
* @returns {InitOutput}
*/
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
export function initSync(
module: { module: SyncInitInput } | SyncInitInput
): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
@@ -64,4 +71,9 @@ export function initSync(module: { module: SyncInitInput } | SyncInitInput): Ini
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
export default function __wbg_init(
module_or_path?:
| { module_or_path: InitInput | Promise<InitInput> }
| InitInput
| Promise<InitInput>
): Promise<InitOutput>;

View File

@@ -1,270 +1,290 @@
/* @ts-self-types="./flocking.d.ts" */
export class SimulationWasm {
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
SimulationWasmFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_simulationwasm_free(ptr, 0);
}
add_boid() {
wasm.simulationwasm_add_boid(this.__wbg_ptr);
}
/**
* @returns {number}
*/
boid_buffer_len() {
const ret = wasm.simulationwasm_boid_buffer_len(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
boid_buffer_ptr() {
const ret = wasm.simulationwasm_boid_buffer_ptr(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
boid_buffer_stride() {
const ret = wasm.simulationwasm_boid_buffer_stride(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
get_align_mult() {
const ret = wasm.simulationwasm_get_align_mult(this.__wbg_ptr);
return ret;
}
/**
* @returns {number}
*/
get_boid_count() {
const ret = wasm.simulationwasm_get_boid_count(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
get_cohesion_mult() {
const ret = wasm.simulationwasm_get_cohesion_mult(this.__wbg_ptr);
return ret;
}
/**
* @returns {number}
*/
get_separation_mult() {
const ret = wasm.simulationwasm_get_separation_mult(this.__wbg_ptr);
return ret;
}
/**
* @param {number} width
* @param {number} height
* @param {number} num_boids
*/
constructor(width, height, num_boids) {
const ret = wasm.simulationwasm_new(width, height, num_boids);
this.__wbg_ptr = ret >>> 0;
SimulationWasmFinalization.register(this, this.__wbg_ptr, this);
return this;
}
remove_boid() {
wasm.simulationwasm_remove_boid(this.__wbg_ptr);
}
/**
* @param {number} width
* @param {number} height
*/
resize(width, height) {
wasm.simulationwasm_resize(this.__wbg_ptr, width, height);
}
/**
* @param {number} value
*/
set_align_mult(value) {
wasm.simulationwasm_set_align_mult(this.__wbg_ptr, value);
}
/**
* @param {number} value
*/
set_cohesion_mult(value) {
wasm.simulationwasm_set_cohesion_mult(this.__wbg_ptr, value);
}
/**
* @param {number} value
*/
set_separation_mult(value) {
wasm.simulationwasm_set_separation_mult(this.__wbg_ptr, value);
}
step() {
wasm.simulationwasm_step(this.__wbg_ptr);
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
SimulationWasmFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_simulationwasm_free(ptr, 0);
}
add_boid() {
wasm.simulationwasm_add_boid(this.__wbg_ptr);
}
/**
* @returns {number}
*/
boid_buffer_len() {
const ret = wasm.simulationwasm_boid_buffer_len(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
boid_buffer_ptr() {
const ret = wasm.simulationwasm_boid_buffer_ptr(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
boid_buffer_stride() {
const ret = wasm.simulationwasm_boid_buffer_stride(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
get_align_mult() {
const ret = wasm.simulationwasm_get_align_mult(this.__wbg_ptr);
return ret;
}
/**
* @returns {number}
*/
get_boid_count() {
const ret = wasm.simulationwasm_get_boid_count(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @returns {number}
*/
get_cohesion_mult() {
const ret = wasm.simulationwasm_get_cohesion_mult(this.__wbg_ptr);
return ret;
}
/**
* @returns {number}
*/
get_separation_mult() {
const ret = wasm.simulationwasm_get_separation_mult(this.__wbg_ptr);
return ret;
}
/**
* @param {number} width
* @param {number} height
* @param {number} num_boids
*/
constructor(width, height, num_boids) {
const ret = wasm.simulationwasm_new(width, height, num_boids);
this.__wbg_ptr = ret >>> 0;
SimulationWasmFinalization.register(this, this.__wbg_ptr, this);
return this;
}
remove_boid() {
wasm.simulationwasm_remove_boid(this.__wbg_ptr);
}
/**
* @param {number} width
* @param {number} height
*/
resize(width, height) {
wasm.simulationwasm_resize(this.__wbg_ptr, width, height);
}
/**
* @param {number} value
*/
set_align_mult(value) {
wasm.simulationwasm_set_align_mult(this.__wbg_ptr, value);
}
/**
* @param {number} value
*/
set_cohesion_mult(value) {
wasm.simulationwasm_set_cohesion_mult(this.__wbg_ptr, value);
}
/**
* @param {number} value
*/
set_separation_mult(value) {
wasm.simulationwasm_set_separation_mult(this.__wbg_ptr, value);
}
step() {
wasm.simulationwasm_step(this.__wbg_ptr);
}
}
if (Symbol.dispose) SimulationWasm.prototype[Symbol.dispose] = SimulationWasm.prototype.free;
if (Symbol.dispose)
SimulationWasm.prototype[Symbol.dispose] = SimulationWasm.prototype.free;
function __wbg_get_imports() {
const import0 = {
__proto__: null,
__wbg___wbindgen_is_function_3c846841762788c1: function(arg0) {
const ret = typeof(getObject(arg0)) === 'function';
return ret;
},
__wbg___wbindgen_is_object_781bc9f159099513: function(arg0) {
const val = getObject(arg0);
const ret = typeof(val) === 'object' && val !== null;
return ret;
},
__wbg___wbindgen_is_string_7ef6b97b02428fae: function(arg0) {
const ret = typeof(getObject(arg0)) === 'string';
return ret;
},
__wbg___wbindgen_is_undefined_52709e72fb9f179c: function(arg0) {
const ret = getObject(arg0) === undefined;
return ret;
},
__wbg___wbindgen_throw_6ddd609b62940d55: function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
},
__wbg_call_2d781c1f4d5c0ef8: function() { return handleError(function (arg0, arg1, arg2) {
const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
return addHeapObject(ret);
}, arguments); },
__wbg_crypto_38df2bab126b63dc: function(arg0) {
const ret = getObject(arg0).crypto;
return addHeapObject(ret);
},
__wbg_getRandomValues_c44a50d8cfdaebeb: function() { return handleError(function (arg0, arg1) {
getObject(arg0).getRandomValues(getObject(arg1));
}, arguments); },
__wbg_length_ea16607d7b61445b: function(arg0) {
const ret = getObject(arg0).length;
return ret;
},
__wbg_msCrypto_bd5a034af96bcba6: function(arg0) {
const ret = getObject(arg0).msCrypto;
return addHeapObject(ret);
},
__wbg_new_with_length_825018a1616e9e55: function(arg0) {
const ret = new Uint8Array(arg0 >>> 0);
return addHeapObject(ret);
},
__wbg_node_84ea875411254db1: function(arg0) {
const ret = getObject(arg0).node;
return addHeapObject(ret);
},
__wbg_process_44c7a14e11e9f69e: function(arg0) {
const ret = getObject(arg0).process;
return addHeapObject(ret);
},
__wbg_prototypesetcall_d62e5099504357e6: function(arg0, arg1, arg2) {
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), getObject(arg2));
},
__wbg_randomFillSync_6c25eac9869eb53c: function() { return handleError(function (arg0, arg1) {
getObject(arg0).randomFillSync(takeObject(arg1));
}, arguments); },
__wbg_require_b4edbdcf3e2a1ef0: function() { return handleError(function () {
const ret = module.require;
return addHeapObject(ret);
}, arguments); },
__wbg_static_accessor_GLOBAL_8adb955bd33fac2f: function() {
const ret = typeof global === 'undefined' ? null : global;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_static_accessor_GLOBAL_THIS_ad356e0db91c7913: function() {
const ret = typeof globalThis === 'undefined' ? null : globalThis;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_static_accessor_SELF_f207c857566db248: function() {
const ret = typeof self === 'undefined' ? null : self;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_static_accessor_WINDOW_bb9f1ba69d61b386: function() {
const ret = typeof window === 'undefined' ? null : window;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_subarray_a068d24e39478a8a: function(arg0, arg1, arg2) {
const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0);
return addHeapObject(ret);
},
__wbg_versions_276b2795b1c6a219: function(arg0) {
const ret = getObject(arg0).versions;
return addHeapObject(ret);
},
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
// Cast intrinsic for `Ref(Slice(U8)) -> NamedExternref("Uint8Array")`.
const ret = getArrayU8FromWasm0(arg0, arg1);
return addHeapObject(ret);
},
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
// Cast intrinsic for `Ref(String) -> Externref`.
const ret = getStringFromWasm0(arg0, arg1);
return addHeapObject(ret);
},
__wbindgen_object_clone_ref: function(arg0) {
const ret = getObject(arg0);
return addHeapObject(ret);
},
__wbindgen_object_drop_ref: function(arg0) {
takeObject(arg0);
},
};
return {
__proto__: null,
"./flocking_bg.js": import0,
};
const import0 = {
__proto__: null,
__wbg___wbindgen_is_function_3c846841762788c1: function (arg0) {
const ret = typeof getObject(arg0) === 'function';
return ret;
},
__wbg___wbindgen_is_object_781bc9f159099513: function (arg0) {
const val = getObject(arg0);
const ret = typeof val === 'object' && val !== null;
return ret;
},
__wbg___wbindgen_is_string_7ef6b97b02428fae: function (arg0) {
const ret = typeof getObject(arg0) === 'string';
return ret;
},
__wbg___wbindgen_is_undefined_52709e72fb9f179c: function (arg0) {
const ret = getObject(arg0) === undefined;
return ret;
},
__wbg___wbindgen_throw_6ddd609b62940d55: function (arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
},
__wbg_call_2d781c1f4d5c0ef8: function () {
return handleError(function (arg0, arg1, arg2) {
const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
return addHeapObject(ret);
}, arguments);
},
__wbg_crypto_38df2bab126b63dc: function (arg0) {
const ret = getObject(arg0).crypto;
return addHeapObject(ret);
},
__wbg_getRandomValues_c44a50d8cfdaebeb: function () {
return handleError(function (arg0, arg1) {
getObject(arg0).getRandomValues(getObject(arg1));
}, arguments);
},
__wbg_length_ea16607d7b61445b: function (arg0) {
const ret = getObject(arg0).length;
return ret;
},
__wbg_msCrypto_bd5a034af96bcba6: function (arg0) {
const ret = getObject(arg0).msCrypto;
return addHeapObject(ret);
},
__wbg_new_with_length_825018a1616e9e55: function (arg0) {
const ret = new Uint8Array(arg0 >>> 0);
return addHeapObject(ret);
},
__wbg_node_84ea875411254db1: function (arg0) {
const ret = getObject(arg0).node;
return addHeapObject(ret);
},
__wbg_process_44c7a14e11e9f69e: function (arg0) {
const ret = getObject(arg0).process;
return addHeapObject(ret);
},
__wbg_prototypesetcall_d62e5099504357e6: function (arg0, arg1, arg2) {
Uint8Array.prototype.set.call(
getArrayU8FromWasm0(arg0, arg1),
getObject(arg2)
);
},
__wbg_randomFillSync_6c25eac9869eb53c: function () {
return handleError(function (arg0, arg1) {
getObject(arg0).randomFillSync(takeObject(arg1));
}, arguments);
},
__wbg_require_b4edbdcf3e2a1ef0: function () {
return handleError(function () {
const ret = module.require;
return addHeapObject(ret);
}, arguments);
},
__wbg_static_accessor_GLOBAL_8adb955bd33fac2f: function () {
const ret = typeof global === 'undefined' ? null : global;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_static_accessor_GLOBAL_THIS_ad356e0db91c7913: function () {
const ret = typeof globalThis === 'undefined' ? null : globalThis;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_static_accessor_SELF_f207c857566db248: function () {
const ret = typeof self === 'undefined' ? null : self;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_static_accessor_WINDOW_bb9f1ba69d61b386: function () {
const ret = typeof window === 'undefined' ? null : window;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
},
__wbg_subarray_a068d24e39478a8a: function (arg0, arg1, arg2) {
const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0);
return addHeapObject(ret);
},
__wbg_versions_276b2795b1c6a219: function (arg0) {
const ret = getObject(arg0).versions;
return addHeapObject(ret);
},
__wbindgen_cast_0000000000000001: function (arg0, arg1) {
// Cast intrinsic for `Ref(Slice(U8)) -> NamedExternref("Uint8Array")`.
const ret = getArrayU8FromWasm0(arg0, arg1);
return addHeapObject(ret);
},
__wbindgen_cast_0000000000000002: function (arg0, arg1) {
// Cast intrinsic for `Ref(String) -> Externref`.
const ret = getStringFromWasm0(arg0, arg1);
return addHeapObject(ret);
},
__wbindgen_object_clone_ref: function (arg0) {
const ret = getObject(arg0);
return addHeapObject(ret);
},
__wbindgen_object_drop_ref: function (arg0) {
takeObject(arg0);
},
};
return {
__proto__: null,
'./flocking_bg.js': import0,
};
}
const SimulationWasmFinalization = (typeof FinalizationRegistry === 'undefined')
const SimulationWasmFinalization =
typeof FinalizationRegistry === 'undefined'
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_simulationwasm_free(ptr >>> 0, 1));
: new FinalizationRegistry((ptr) =>
wasm.__wbg_simulationwasm_free(ptr >>> 0, 1)
);
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
heap[idx] = obj;
return idx;
}
function dropObject(idx) {
if (idx < 1028) return;
heap[idx] = heap_next;
heap_next = idx;
if (idx < 1028) return;
heap[idx] = heap_next;
heap_next = idx;
}
function getArrayU8FromWasm0(ptr, len) {
ptr = ptr >>> 0;
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
ptr = ptr >>> 0;
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
}
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return decodeText(ptr, len);
ptr = ptr >>> 0;
return decodeText(ptr, len);
}
let cachedUint8ArrayMemory0 = null;
function getUint8ArrayMemory0() {
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
if (
cachedUint8ArrayMemory0 === null ||
cachedUint8ArrayMemory0.byteLength === 0
) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
}
function getObject(idx) { return heap[idx]; }
function getObject(idx) {
return heap[idx];
}
function handleError(f, args) {
try {
return f.apply(this, args);
} catch (e) {
wasm.__wbindgen_export(addHeapObject(e));
}
try {
return f.apply(this, args);
} catch (e) {
wasm.__wbindgen_export(addHeapObject(e));
}
}
let heap = new Array(1024).fill(undefined);
@@ -273,116 +293,140 @@ heap.push(undefined, null, true, false);
let heap_next = heap.length;
function isLikeNone(x) {
return x === undefined || x === null;
return x === undefined || x === null;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
const ret = getObject(idx);
dropObject(idx);
return ret;
}
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
let cachedTextDecoder = new TextDecoder('utf-8', {
ignoreBOM: true,
fatal: true,
});
cachedTextDecoder.decode();
const MAX_SAFARI_DECODE_BYTES = 2146435072;
let numBytesDecoded = 0;
function decodeText(ptr, len) {
numBytesDecoded += len;
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
numBytesDecoded = len;
}
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
numBytesDecoded += len;
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
cachedTextDecoder = new TextDecoder('utf-8', {
ignoreBOM: true,
fatal: true,
});
cachedTextDecoder.decode();
numBytesDecoded = len;
}
return cachedTextDecoder.decode(
getUint8ArrayMemory0().subarray(ptr, ptr + len)
);
}
let wasmModule, wasm;
function __wbg_finalize_init(instance, module) {
wasm = instance.exports;
wasmModule = module;
cachedUint8ArrayMemory0 = null;
return wasm;
wasm = instance.exports;
wasmModule = module;
cachedUint8ArrayMemory0 = null;
return wasm;
}
async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
const validResponse = module.ok && expectedResponseType(module.type);
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
const validResponse = module.ok && expectedResponseType(module.type);
if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else { throw e; }
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
if (
validResponse &&
module.headers.get('Content-Type') !== 'application/wasm'
) {
console.warn(
'`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n',
e
);
} else {
return instance;
throw e;
}
}
}
function expectedResponseType(type) {
switch (type) {
case 'basic': case 'cors': case 'default': return true;
}
return false;
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
function expectedResponseType(type) {
switch (type) {
case 'basic':
case 'cors':
case 'default':
return true;
}
return false;
}
}
function initSync(module) {
if (wasm !== undefined) return wasm;
if (wasm !== undefined) return wasm;
if (module !== undefined) {
if (Object.getPrototypeOf(module) === Object.prototype) {
({module} = module)
} else {
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
}
if (module !== undefined) {
if (Object.getPrototypeOf(module) === Object.prototype) {
({ module } = module);
} else {
console.warn(
'using deprecated parameters for `initSync()`; pass a single object instead'
);
}
}
const imports = __wbg_get_imports();
if (!(module instanceof WebAssembly.Module)) {
module = new WebAssembly.Module(module);
}
const instance = new WebAssembly.Instance(module, imports);
return __wbg_finalize_init(instance, module);
const imports = __wbg_get_imports();
if (!(module instanceof WebAssembly.Module)) {
module = new WebAssembly.Module(module);
}
const instance = new WebAssembly.Instance(module, imports);
return __wbg_finalize_init(instance, module);
}
async function __wbg_init(module_or_path) {
if (wasm !== undefined) return wasm;
if (wasm !== undefined) return wasm;
if (module_or_path !== undefined) {
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
({module_or_path} = module_or_path)
} else {
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
}
if (module_or_path !== undefined) {
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
({ module_or_path } = module_or_path);
} else {
console.warn(
'using deprecated parameters for the initialization function; pass a single object instead'
);
}
}
if (module_or_path === undefined) {
module_or_path = new URL('flocking_bg.wasm', import.meta.url);
}
const imports = __wbg_get_imports();
if (module_or_path === undefined) {
module_or_path = new URL('flocking_bg.wasm', import.meta.url);
}
const imports = __wbg_get_imports();
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
module_or_path = fetch(module_or_path);
}
if (
typeof module_or_path === 'string' ||
(typeof Request === 'function' && module_or_path instanceof Request) ||
(typeof URL === 'function' && module_or_path instanceof URL)
) {
module_or_path = fetch(module_or_path);
}
const { instance, module } = await __wbg_load(await module_or_path, imports);
const { instance, module } = await __wbg_load(await module_or_path, imports);
return __wbg_finalize_init(instance, module);
return __wbg_finalize_init(instance, module);
}
export { initSync, __wbg_init as default };

View File

@@ -12,4 +12,4 @@
"sideEffects": [
"./snippets/*"
]
}
}

View File

@@ -19,223 +19,227 @@ let displayHeight = 1;
let resizeAnimationFrame = 0;
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
return Math.min(Math.max(value, min), max);
}
function getInitialBoidCount(width, height) {
const areaScale = (width * height) / (1920 * 1080);
const targetCount = Math.round(BOIDS_PER_1920X1080 * areaScale);
const clampedCount = clamp(targetCount, MIN_INITIAL_BOIDS, MAX_INITIAL_BOIDS);
return Math.round(clampedCount / BOID_STEP) * BOID_STEP;
const areaScale = (width * height) / (1920 * 1080);
const targetCount = Math.round(BOIDS_PER_1920X1080 * areaScale);
const clampedCount = clamp(targetCount, MIN_INITIAL_BOIDS, MAX_INITIAL_BOIDS);
return Math.round(clampedCount / BOID_STEP) * BOID_STEP;
}
function syncBoidCountToViewport(width, height) {
const targetCount = getInitialBoidCount(width, height);
let currentCount = sim.get_boid_count();
const targetCount = getInitialBoidCount(width, height);
let currentCount = sim.get_boid_count();
while (currentCount < targetCount) {
sim.add_boid();
currentCount = sim.get_boid_count();
}
while (currentCount < targetCount) {
sim.add_boid();
currentCount = sim.get_boid_count();
}
while (currentCount > targetCount) {
sim.remove_boid();
currentCount = sim.get_boid_count();
}
while (currentCount > targetCount) {
sim.remove_boid();
currentCount = sim.get_boid_count();
}
updateBoidCount();
updateBoidCount();
}
function updateControlsCollapsedState(isCollapsed) {
if (controls === null || controlsToggle === null) {
return;
}
if (controls === null || controlsToggle === null) {
return;
}
controls.classList.toggle('collapsed', isCollapsed);
controls.setAttribute('aria-expanded', String(!isCollapsed));
controlsToggle.setAttribute('aria-expanded', String(!isCollapsed));
controlsToggle.setAttribute('aria-label', isCollapsed ? 'Expand controls' : 'Collapse controls');
controlsToggle.textContent = isCollapsed ? '' : '';
controls.classList.toggle('collapsed', isCollapsed);
controls.setAttribute('aria-expanded', String(!isCollapsed));
controlsToggle.setAttribute('aria-expanded', String(!isCollapsed));
controlsToggle.setAttribute(
'aria-label',
isCollapsed ? 'Expand controls' : 'Collapse controls'
);
controlsToggle.textContent = isCollapsed ? '' : '';
}
function sizeCanvasToViewport() {
const dpr = window.devicePixelRatio || 1;
const nextDisplayWidth = Math.max(1, Math.floor(canvas.clientWidth));
const nextDisplayHeight = Math.max(1, Math.floor(canvas.clientHeight));
const nextBufferWidth = Math.max(1, Math.round(nextDisplayWidth * dpr));
const nextBufferHeight = Math.max(1, Math.round(nextDisplayHeight * dpr));
const dpr = window.devicePixelRatio || 1;
const nextDisplayWidth = Math.max(1, Math.floor(canvas.clientWidth));
const nextDisplayHeight = Math.max(1, Math.floor(canvas.clientHeight));
const nextBufferWidth = Math.max(1, Math.round(nextDisplayWidth * dpr));
const nextBufferHeight = Math.max(1, Math.round(nextDisplayHeight * dpr));
if (canvas.width !== nextBufferWidth || canvas.height !== nextBufferHeight) {
canvas.width = nextBufferWidth;
canvas.height = nextBufferHeight;
}
if (canvas.width !== nextBufferWidth || canvas.height !== nextBufferHeight) {
canvas.width = nextBufferWidth;
canvas.height = nextBufferHeight;
}
displayWidth = nextDisplayWidth;
displayHeight = nextDisplayHeight;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
document.getElementById('canvasSize').textContent = `${displayWidth}x${displayHeight}`;
displayWidth = nextDisplayWidth;
displayHeight = nextDisplayHeight;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
document.getElementById('canvasSize').textContent =
`${displayWidth}x${displayHeight}`;
return { width: displayWidth, height: displayHeight };
return { width: displayWidth, height: displayHeight };
}
function scheduleResize() {
if (resizeAnimationFrame !== 0) {
return;
}
if (resizeAnimationFrame !== 0) {
return;
}
resizeAnimationFrame = window.requestAnimationFrame(() => {
resizeAnimationFrame = 0;
const { width, height } = sizeCanvasToViewport();
if (sim !== null) {
sim.resize(width, height);
syncBoidCountToViewport(width, height);
}
});
resizeAnimationFrame = window.requestAnimationFrame(() => {
resizeAnimationFrame = 0;
const { width, height } = sizeCanvasToViewport();
if (sim !== null) {
sim.resize(width, height);
syncBoidCountToViewport(width, height);
}
});
}
function setupResizeObserver() {
controlsResizeObserver = new ResizeObserver(() => {
scheduleResize();
});
controlsResizeObserver.observe(document.getElementById('container'));
controlsResizeObserver.observe(controls);
controlsResizeObserver = new ResizeObserver(() => {
scheduleResize();
});
controlsResizeObserver.observe(document.getElementById('container'));
controlsResizeObserver.observe(controls);
}
async function run() {
wasm = await init();
wasm = await init();
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
controls = document.getElementById('controls');
controlsToggle = document.getElementById('controlsToggle');
updateControlsCollapsedState(false);
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
controls = document.getElementById('controls');
controlsToggle = document.getElementById('controlsToggle');
updateControlsCollapsedState(false);
const { width, height } = sizeCanvasToViewport();
sim = new SimulationWasm(width, height, getInitialBoidCount(width, height));
const { width, height } = sizeCanvasToViewport();
sim = new SimulationWasm(width, height, getInitialBoidCount(width, height));
setupControls();
setupResizeObserver();
updateBoidCount();
window.addEventListener('resize', scheduleResize);
setupControls();
setupResizeObserver();
updateBoidCount();
window.addEventListener('resize', scheduleResize);
animate();
animate();
}
function setupControls() {
const alignSlider = document.getElementById('alignSlider');
const cohesionSlider = document.getElementById('cohesionSlider');
const separationSlider = document.getElementById('separationSlider');
const addBtn = document.getElementById('addBtn');
const removeBtn = document.getElementById('removeBtn');
const alignSlider = document.getElementById('alignSlider');
const cohesionSlider = document.getElementById('cohesionSlider');
const separationSlider = document.getElementById('separationSlider');
const addBtn = document.getElementById('addBtn');
const removeBtn = document.getElementById('removeBtn');
controlsToggle.addEventListener('click', () => {
const isCollapsed = !controls.classList.contains('collapsed');
updateControlsCollapsedState(isCollapsed);
scheduleResize();
});
controlsToggle.addEventListener('click', () => {
const isCollapsed = !controls.classList.contains('collapsed');
updateControlsCollapsedState(isCollapsed);
scheduleResize();
});
alignSlider.addEventListener('input', (e) => {
const value = Number.parseFloat(e.target.value);
sim.set_align_mult(value);
document.getElementById('alignValue').textContent = value.toFixed(2);
});
alignSlider.addEventListener('input', (e) => {
const value = Number.parseFloat(e.target.value);
sim.set_align_mult(value);
document.getElementById('alignValue').textContent = value.toFixed(2);
});
cohesionSlider.addEventListener('input', (e) => {
const value = Number.parseFloat(e.target.value);
sim.set_cohesion_mult(value);
document.getElementById('cohesionValue').textContent = value.toFixed(2);
});
cohesionSlider.addEventListener('input', (e) => {
const value = Number.parseFloat(e.target.value);
sim.set_cohesion_mult(value);
document.getElementById('cohesionValue').textContent = value.toFixed(2);
});
separationSlider.addEventListener('input', (e) => {
const value = Number.parseFloat(e.target.value);
sim.set_separation_mult(value);
document.getElementById('separationValue').textContent = value.toFixed(2);
});
separationSlider.addEventListener('input', (e) => {
const value = Number.parseFloat(e.target.value);
sim.set_separation_mult(value);
document.getElementById('separationValue').textContent = value.toFixed(2);
});
addBtn.addEventListener('click', () => {
sim.add_boid();
updateBoidCount();
});
addBtn.addEventListener('click', () => {
sim.add_boid();
updateBoidCount();
});
removeBtn.addEventListener('click', () => {
sim.remove_boid();
updateBoidCount();
});
removeBtn.addEventListener('click', () => {
sim.remove_boid();
updateBoidCount();
});
}
function updateBoidCount() {
document.getElementById('boidCount').textContent = sim.get_boid_count();
document.getElementById('boidCount').textContent = sim.get_boid_count();
}
function getBoidView() {
const ptr = sim.boid_buffer_ptr();
const len = sim.boid_buffer_len();
return new Float32Array(wasm.memory.buffer, ptr, len);
const ptr = sim.boid_buffer_ptr();
const len = sim.boid_buffer_len();
return new Float32Array(wasm.memory.buffer, ptr, len);
}
function animate() {
ctx.fillStyle = '#121212';
ctx.fillRect(0, 0, displayWidth, displayHeight);
ctx.fillStyle = '#121212';
ctx.fillRect(0, 0, displayWidth, displayHeight);
sim.step();
const boidData = getBoidView();
const stride = sim.boid_buffer_stride();
sim.step();
const boidData = getBoidView();
const stride = sim.boid_buffer_stride();
for (let i = 0; i < boidData.length; i += stride) {
const x = boidData[i];
const y = boidData[i + 1];
const vx = boidData[i + 2];
const vy = boidData[i + 3];
const r = boidData[i + 4];
const g = boidData[i + 5];
const b = boidData[i + 6];
const a = boidData[i + 7];
for (let i = 0; i < boidData.length; i += stride) {
const x = boidData[i];
const y = boidData[i + 1];
const vx = boidData[i + 2];
const vy = boidData[i + 3];
const r = boidData[i + 4];
const g = boidData[i + 5];
const b = boidData[i + 6];
const a = boidData[i + 7];
const speedSq = vx * vx + vy * vy;
let dx;
let dy;
if (speedSq > 1e-6) {
const invSpeed = 1 / Math.sqrt(speedSq);
dx = vx * invSpeed;
dy = vy * invSpeed;
} else {
dx = 0;
dy = -1;
}
const size = 5;
const headMult = 3;
const px = -dy;
const py = dx;
const tipX = x + dx * size * headMult;
const tipY = y + dy * size * headMult;
const baseX = x - dx * size;
const baseY = y - dy * size;
const leftX = baseX + px * size;
const leftY = baseY + py * size;
const rightX = baseX - px * size;
const rightY = baseY - py * size;
ctx.strokeStyle = `rgba(${r},${g},${b},${a / 255})`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(tipX, tipY);
ctx.lineTo(leftX, leftY);
ctx.lineTo(rightX, rightY);
ctx.closePath();
ctx.stroke();
const speedSq = vx * vx + vy * vy;
let dx;
let dy;
if (speedSq > 1e-6) {
const invSpeed = 1 / Math.sqrt(speedSq);
dx = vx * invSpeed;
dy = vy * invSpeed;
} else {
dx = 0;
dy = -1;
}
frameCount += 1;
const now = Date.now();
if (now - lastFpsUpdate >= 1000) {
document.getElementById('fps').textContent = frameCount;
frameCount = 0;
lastFpsUpdate = now;
}
const size = 5;
const headMult = 3;
const px = -dy;
const py = dx;
requestAnimationFrame(animate);
const tipX = x + dx * size * headMult;
const tipY = y + dy * size * headMult;
const baseX = x - dx * size;
const baseY = y - dy * size;
const leftX = baseX + px * size;
const leftY = baseY + py * size;
const rightX = baseX - px * size;
const rightY = baseY - py * size;
ctx.strokeStyle = `rgba(${r},${g},${b},${a / 255})`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(tipX, tipY);
ctx.lineTo(leftX, leftY);
ctx.lineTo(rightX, rightY);
ctx.closePath();
ctx.stroke();
}
frameCount += 1;
const now = Date.now();
if (now - lastFpsUpdate >= 1000) {
document.getElementById('fps').textContent = frameCount;
frameCount = 0;
lastFpsUpdate = now;
}
requestAnimationFrame(animate);
}
await run();

View File

@@ -1,239 +1,244 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--panel-width: 280px;
--panel-collapsed-width: 44px;
--panel-transition: 180ms ease;
--panel-width: 280px;
--panel-collapsed-width: 44px;
--panel-transition: 180ms ease;
}
html, body {
width: 100%;
height: 100%;
html,
body {
width: 100%;
height: 100%;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #121212;
color: #d9d9d9;
overflow: hidden;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, sans-serif;
background: #121212;
color: #d9d9d9;
overflow: hidden;
}
#container {
display: flex;
width: 100vw;
height: 100vh;
display: flex;
width: 100vw;
height: 100vh;
}
#canvas {
flex: 1;
min-width: 0;
min-height: 0;
display: block;
background: #121212;
flex: 1;
min-width: 0;
min-height: 0;
display: block;
background: #121212;
}
#controls {
width: var(--panel-width);
background: #1e1e1e;
border-left: 1px solid #333;
overflow: hidden;
display: flex;
flex-direction: column;
transition: width var(--panel-transition), min-width var(--panel-transition);
min-width: var(--panel-width);
width: var(--panel-width);
background: #1e1e1e;
border-left: 1px solid #333;
overflow: hidden;
display: flex;
flex-direction: column;
transition:
width var(--panel-transition),
min-width var(--panel-transition);
min-width: var(--panel-width);
}
#controls.collapsed {
width: var(--panel-collapsed-width);
min-width: var(--panel-collapsed-width);
width: var(--panel-collapsed-width);
min-width: var(--panel-collapsed-width);
}
#controlsHeader {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 14px 12px 12px 16px;
border-bottom: 1px solid #333;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 14px 12px 12px 16px;
border-bottom: 1px solid #333;
}
#controlsTitle {
font-size: 16px;
color: #d9d9d9;
white-space: nowrap;
font-size: 16px;
color: #d9d9d9;
white-space: nowrap;
}
#controlsToggle {
width: 32px;
min-width: 32px;
height: 32px;
padding: 0;
border: 1px solid #333;
background: #2a2a2a;
color: #d9d9d9;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
line-height: 1;
transition: all 0.2s;
width: 32px;
min-width: 32px;
height: 32px;
padding: 0;
border: 1px solid #333;
background: #2a2a2a;
color: #d9d9d9;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
line-height: 1;
transition: all 0.2s;
}
#controlsToggle:hover {
background: #3a3a3a;
border-color: #4a9eff;
background: #3a3a3a;
border-color: #4a9eff;
}
#controlsBody {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 20px;
opacity: 1;
transition: opacity 120ms ease;
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 20px;
opacity: 1;
transition: opacity 120ms ease;
}
#controls.collapsed #controlsBody {
opacity: 0;
pointer-events: none;
opacity: 0;
pointer-events: none;
}
#controls.collapsed #controlsTitle {
display: none;
display: none;
}
.control-group {
display: flex;
flex-direction: column;
gap: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
label {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #a0a0a0;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #a0a0a0;
}
input[type="range"] {
width: 100%;
height: 6px;
border-radius: 3px;
background: #333;
outline: none;
-webkit-appearance: none;
appearance: none;
input[type='range'] {
width: 100%;
height: 6px;
border-radius: 3px;
background: #333;
outline: none;
-webkit-appearance: none;
appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #4a9eff;
cursor: pointer;
transition: background 0.2s;
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #4a9eff;
cursor: pointer;
transition: background 0.2s;
}
input[type="range"]::-webkit-slider-thumb:hover {
background: #2196f3;
input[type='range']::-webkit-slider-thumb:hover {
background: #2196f3;
}
input[type="range"]::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #4a9eff;
cursor: pointer;
border: none;
transition: background 0.2s;
input[type='range']::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #4a9eff;
cursor: pointer;
border: none;
transition: background 0.2s;
}
input[type="range"]::-moz-range-thumb:hover {
background: #2196f3;
input[type='range']::-moz-range-thumb:hover {
background: #2196f3;
}
.value-display {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
}
.value-display strong {
color: #4a9eff;
font-weight: 600;
color: #4a9eff;
font-weight: 600;
}
.button-group {
display: flex;
gap: 10px;
display: flex;
gap: 10px;
}
button {
flex: 1;
padding: 10px 16px;
border: 1px solid #333;
background: #2a2a2a;
color: #d9d9d9;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
flex: 1;
padding: 10px 16px;
border: 1px solid #333;
background: #2a2a2a;
color: #d9d9d9;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
}
button:hover {
background: #3a3a3a;
border-color: #4a9eff;
background: #3a3a3a;
border-color: #4a9eff;
}
button:active {
background: #1a1a1a;
background: #1a1a1a;
}
.stats {
padding: 12px;
background: #252525;
border-radius: 4px;
border-left: 3px solid #4a9eff;
font-size: 13px;
padding: 12px;
background: #252525;
border-radius: 4px;
border-left: 3px solid #4a9eff;
font-size: 13px;
}
.stat-item {
display: flex;
justify-content: space-between;
margin-bottom: 6px;
display: flex;
justify-content: space-between;
margin-bottom: 6px;
}
.stat-item:last-child {
margin-bottom: 0;
margin-bottom: 0;
}
.stat-label {
color: #a0a0a0;
color: #a0a0a0;
}
.stat-value {
color: #4a9eff;
font-weight: 600;
font-family: 'Courier New', monospace;
color: #4a9eff;
font-weight: 600;
font-family: 'Courier New', monospace;
}
#fps {
color: #4a9eff;
font-size: 13px;
font-family: 'Courier New', monospace;
color: #4a9eff;
font-size: 13px;
font-family: 'Courier New', monospace;
}
.divider {
height: 1px;
background: #333;
height: 1px;
background: #333;
}

View File

@@ -1,120 +1,120 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Fourier Series — Epicycles (p5.js)</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background: #fafafa;
margin: 20px;
text-align: center;
}
#container {
max-width: 900px;
margin: 0 auto;
}
h1,
h2,
p {
margin: 10px 0;
}
input[type="range"] {
display: block;
margin: 10px auto 0;
width: 260px;
}
</style>
</head>
<body>
<div id="container">
<h1>Fourier Series</h1>
<p>
This interactive sketch visualizes a Fourier series using epicycles.
Each circle represents an odd harmonic; as you add more epicycles with
the slider, the path on the right converges toward a square wave. The
rotating vectors sum to a point whose vertical position is traced over
time.
</p>
<div class="canvas-holder"></div>
</div>
<script>
class dot {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class circle {
constructor(cx, cy, r, a) {
this.cx = cx;
this.cy = cy;
this.r = r;
this.a = a;
}
}
let time = 0;
let wave = [];
let slider;
function setup() {
createCanvas(900, 800);
slider = createSlider(1, 50, 5);
}
function draw() {
background(41);
translate(250, height / 2);
let x = 0;
let y = 0;
for (let i = 0; i < slider.value(); i++) {
let prevx = x;
let prevy = y;
let n = i * 2 + 1;
let radius = 100 * (4 / (n * PI));
x += radius * cos(n * time);
y += radius * sin(n * time);
stroke(255, 100);
noFill();
ellipse(prevx, prevy, radius * 2);
//fill(255);
stroke(255);
line(prevx, prevy, x, y);
//ellipse(x, y, 8);
}
wave.unshift(y);
translate(200, 0);
line(x - 200, y, 0, wave[0]);
beginShape();
noFill();
for (let i = 0; i < wave.length; i++) {
vertex(i, wave[i]);
}
endShape();
time += 0.02;
if (wave.length > 450) {
wave.pop();
}
}
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Fourier Series — Epicycles (p5.js)</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background: #fafafa;
margin: 20px;
text-align: center;
}
#container {
max-width: 900px;
margin: 0 auto;
}
h1,
h2,
p {
margin: 10px 0;
}
input[type='range'] {
display: block;
margin: 10px auto 0;
width: 260px;
}
</style>
</head>
<body>
<div id="container">
<h1>Fourier Series</h1>
<p>
This interactive sketch visualizes a Fourier series using epicycles.
Each circle represents an odd harmonic; as you add more epicycles with
the slider, the path on the right converges toward a square wave. The
rotating vectors sum to a point whose vertical position is traced over
time.
</p>
<div class="canvas-holder"></div>
</div>
<script>
class dot {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class circle {
constructor(cx, cy, r, a) {
this.cx = cx;
this.cy = cy;
this.r = r;
this.a = a;
}
}
let time = 0;
let wave = [];
let slider;
function setup() {
createCanvas(900, 800);
slider = createSlider(1, 50, 5);
}
function draw() {
background(41);
translate(250, height / 2);
let x = 0;
let y = 0;
for (let i = 0; i < slider.value(); i++) {
let prevx = x;
let prevy = y;
let n = i * 2 + 1;
let radius = 100 * (4 / (n * PI));
x += radius * cos(n * time);
y += radius * sin(n * time);
stroke(255, 100);
noFill();
ellipse(prevx, prevy, radius * 2);
//fill(255);
stroke(255);
line(prevx, prevy, x, y);
//ellipse(x, y, 8);
}
wave.unshift(y);
translate(200, 0);
line(x - 200, y, 0, wave[0]);
beginShape();
noFill();
for (let i = 0; i < wave.length; i++) {
vertex(i, wave[i]);
}
endShape();
time += 0.02;
if (wave.length > 450) {
wave.pop();
}
}
</script>
</body>
</html>

View File

@@ -1,247 +1,247 @@
<!doctype html>
<html>
<head>
<meta
name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style>
body {
font-family:
system-ui,
-apple-system,
Segoe UI,
Roboto,
Helvetica,
Arial,
sans-serif;
margin: 16px;
background: #fafafa;
color: #222;
}
.intro {
max-width: 900px;
margin: 0 auto 18px;
padding: 0 12px;
}
.intro h1 {
font-size: 1.5rem;
margin: 0 0 6px;
}
.intro p {
margin: 4px 0 10px;
line-height: 1.45;
}
canvas {
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="intro">
<h1>Marching Squares Field Visualizer</h1>
<p>
This sketch uses the <strong>Marching Squares</strong> algorithm to
generate and display contour boundaries on a grid. Each cell is either
active or inactive, the algorithm determines the correct line or
triangle to draw based on the surrounding corners.
</p>
<p><b>Left-click</b> toggles a cell between the two states.</p>
<p>
<b>Rightclick</b> shows or hides the contour lines produced by the
algorithm.
</p>
</div>
<script>
var reso = 50;
var cols, rows;
var field = [];
var draw_lines = true;
function setup() {
createCanvas(800, 600);
cols = width / reso + 1;
rows = height / reso + 1;
for (var i = 0; i < cols * rows; i++) field[i] = Math.floor(random(2));
}
function draw() {
background(100);
const cBlue = color(55, 120, 232);
const cGreen = color(146, 232, 55);
for (var i = 0; i < cols * rows; i++) {
let x = i % cols;
let y = Math.floor(i / cols);
let c = field[i] ? cBlue : cGreen;
fill(c);
rect(x * reso - reso * 0.5, y * reso - reso * 0.5, reso, reso);
}
for (var i = 0; i < cols * rows; i++) {
if (i + cols + 1 > cols * rows) {
break;
}
var x = (i % cols) * reso;
var y = Math.floor(i / cols) * reso;
const a = [x + reso * 0.5, y];
const b = [x + reso, y + reso * 0.5];
const c = [x + reso * 0.5, y + reso];
const d = [x, y + reso * 0.5];
var s = getState(
field[i],
field[i + 1],
field[i + cols + 1],
field[i + cols],
);
noStroke();
switch (s) {
case 1:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, c[0], c[1], d[0], d[1]);
break;
case 14:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, c[0], c[1], d[0], d[1]);
break;
case 2:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, b[0], b[1], c[0], c[1]);
break;
case 13:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, b[0], b[1], c[0], c[1]);
break;
case 3:
fill(cBlue);
rect(d[0], d[1], reso, reso * 0.5);
break;
case 12:
fill(cGreen);
rect(d[0], d[1], reso, reso * 0.5);
break;
case 4:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], b[0], b[1]);
break;
case 11:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], b[0], b[1]);
break;
case 5:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], d[0], d[1]);
triangle(x + reso * 0.5, y + reso * 0.5, b[0], b[1], c[0], c[1]);
break;
case 6:
fill(cBlue);
rect(a[0], a[1], reso * 0.5, reso);
break;
case 9:
fill(cGreen);
rect(a[0], a[1], reso * 0.5, reso);
break;
case 7:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], d[0], d[1]);
break;
case 8:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], d[0], d[1]);
break;
case 10:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], b[0], b[1]);
triangle(x + reso * 0.5, y + reso * 0.5, c[0], c[1], d[0], d[1]);
break;
default:
break;
}
if (draw_lines) {
drawLines(s, a, b, c, d);
}
}
}
function getState(a, b, c, d) {
return d * 1 + c * 2 + b * 4 + a * 8;
}
function drawLines(s, a, b, c, d) {
stroke(232, 229, 55);
strokeWeight(reso * 0.1);
switch (s) {
case 1:
case 14:
line(c[0], c[1], d[0], d[1]);
break;
case 2:
case 13:
line(b[0], b[1], c[0], c[1]);
break;
case 3:
case 12:
line(b[0], b[1], d[0], d[1]);
break;
case 4:
case 11:
line(a[0], a[1], b[0], b[1]);
break;
case 5:
line(a[0], a[1], d[0], d[1]);
line(b[0], b[1], c[0], c[1]);
break;
case 6:
case 9:
line(a[0], a[1], c[0], c[1]);
break;
case 7:
case 8:
line(a[0], a[1], d[0], d[1]);
break;
case 10:
line(a[0], a[1], b[0], b[1]);
line(c[0], c[1], d[0], d[1]);
break;
default:
break;
}
noStroke();
}
function mousePressed() {
if (mouseButton == LEFT) {
var x = Math.floor((mouseX + 0.5 * reso) / reso);
var y = Math.floor((mouseY + 0.5 * reso) / reso);
var i = Math.floor(y * cols + x);
field[i] = (field[i] + 1) % 2;
} else if (mouseButton == RIGHT) {
draw_lines = !draw_lines;
}
}
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta
name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style>
body {
font-family:
system-ui,
-apple-system,
Segoe UI,
Roboto,
Helvetica,
Arial,
sans-serif;
margin: 16px;
background: #fafafa;
color: #222;
}
.intro {
max-width: 900px;
margin: 0 auto 18px;
padding: 0 12px;
}
.intro h1 {
font-size: 1.5rem;
margin: 0 0 6px;
}
.intro p {
margin: 4px 0 10px;
line-height: 1.45;
}
canvas {
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="intro">
<h1>Marching Squares Field Visualizer</h1>
<p>
This sketch uses the <strong>Marching Squares</strong> algorithm to
generate and display contour boundaries on a grid. Each cell is either
active or inactive, the algorithm determines the correct line or
triangle to draw based on the surrounding corners.
</p>
<p><b>Left-click</b> toggles a cell between the two states.</p>
<p>
<b>Rightclick</b> shows or hides the contour lines produced by the
algorithm.
</p>
</div>
<script>
var reso = 50;
var cols, rows;
var field = [];
var draw_lines = true;
function setup() {
createCanvas(800, 600);
cols = width / reso + 1;
rows = height / reso + 1;
for (var i = 0; i < cols * rows; i++) field[i] = Math.floor(random(2));
}
function draw() {
background(100);
const cBlue = color(55, 120, 232);
const cGreen = color(146, 232, 55);
for (var i = 0; i < cols * rows; i++) {
let x = i % cols;
let y = Math.floor(i / cols);
let c = field[i] ? cBlue : cGreen;
fill(c);
rect(x * reso - reso * 0.5, y * reso - reso * 0.5, reso, reso);
}
for (var i = 0; i < cols * rows; i++) {
if (i + cols + 1 > cols * rows) {
break;
}
var x = (i % cols) * reso;
var y = Math.floor(i / cols) * reso;
const a = [x + reso * 0.5, y];
const b = [x + reso, y + reso * 0.5];
const c = [x + reso * 0.5, y + reso];
const d = [x, y + reso * 0.5];
var s = getState(
field[i],
field[i + 1],
field[i + cols + 1],
field[i + cols]
);
noStroke();
switch (s) {
case 1:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, c[0], c[1], d[0], d[1]);
break;
case 14:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, c[0], c[1], d[0], d[1]);
break;
case 2:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, b[0], b[1], c[0], c[1]);
break;
case 13:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, b[0], b[1], c[0], c[1]);
break;
case 3:
fill(cBlue);
rect(d[0], d[1], reso, reso * 0.5);
break;
case 12:
fill(cGreen);
rect(d[0], d[1], reso, reso * 0.5);
break;
case 4:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], b[0], b[1]);
break;
case 11:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], b[0], b[1]);
break;
case 5:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], d[0], d[1]);
triangle(x + reso * 0.5, y + reso * 0.5, b[0], b[1], c[0], c[1]);
break;
case 6:
fill(cBlue);
rect(a[0], a[1], reso * 0.5, reso);
break;
case 9:
fill(cGreen);
rect(a[0], a[1], reso * 0.5, reso);
break;
case 7:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], d[0], d[1]);
break;
case 8:
fill(cGreen);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], d[0], d[1]);
break;
case 10:
fill(cBlue);
triangle(x + reso * 0.5, y + reso * 0.5, a[0], a[1], b[0], b[1]);
triangle(x + reso * 0.5, y + reso * 0.5, c[0], c[1], d[0], d[1]);
break;
default:
break;
}
if (draw_lines) {
drawLines(s, a, b, c, d);
}
}
}
function getState(a, b, c, d) {
return d * 1 + c * 2 + b * 4 + a * 8;
}
function drawLines(s, a, b, c, d) {
stroke(232, 229, 55);
strokeWeight(reso * 0.1);
switch (s) {
case 1:
case 14:
line(c[0], c[1], d[0], d[1]);
break;
case 2:
case 13:
line(b[0], b[1], c[0], c[1]);
break;
case 3:
case 12:
line(b[0], b[1], d[0], d[1]);
break;
case 4:
case 11:
line(a[0], a[1], b[0], b[1]);
break;
case 5:
line(a[0], a[1], d[0], d[1]);
line(b[0], b[1], c[0], c[1]);
break;
case 6:
case 9:
line(a[0], a[1], c[0], c[1]);
break;
case 7:
case 8:
line(a[0], a[1], d[0], d[1]);
break;
case 10:
line(a[0], a[1], b[0], b[1]);
line(c[0], c[1], d[0], d[1]);
break;
default:
break;
}
noStroke();
}
function mousePressed() {
if (mouseButton == LEFT) {
var x = Math.floor((mouseX + 0.5 * reso) / reso);
var y = Math.floor((mouseY + 0.5 * reso) / reso);
var i = Math.floor(y * cols + x);
field[i] = (field[i] + 1) % 2;
} else if (mouseButton == RIGHT) {
draw_lines = !draw_lines;
}
}
</script>
</body>
</html>

View File

@@ -4,13 +4,13 @@ function Cell(i, j) {
this.walls = [true, true, true, true];
this.visited = false;
this.checkNeighbors = function() {
this.checkNeighbors = function () {
var neighbors = [];
var top = grid[index(i, j -1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
var top = grid[index(i, j - 1)];
var right = grid[index(i + 1, j)];
var bottom = grid[index(i, j + 1)];
var left = grid[index(i - 1, j)];
if (top && !top.visited) {
neighbors.push(top);
@@ -31,32 +31,30 @@ function Cell(i, j) {
} else {
return undefined;
}
}
this.highlight = function() {
var x = this.i*w;
var y = this.j*w;
};
this.highlight = function () {
var x = this.i * w;
var y = this.j * w;
noStroke();
fill(252, 106, 2, 100);
rect(x, y, w, w);
}
};
this.show = function() {
var x = this.i*w;
var y = this.j*w;
this.show = function () {
var x = this.i * w;
var y = this.j * w;
stroke(0);
if (this.walls[0]) {
line(x , y , x + w, y);
line(x, y, x + w, y);
}
if (this.walls[1]) {
line(x + w, y , x + w, y + w);
line(x + w, y, x + w, y + w);
}
if (this.walls[2]) {
line(x + w, y + w, x , y + w);
line(x + w, y + w, x, y + w);
}
if (this.walls[3]) {
line(x , y + w, x , y);
line(x, y + w, x, y);
}
if (this.visited) {
@@ -64,5 +62,5 @@ function Cell(i, j) {
fill(242, 255, 28, 125);
rect(x, y, w, w);
}
}
};
}

View File

@@ -4,134 +4,129 @@ var grid = [];
var current;
var stack = [];
function setup(){
createCanvas(800, 800);
cols = floor(width/w)
rows = floor(height/w)
//frameRate(6);
function setup() {
createCanvas(800, 800);
cols = floor(width / w);
rows = floor(height / w);
//frameRate(6);
for (var j = 0; j < rows; j++){
for (var i = 0; i < cols; i++){
var cell = new Cell(i,j);
grid.push(cell);
}
for (var j = 0; j < rows; j++) {
for (var i = 0; i < cols; i++) {
var cell = new Cell(i, j);
grid.push(cell);
}
current = grid[0];
}
current = grid[0];
}
function draw(){
background(150);
for (var i = 0; i < grid.length; i++){
grid[i].show();
function draw() {
background(150);
for (var i = 0; i < grid.length; i++) {
grid[i].show();
}
current.visited = true;
current.highlight();
var next = current.checkNeighbors();
if (next) {
next.visited = true;
stack.push(current);
}
current.visited = true;
current.highlight();
var next = current.checkNeighbors();
if (next) {
next.visited = true;
stack.push(current);
removeWalls(current, next);
current = next;
} else if (stack.length > 0){
current = stack.pop();
}
removeWalls(current, next);
current = next;
} else if (stack.length > 0) {
current = stack.pop();
}
}
function index(i, j) {
if (i < 0 || j < 0 || i > cols - 1 || j > rows - 1) {
return -1;
}
function index(i, j){
if(i < 0 || j < 0 || i > cols-1 || j > rows-1){
return -1
}
return i + j * cols;
return i + j * cols;
}
function cell(i, j){
this.i = i;
this.j = j;
this.walls = [true, true, true, true];
this.visited = false;
function cell(i, j) {
this.i = i;
this.j = j;
this.walls = [true, true, true, true];
this.visited = false;
this.checkNeighbors = function(){
var neighbors = [];
var top = grid[index(i, j-1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j + 1)];
var left = grid[index(i-1, j)];
this.checkNeighbors = function () {
var neighbors = [];
if (top && !top.visited){
neighbors.push(top);
}
if (right && !right.visited){
neighbors.push(right);
}
if (bottom && !bottom.visited){
neighbors.push(bottom);
}
if (left && !left.visited){
neighbors.push(left);
}
if (neighbors.length > 0){
var r = floor(random(0, neighbors.length));
return neighbor[r];
} else {
return undefined;
}
this.highlight = function(){
var x = this.i*w;
var y = this.j*w;
noStroke();
fill(0, 0, 255, 125);
rect(x,y,w,w);
}
}
var top = grid[index(i, j - 1)];
var right = grid[index(i + 1, j)];
var bottom = grid[index(i, j + 1)];
var left = grid[index(i - 1, j)];
this.show = function(){
var x = this.i*w;
var y = this.j*w;
stroke(255);
if (this.walls[0]){
line(x,y,x+w,y);
}
if (this.walls[1]){
line(x+w,y,x+w,y+w);
}
if (this.walls[2]){
line(x+w,y+w,x,y+w);
}
if (this.walls[3]){
line(x,y+w,x,y);
}
if (this.visited){
fill(255, 0, 255, 125);
rect(x,y,w,w);
}
}
if (top && !top.visited) {
neighbors.push(top);
}
if (right && !right.visited) {
neighbors.push(right);
}
if (bottom && !bottom.visited) {
neighbors.push(bottom);
}
if (left && !left.visited) {
neighbors.push(left);
}
if (neighbors.length > 0) {
var r = floor(random(0, neighbors.length));
return neighbor[r];
} else {
return undefined;
}
this.highlight = function () {
var x = this.i * w;
var y = this.j * w;
noStroke();
fill(0, 0, 255, 125);
rect(x, y, w, w);
};
};
this.show = function () {
var x = this.i * w;
var y = this.j * w;
stroke(255);
if (this.walls[0]) {
line(x, y, x + w, y);
}
if (this.walls[1]) {
line(x + w, y, x + w, y + w);
}
if (this.walls[2]) {
line(x + w, y + w, x, y + w);
}
if (this.walls[3]) {
line(x, y + w, x, y);
}
if (this.visited) {
fill(255, 0, 255, 125);
rect(x, y, w, w);
}
};
}
function removeWalls(a, b){
var x = a.i - b.i;
if (x === 1){
a.walls[3] = false;
b.walls[1] = false;
} else if (x === -1){
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1){
a.walls[0] = false;
b.walls[2] = false;
} else if (y === -1){
a.walls[2] = false;
b.walls[0] = false;
}
function removeWalls(a, b) {
var x = a.i - b.i;
if (x === 1) {
a.walls[3] = false;
b.walls[1] = false;
} else if (x === -1) {
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1) {
a.walls[0] = false;
b.walls[2] = false;
} else if (y === -1) {
a.walls[2] = false;
b.walls[0] = false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,73 +6,81 @@ const fullscreenButton = document.getElementById('fullscreenButton');
outputElement.value = '';
canvasElement.addEventListener('webglcontextlost', (event) => {
canvasElement.addEventListener(
'webglcontextlost',
(event) => {
event.preventDefault();
setStatus('WebGL context lost. Reload the page to restart the simulation.');
}, false);
},
false
);
function setStatus(text) {
if (!setStatus.last) {
setStatus.last = { time: Date.now(), text: '' };
}
if (text === setStatus.last.text) {
return;
}
const match = text && text.match(/([^(]+)\((\d+(?:\.\d+)?)\/(\d+)\)/);
const now = Date.now();
if (match && now - setStatus.last.time < 30) {
return;
}
setStatus.last.time = now;
setStatus.last.text = text;
if (match) {
statusElement.textContent = match[1].trim();
progressElement.value = Number.parseInt(match[2], 10) * 100;
progressElement.max = Number.parseInt(match[3], 10) * 100;
progressElement.hidden = false;
} else {
statusElement.textContent = text || '';
progressElement.hidden = !text;
if (!text) {
progressElement.removeAttribute('value');
}
if (!setStatus.last) {
setStatus.last = { time: Date.now(), text: '' };
}
if (text === setStatus.last.text) {
return;
}
const match = text && text.match(/([^(]+)\((\d+(?:\.\d+)?)\/(\d+)\)/);
const now = Date.now();
if (match && now - setStatus.last.time < 30) {
return;
}
setStatus.last.time = now;
setStatus.last.text = text;
if (match) {
statusElement.textContent = match[1].trim();
progressElement.value = Number.parseInt(match[2], 10) * 100;
progressElement.max = Number.parseInt(match[3], 10) * 100;
progressElement.hidden = false;
} else {
statusElement.textContent = text || '';
progressElement.hidden = !text;
if (!text) {
progressElement.removeAttribute('value');
}
}
}
var Module = {
canvas: canvasElement,
print: (...args) => {
console.log(...args);
outputElement.value += `${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
},
printErr: (...args) => {
console.error(...args);
outputElement.value += `[err] ${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
},
setStatus,
totalDependencies: 0,
monitorRunDependencies(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
setStatus(left ? `Preparing... (${this.totalDependencies - left}/${this.totalDependencies})` : 'Running...');
if (!left) {
setTimeout(() => setStatus(''), 250);
}
canvas: canvasElement,
print: (...args) => {
console.log(...args);
outputElement.value += `${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
},
printErr: (...args) => {
console.error(...args);
outputElement.value += `[err] ${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
},
setStatus,
totalDependencies: 0,
monitorRunDependencies(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
setStatus(
left
? `Preparing... (${this.totalDependencies - left}/${this.totalDependencies})`
: 'Running...'
);
if (!left) {
setTimeout(() => setStatus(''), 250);
}
},
};
fullscreenButton.addEventListener('click', () => {
if (typeof Module.requestFullscreen === 'function') {
Module.requestFullscreen(false, false);
}
if (typeof Module.requestFullscreen === 'function') {
Module.requestFullscreen(false, false);
}
});
globalThis.onerror = () => {
setStatus('Exception thrown, see JavaScript console');
};
setStatus('Exception thrown, see JavaScript console');
};

View File

@@ -162,7 +162,7 @@ textarea {
margin-top: 10px;
padding: 10px;
border: 1px solid #ddd;
font-family: "Courier New", monospace;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
resize: vertical;
background: white;

View File

@@ -44,7 +44,7 @@
var circleCount, total;
var x, y;
var calcPI;
var DisplayPI = "a";
var DisplayPI = 'a';
const PI = 3.1415926535;
var BestPI = 50;
@@ -84,15 +84,15 @@
DisplayPI = calcPI;
BestPI = calcPI;
var PIDisplay = document.getElementById("PIbox");
var PIDisplay = document.getElementById('PIbox');
PIDisplay.innerHTML = DisplayPI;
var ErrorDisplay = document.getElementById("percentage");
var DisplayError = "a";
var ErrorDisplay = document.getElementById('percentage');
var DisplayError = 'a';
var Perror = (calcPI - PI) / PI;
Perror *= 100;
DisplayError = nfc(Perror, 10);
ErrorDisplay.innerHTML = DisplayError + "%";
ErrorDisplay.innerHTML = DisplayError + '%';
}
}
</script>

View File

@@ -7,83 +7,94 @@ const resizeCheckbox = document.getElementById('resize');
const pointerLockCheckbox = document.getElementById('pointerLock');
if (outputElement) {
outputElement.value = '';
outputElement.value = '';
}
canvasElement.addEventListener('webglcontextlost', (event) => {
canvasElement.addEventListener(
'webglcontextlost',
(event) => {
event.preventDefault();
setStatus('WebGL context lost. Reload the page to restart the game.');
}, false);
},
false
);
function setStatus(text) {
if (!setStatus.last) {
setStatus.last = { time: Date.now(), text: '' };
}
if (text === setStatus.last.text) {
return;
}
const match = text?.match(/([^(]+)\((\d+(?:\.\d+)?)\/(\d+)\)/);
const now = Date.now();
if (match && now - setStatus.last.time < 30) {
return;
}
setStatus.last.time = now;
setStatus.last.text = text;
if (match) {
statusElement.textContent = match[1].trim();
progressElement.value = Number.parseInt(match[2], 10) * 100;
progressElement.max = Number.parseInt(match[3], 10) * 100;
progressElement.hidden = false;
} else {
statusElement.textContent = text || '';
progressElement.hidden = !text;
if (!text) {
progressElement.removeAttribute('value');
}
if (!setStatus.last) {
setStatus.last = { time: Date.now(), text: '' };
}
if (text === setStatus.last.text) {
return;
}
const match = text?.match(/([^(]+)\((\d+(?:\.\d+)?)\/(\d+)\)/);
const now = Date.now();
if (match && now - setStatus.last.time < 30) {
return;
}
setStatus.last.time = now;
setStatus.last.text = text;
if (match) {
statusElement.textContent = match[1].trim();
progressElement.value = Number.parseInt(match[2], 10) * 100;
progressElement.max = Number.parseInt(match[3], 10) * 100;
progressElement.hidden = false;
} else {
statusElement.textContent = text || '';
progressElement.hidden = !text;
if (!text) {
progressElement.removeAttribute('value');
}
}
}
globalThis.Module = globalThis.Module || {};
globalThis.Module.canvas = canvasElement;
globalThis.Module.print = (...args) => {
console.log(...args);
if (!outputElement) {
return;
}
outputElement.value += `${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
console.log(...args);
if (!outputElement) {
return;
}
outputElement.value += `${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
};
globalThis.Module.printErr = (...args) => {
console.error(...args);
if (!outputElement) {
return;
}
outputElement.value += `[err] ${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
console.error(...args);
if (!outputElement) {
return;
}
outputElement.value += `[err] ${args.join(' ')}\n`;
outputElement.scrollTop = outputElement.scrollHeight;
};
globalThis.Module.setStatus = setStatus;
globalThis.Module.totalDependencies = 0;
globalThis.Module.monitorRunDependencies = function (left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
setStatus(left ? `Preparing... (${this.totalDependencies - left}/${this.totalDependencies})` : 'Running...');
if (!left) {
setTimeout(() => setStatus(''), 250);
}
this.totalDependencies = Math.max(this.totalDependencies, left);
setStatus(
left
? `Preparing... (${this.totalDependencies - left}/${this.totalDependencies})`
: 'Running...'
);
if (!left) {
setTimeout(() => setStatus(''), 250);
}
};
fullscreenButton.addEventListener('click', () => {
if (typeof globalThis.Module.requestFullscreen === 'function') {
globalThis.Module.requestFullscreen(pointerLockCheckbox.checked, resizeCheckbox.checked);
}
if (typeof globalThis.Module.requestFullscreen === 'function') {
globalThis.Module.requestFullscreen(
pointerLockCheckbox.checked,
resizeCheckbox.checked
);
}
});
setStatus('Downloading...');
globalThis.onerror = () => {
setStatus('Exception thrown, see JavaScript console');
setStatus('Exception thrown, see JavaScript console');
};

View File

@@ -1,41 +1,41 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg: #fffdf7;
--text: #0f172a;
--muted: #475569;
--border: #0f172a;
--panel: #dff4ff;
--panel-2: #ffffff;
--accent: #00a6fb;
--accent-strong: #0077b6;
--highlight: #ffd23f;
--danger: #ff5d73;
--bg: #fffdf7;
--text: #0f172a;
--muted: #475569;
--border: #0f172a;
--panel: #dff4ff;
--panel-2: #ffffff;
--accent: #00a6fb;
--accent-strong: #0077b6;
--highlight: #ffd23f;
--danger: #ff5d73;
}
body {
background: var(--bg);
color: var(--text);
font-family: Arial, Helvetica, sans-serif;
font-size: 18px;
line-height: 1.6;
padding: 20px;
background: var(--bg);
color: var(--text);
font-family: Arial, Helvetica, sans-serif;
font-size: 18px;
line-height: 1.6;
padding: 20px;
}
.container {
max-width: 920px;
margin: 0 auto;
max-width: 920px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
border-bottom: 3px solid var(--border);
padding-bottom: 20px;
text-align: center;
margin-bottom: 30px;
border-bottom: 3px solid var(--border);
padding-bottom: 20px;
}
h1,
@@ -44,216 +44,216 @@ h3,
button,
summary,
.back-button {
font-family: "Arial Black", Arial, Helvetica, sans-serif;
font-weight: 900;
letter-spacing: 0.02em;
font-family: 'Arial Black', Arial, Helvetica, sans-serif;
font-weight: 900;
letter-spacing: 0.02em;
}
h1 {
font-size: clamp(2.4rem, 5vw, 3.6rem);
margin-bottom: 10px;
line-height: 1;
text-transform: uppercase;
font-size: clamp(2.4rem, 5vw, 3.6rem);
margin-bottom: 10px;
line-height: 1;
text-transform: uppercase;
}
h2 {
font-size: 1.8rem;
margin: 0 0 16px;
font-size: 1.8rem;
margin: 0 0 16px;
}
h3 {
font-size: 1.35rem;
margin: 0 0 14px;
font-size: 1.35rem;
margin: 0 0 14px;
}
p,
ul {
margin-bottom: 16px;
margin-bottom: 16px;
}
.subtitle {
color: var(--accent-strong);
font-size: 1rem;
color: var(--accent-strong);
font-size: 1rem;
}
.back-button {
display: inline-block;
margin-bottom: 20px;
padding: 10px 15px;
background: var(--highlight);
color: var(--text);
text-decoration: none;
border: 3px solid var(--border);
display: inline-block;
margin-bottom: 20px;
padding: 10px 15px;
background: var(--highlight);
color: var(--text);
text-decoration: none;
border: 3px solid var(--border);
}
.back-button:hover,
.action-button:hover {
background: var(--text);
color: #fff;
background: var(--text);
color: #fff;
}
.info-box {
background: var(--highlight);
border: 3px solid var(--border);
padding: 16px;
margin: 20px 0;
background: var(--highlight);
border: 3px solid var(--border);
padding: 16px;
margin: 20px 0;
}
.canvas-container {
background: var(--panel);
border: 3px solid var(--border);
padding: 20px;
margin: 30px 0;
background: var(--panel);
border: 3px solid var(--border);
padding: 20px;
margin: 30px 0;
}
.canvas-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 15px;
flex-wrap: wrap;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.canvas-toolbar p {
margin: 0;
color: var(--muted);
font-size: 0.95rem;
text-transform: uppercase;
margin: 0;
color: var(--muted);
font-size: 0.95rem;
text-transform: uppercase;
}
.action-button {
padding: 10px 14px;
background: var(--danger);
color: white;
border: 3px solid var(--border);
cursor: pointer;
font: inherit;
padding: 10px 14px;
background: var(--danger);
color: white;
border: 3px solid var(--border);
cursor: pointer;
font: inherit;
}
.canvas-shell {
display: flex;
justify-content: center;
align-items: center;
min-height: 520px;
overflow: auto;
background: var(--panel-2);
border: 3px solid var(--border);
padding: 12px;
display: flex;
justify-content: center;
align-items: center;
min-height: 520px;
overflow: auto;
background: var(--panel-2);
border: 3px solid var(--border);
padding: 12px;
}
#canvas {
display: block;
margin: 0 auto;
max-width: 100%;
height: auto;
background: #000;
outline: none;
image-rendering: pixelated;
display: block;
margin: 0 auto;
max-width: 100%;
height: auto;
background: #000;
outline: none;
image-rendering: pixelated;
}
.canvas-options {
display: flex;
flex-wrap: wrap;
gap: 14px;
margin-top: 15px;
font-size: 0.95rem;
color: var(--muted);
display: flex;
flex-wrap: wrap;
gap: 14px;
margin-top: 15px;
font-size: 0.95rem;
color: var(--muted);
}
.canvas-options label {
display: inline-flex;
align-items: center;
gap: 8px;
display: inline-flex;
align-items: center;
gap: 8px;
}
.canvas-options input {
accent-color: var(--accent-strong);
accent-color: var(--accent-strong);
}
.status {
margin-top: 15px;
min-height: 24px;
color: var(--accent-strong);
font-weight: 700;
margin-top: 15px;
min-height: 24px;
color: var(--accent-strong);
font-weight: 700;
}
progress {
width: 100%;
height: 18px;
margin-top: 10px;
border: 3px solid var(--border);
width: 100%;
height: 18px;
margin-top: 10px;
border: 3px solid var(--border);
}
progress[hidden] {
display: none;
display: none;
}
details {
margin-top: 15px;
margin-top: 15px;
}
summary {
cursor: pointer;
cursor: pointer;
}
.feature-list {
padding-left: 24px;
padding-left: 24px;
}
.feature-list li {
margin-bottom: 10px;
margin-bottom: 10px;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
textarea {
width: 100%;
min-height: 120px;
margin-top: 10px;
padding: 10px;
border: 3px solid var(--border);
font-family: "Courier New", monospace;
font-size: 0.9rem;
resize: vertical;
background: white;
color: var(--text);
width: 100%;
min-height: 120px;
margin-top: 10px;
padding: 10px;
border: 3px solid var(--border);
font-family: 'Courier New', monospace;
font-size: 0.9rem;
resize: vertical;
background: white;
color: var(--text);
}
footer {
text-align: center;
margin-top: 40px;
padding-top: 20px;
border-top: 3px solid var(--border);
font-size: 0.9rem;
color: var(--muted);
text-align: center;
margin-top: 40px;
padding-top: 20px;
border-top: 3px solid var(--border);
font-size: 0.9rem;
color: var(--muted);
}
@media (max-width: 768px) {
body {
padding: 16px;
font-size: 16px;
}
body {
padding: 16px;
font-size: 16px;
}
h1 {
font-size: 2.2rem;
}
h1 {
font-size: 2.2rem;
}
.canvas-container {
padding: 15px;
}
.canvas-container {
padding: 15px;
}
.canvas-shell {
min-height: 360px;
}
.canvas-shell {
min-height: 360px;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@ export function setupCanvas(gl, canvas) {
return {
canvas,
gl,
aspectRatio: canvas.width / canvas.height
aspectRatio: canvas.width / canvas.height,
};
}
@@ -15,5 +15,9 @@ export function resizeCanvas(gl, canvas, program) {
canvas.height = canvas.clientHeight * window.devicePixelRatio;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.useProgram(program);
gl.uniform2f(gl.getUniformLocation(program, "u_resolution"), canvas.width, canvas.height);
gl.uniform2f(
gl.getUniformLocation(program, 'u_resolution'),
canvas.width,
canvas.height
);
}

View File

@@ -1,8 +1,8 @@
import { initWasm } from "./wasm.js";
import { setupCanvas, resizeCanvas } from "./canvas.js";
import { createShaderProgram } from "./shaders.js";
import { draw } from "./tsp-gl.js";
import { initPoints } from "./utils.js";
import { initWasm } from './wasm.js';
import { setupCanvas, resizeCanvas } from './canvas.js';
import { createShaderProgram } from './shaders.js';
import { draw } from './tsp-gl.js';
import { initPoints } from './utils.js';
const DEFAULT_POINT_COUNT = 36;
const MIN_POINTS = 4;
@@ -57,28 +57,31 @@ function createLineFragmentShader() {
}
async function run() {
const canvas = document.getElementById("tsp-canvas");
const pointCountInput = document.getElementById("pointCountInput");
const applyPointCountButton = document.getElementById("applyPointCountButton");
const pointCountStatus = document.getElementById("pointCountStatus");
const canvas = document.getElementById('tsp-canvas');
const pointCountInput = document.getElementById('pointCountInput');
const applyPointCountButton = document.getElementById(
'applyPointCountButton'
);
const pointCountStatus = document.getElementById('pointCountStatus');
if (!canvas || !pointCountInput || !applyPointCountButton || !pointCountStatus) {
if (
!canvas ||
!pointCountInput ||
!applyPointCountButton ||
!pointCountStatus
) {
return;
}
const gl = canvas.getContext("webgl2");
const gl = canvas.getContext('webgl2');
if (!gl) {
pointCountStatus.textContent = "WebGL2 unavailable in this browser.";
pointCountStatus.textContent = 'WebGL2 unavailable in this browser.';
return;
}
gl.clearColor(0.1, 0.1, 0.1, 1);
const {
initialisePoints,
getPointOrder,
memory
} = await initWasm();
const { initialisePoints, getPointOrder, memory } = await initWasm();
const vertexCode = `
attribute vec2 a_position;
@@ -93,7 +96,7 @@ async function run() {
const lineBuffer = gl.createBuffer();
if (!positionBuffer || !lineBuffer) {
pointCountStatus.textContent = "Unable to initialise WebGL buffers.";
pointCountStatus.textContent = 'Unable to initialise WebGL buffers.';
return;
}
@@ -103,20 +106,26 @@ async function run() {
const state = {
scene: null,
draggingIndex: -1
draggingIndex: -1,
};
function updateStatus(pointCount) {
pointCountStatus.textContent = `${pointCount} point${pointCount === 1 ? "" : "s"}`;
pointCountStatus.textContent = `${pointCount} point${pointCount === 1 ? '' : 's'}`;
}
function recalculateRoute(scene) {
const pointOrderPtr = getPointOrder(scene.pointsPtr, scene.pointCount);
scene.pointOrder = Array.from(new Uint32Array(memory.buffer, pointOrderPtr, scene.pointCount));
scene.pointOrder = Array.from(
new Uint32Array(memory.buffer, pointOrderPtr, scene.pointCount)
);
}
function syncPointsToMemory(scene) {
scene.pointArray = new Float32Array(memory.buffer, scene.pointsPtr, scene.pointCount * 2);
scene.pointArray = new Float32Array(
memory.buffer,
scene.pointsPtr,
scene.pointCount * 2
);
for (let i = 0; i < scene.points.length; i++) {
scene.pointArray[i * 2] = scene.points[i].cx;
@@ -133,10 +142,22 @@ async function run() {
}
const pointsPtr = initialisePoints(pointCount, Date.now());
const pointArray = new Float32Array(memory.buffer, pointsPtr, pointCount * 2);
const pointArray = new Float32Array(
memory.buffer,
pointsPtr,
pointCount * 2
);
const points = initPoints(pointArray);
const program = createShaderProgram(gl, vertexCode, createPointFragmentShader(pointCount));
const lineProgram = createShaderProgram(gl, vertexCode, createLineFragmentShader());
const program = createShaderProgram(
gl,
vertexCode,
createPointFragmentShader(pointCount)
);
const lineProgram = createShaderProgram(
gl,
vertexCode,
createLineFragmentShader()
);
state.scene = {
pointCount,
@@ -145,7 +166,7 @@ async function run() {
points,
pointOrder: [],
program,
lineProgram
lineProgram,
};
recalculateRoute(state.scene);
@@ -154,7 +175,7 @@ async function run() {
pointCountInput.value = String(pointCount);
}
canvas.addEventListener("mousedown", (e) => {
canvas.addEventListener('mousedown', (e) => {
if (!state.scene) {
return;
}
@@ -165,8 +186,8 @@ async function run() {
for (let i = 0; i < state.scene.points.length; i++) {
const point = state.scene.points[i];
const dx = (0.5 * x + 0.5) - point.cx;
const dy = (0.5 * y + 0.5) - point.cy;
const dx = 0.5 * x + 0.5 - point.cx;
const dy = 0.5 * y + 0.5 - point.cy;
const distance = Math.hypot(dx, dy);
if (distance <= RADIUS_SIZE) {
@@ -176,14 +197,16 @@ async function run() {
}
});
canvas.addEventListener("mousemove", (e) => {
canvas.addEventListener('mousemove', (e) => {
if (!state.scene || state.draggingIndex === -1) {
return;
}
const rect = canvas.getBoundingClientRect();
state.scene.points[state.draggingIndex].cx = (e.clientX - rect.left) / rect.width;
state.scene.points[state.draggingIndex].cy = 1 - (e.clientY - rect.top) / rect.height;
state.scene.points[state.draggingIndex].cx =
(e.clientX - rect.left) / rect.width;
state.scene.points[state.draggingIndex].cy =
1 - (e.clientY - rect.top) / rect.height;
syncPointsToMemory(state.scene);
recalculateRoute(state.scene);
@@ -193,26 +216,30 @@ async function run() {
state.draggingIndex = -1;
}
canvas.addEventListener("mouseup", stopDragging);
canvas.addEventListener("mouseleave", stopDragging);
canvas.addEventListener('mouseup', stopDragging);
canvas.addEventListener('mouseleave', stopDragging);
function applyPointCount() {
const nextPointCount = clampPointCount(Number.parseInt(pointCountInput.value, 10));
const nextPointCount = clampPointCount(
Number.parseInt(pointCountInput.value, 10)
);
initialiseScene(nextPointCount);
}
applyPointCountButton.addEventListener("click", applyPointCount);
pointCountInput.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
applyPointCountButton.addEventListener('click', applyPointCount);
pointCountInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
applyPointCount();
}
});
pointCountInput.addEventListener("blur", () => {
pointCountInput.value = String(clampPointCount(Number.parseInt(pointCountInput.value, 10)));
pointCountInput.addEventListener('blur', () => {
pointCountInput.value = String(
clampPointCount(Number.parseInt(pointCountInput.value, 10))
);
});
window.addEventListener("resize", () => {
window.addEventListener('resize', () => {
if (!state.scene) {
return;
}

View File

@@ -17,4 +17,3 @@ export function createShaderProgram(gl, vertexCode, fragCode) {
return program;
}

View File

@@ -1,7 +1,16 @@
// tsp-gl.js
import { updateLines } from "./utils.js";
import { updateLines } from './utils.js';
export function draw(gl, points, program, lineProgram, positionBuffer, lineBuffer, pointOrder, aspectRatio) {
export function draw(
gl,
points,
program,
lineProgram,
positionBuffer,
lineBuffer,
pointOrder,
aspectRatio
) {
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw Circles
@@ -22,10 +31,10 @@ function updateCircleUniforms(gl, points, program, positionBuffer) {
gl.useProgram(program);
gl.uniform2fv(gl.getUniformLocation(program, "u_centers"), centerArray);
gl.uniform2fv(gl.getUniformLocation(program, 'u_centers'), centerArray);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positionAttribLoc = gl.getAttribLocation(program, "a_position");
const positionAttribLoc = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionAttribLoc);
gl.vertexAttribPointer(positionAttribLoc, 2, gl.FLOAT, false, 0, 0);
@@ -38,7 +47,7 @@ function drawLines(gl, program, buffer, lineVertices) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.DYNAMIC_DRAW);
const positionAttribLoc = gl.getAttribLocation(program, "a_position");
const positionAttribLoc = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionAttribLoc);
gl.vertexAttribPointer(positionAttribLoc, 2, gl.FLOAT, false, 0, 0);

View File

@@ -1,24 +1,27 @@
export async function initWasm() {
let wasmMemory = new WebAssembly.Memory({
initial: 256,
maximum: 256
maximum: 256,
});
let importObject = {
env: {
memory: wasmMemory,
}
},
};
const response = await fetch("./main.wasm");
const wasmModule = await WebAssembly.instantiateStreaming(response, importObject);
const { initialisePoints, getPointOrder, memory } = wasmModule.instance.exports;
const response = await fetch('./main.wasm');
const wasmModule = await WebAssembly.instantiateStreaming(
response,
importObject
);
const { initialisePoints, getPointOrder, memory } =
wasmModule.instance.exports;
return {
initialisePoints,
getPointOrder,
memory,
wasmMemory
}
wasmMemory,
};
}

View File

@@ -1,222 +1,222 @@
<!doctype html>
<html>
<head>
<meta
name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style>
body {
padding: 0;
margin: 0;
font-family:
system-ui,
-apple-system,
Segoe UI,
Roboto,
Helvetica,
Arial,
sans-serif;
background: #fafafa;
}
.intro {
max-width: 900px;
margin: 20px auto 10px;
padding: 0 16px;
}
.intro h1 {
margin: 0 0 6px;
font-size: 1.5rem;
}
.intro p {
margin: 4px 0 14px;
line-height: 1.45;
}
.slidecontainer {
width: 100%;
/* Width of the outside container */
}
/* The slider itself */
.slider {
-webkit-appearance: none;
/* Override default CSS styles */
appearance: none;
width: 100%;
/* Full-width */
height: 25px;
/* Specified height */
background: #d3d3d3;
/* Grey background */
outline: none;
/* Remove outline */
opacity: 1;
/* Set transparency (for mouse-over effects on hover) */
-webkit-transition: 0.2s;
/* 0.2 seconds transition on hover */
transition: opacity 0.2s;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
/* Override default look */
appearance: none;
width: 25px;
/* Set a specific slider handle width */
height: 25px;
/* Slider handle height */
background: #434343;
/* Grey background */
cursor: pointer;
/* Cursor on hover */
}
.slider::-moz-range-thumb {
width: 25px;
/* Set a specific slider handle width */
height: 25px;
/* Slider handle height */
background: #434343;
/* Grey background */
cursor: pointer;
/* Cursor on hover */
}
</style>
</head>
<body>
<div class="intro">
<h1>Oscillating Arrow Field Visualizer</h1>
<p>
A sketch to <s>hopefully</s> demonstrate the illusion of the
<a href="https://en.wikipedia.org/wiki/M%C3%BCller-Lyer_illusion"
>Müller-Lyer Illusion</a
>
</p>
<p>
The lines - that are the same length, should look longer and shorter
based on the angle of the arrow heads.
</p>
<p>
Increasing the value adds more horizontal slices, creating a denser and
more detailed pattern. Tap or click to pause and resume the motion.
</p>
</div>
<div class="slidecontainer">
<input
type="range"
min="2"
max="11"
step="1"
value="2"
class="slider"
id="myRange"
/>
</div>
<script>
let vMargin, hMargin, arrowHeight, t, lineNum;
let lineMove = true;
function setup() {
createCanvas(windowWidth, windowHeight * 0.6);
hMargin = 12;
arrowHeight = 25;
t = 0;
lineNum = 1;
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight * 0.6);
}
function draw() {
background(43);
angleMode(DEGREES);
lineNum = document.getElementById("myRange").value;
drawLine(hMargin, lineNum + 1);
if (lineMove) {
t++;
}
if (t == 360) {
t = 0;
}
}
function drawLine(hm, vm) {
//let changex = map(sin(t), -1, 1, -22, 22);
let changex = sin(t) * 18;
vm /= 10;
for (let j = 1; j < vm - 1; j++) {
stroke(255, 34, 63);
strokeWeight(4);
line(width / hm, (j * height) / vm, width / 2, (j * height) / vm); //red line
stroke(69, 98, 255);
line(
width / 2,
(j * height) / vm,
((hm - 1) * width) / hm,
(j * height) / vm,
); //blue line
stroke(255);
line(
width / hm,
(j * height) / vm,
width / hm + changex,
(j * height) / vm - arrowHeight,
); //left top
line(
width / hm + changex,
(j * height) / vm + arrowHeight,
width / hm,
(j * height) / vm,
); //left bottom
line(
((hm - 1) * width) / hm + changex,
(j * height) / vm + arrowHeight,
((hm - 1) * width) / hm,
(j * height) / vm,
); //right bottom
line(
width / 2 - changex,
(j * height) / vm + arrowHeight,
width / 2,
(j * height) / vm,
); //center bottom
line(
((hm - 1) * width) / hm,
(j * height) / vm,
((hm - 1) * width) / hm + changex,
(j * height) / vm - arrowHeight,
); //right bottom
line(
width / 2,
(j * height) / vm,
width / 2 - changex,
(j * height) / vm - arrowHeight,
); //center top
}
}
function mouseClicked() {
lineMove = !lineMove;
}
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta
name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style>
body {
padding: 0;
margin: 0;
font-family:
system-ui,
-apple-system,
Segoe UI,
Roboto,
Helvetica,
Arial,
sans-serif;
background: #fafafa;
}
.intro {
max-width: 900px;
margin: 20px auto 10px;
padding: 0 16px;
}
.intro h1 {
margin: 0 0 6px;
font-size: 1.5rem;
}
.intro p {
margin: 4px 0 14px;
line-height: 1.45;
}
.slidecontainer {
width: 100%;
/* Width of the outside container */
}
/* The slider itself */
.slider {
-webkit-appearance: none;
/* Override default CSS styles */
appearance: none;
width: 100%;
/* Full-width */
height: 25px;
/* Specified height */
background: #d3d3d3;
/* Grey background */
outline: none;
/* Remove outline */
opacity: 1;
/* Set transparency (for mouse-over effects on hover) */
-webkit-transition: 0.2s;
/* 0.2 seconds transition on hover */
transition: opacity 0.2s;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
/* Override default look */
appearance: none;
width: 25px;
/* Set a specific slider handle width */
height: 25px;
/* Slider handle height */
background: #434343;
/* Grey background */
cursor: pointer;
/* Cursor on hover */
}
.slider::-moz-range-thumb {
width: 25px;
/* Set a specific slider handle width */
height: 25px;
/* Slider handle height */
background: #434343;
/* Grey background */
cursor: pointer;
/* Cursor on hover */
}
</style>
</head>
<body>
<div class="intro">
<h1>Oscillating Arrow Field Visualizer</h1>
<p>
A sketch to <s>hopefully</s> demonstrate the illusion of the
<a href="https://en.wikipedia.org/wiki/M%C3%BCller-Lyer_illusion"
>Müller-Lyer Illusion</a
>
</p>
<p>
The lines - that are the same length, should look longer and shorter
based on the angle of the arrow heads.
</p>
<p>
Increasing the value adds more horizontal slices, creating a denser and
more detailed pattern. Tap or click to pause and resume the motion.
</p>
</div>
<div class="slidecontainer">
<input
type="range"
min="2"
max="11"
step="1"
value="2"
class="slider"
id="myRange"
/>
</div>
<script>
let vMargin, hMargin, arrowHeight, t, lineNum;
let lineMove = true;
function setup() {
createCanvas(windowWidth, windowHeight * 0.6);
hMargin = 12;
arrowHeight = 25;
t = 0;
lineNum = 1;
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight * 0.6);
}
function draw() {
background(43);
angleMode(DEGREES);
lineNum = document.getElementById('myRange').value;
drawLine(hMargin, lineNum + 1);
if (lineMove) {
t++;
}
if (t == 360) {
t = 0;
}
}
function drawLine(hm, vm) {
//let changex = map(sin(t), -1, 1, -22, 22);
let changex = sin(t) * 18;
vm /= 10;
for (let j = 1; j < vm - 1; j++) {
stroke(255, 34, 63);
strokeWeight(4);
line(width / hm, (j * height) / vm, width / 2, (j * height) / vm); //red line
stroke(69, 98, 255);
line(
width / 2,
(j * height) / vm,
((hm - 1) * width) / hm,
(j * height) / vm
); //blue line
stroke(255);
line(
width / hm,
(j * height) / vm,
width / hm + changex,
(j * height) / vm - arrowHeight
); //left top
line(
width / hm + changex,
(j * height) / vm + arrowHeight,
width / hm,
(j * height) / vm
); //left bottom
line(
((hm - 1) * width) / hm + changex,
(j * height) / vm + arrowHeight,
((hm - 1) * width) / hm,
(j * height) / vm
); //right bottom
line(
width / 2 - changex,
(j * height) / vm + arrowHeight,
width / 2,
(j * height) / vm
); //center bottom
line(
((hm - 1) * width) / hm,
(j * height) / vm,
((hm - 1) * width) / hm + changex,
(j * height) / vm - arrowHeight
); //right bottom
line(
width / 2,
(j * height) / vm,
width / 2 - changex,
(j * height) / vm - arrowHeight
); //center top
}
}
function mouseClicked() {
lineMove = !lineMove;
}
</script>
</body>
</html>