introduce prettierrc formatting
This commit is contained in:
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
88
projects/flocking/pkg/flocking.d.ts
vendored
88
projects/flocking/pkg/flocking.d.ts
vendored
@@ -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>;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
"sideEffects": [
|
||||
"./snippets/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>Right‑click</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>Right‑click</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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
@@ -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');
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -17,4 +17,3 @@ export function createShaderProgram(gl, vertexCode, fragCode) {
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user