formatting
This commit is contained in:
210
404.css
210
404.css
@@ -1,171 +1,171 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.not-found {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 7rem 2rem 5rem;
|
||||
gap: 0;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 7rem 2rem 5rem;
|
||||
gap: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nf-code {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-size: clamp(7rem, 22vw, 16rem);
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
color: var(--surface0);
|
||||
letter-spacing: -0.02em;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.1s forwards;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: clamp(7rem, 22vw, 16rem);
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
color: var(--surface0);
|
||||
letter-spacing: -0.02em;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.1s forwards;
|
||||
}
|
||||
|
||||
|
||||
.nf-eyebrow {
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--mauve);
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.3s forwards;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--mauve);
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.3s forwards;
|
||||
}
|
||||
|
||||
.nf-eyebrow::before,
|
||||
.nf-eyebrow::after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 3rem;
|
||||
background: var(--mauve);
|
||||
opacity: 0.5;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 3rem;
|
||||
background: var(--mauve);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.nf-heading {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-size: clamp(1.6rem, 4vw, 2.8rem);
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
line-height: 1.15;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.4s forwards;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: clamp(1.6rem, 4vw, 2.8rem);
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
line-height: 1.15;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.4s forwards;
|
||||
}
|
||||
|
||||
.nf-heading em {
|
||||
font-style: italic;
|
||||
color: var(--mauve);
|
||||
font-style: italic;
|
||||
color: var(--mauve);
|
||||
}
|
||||
|
||||
.nf-desc {
|
||||
font-size: 0.88rem;
|
||||
color: var(--subtext0);
|
||||
max-width: 420px;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 2.5rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.5s forwards;
|
||||
font-size: 0.88rem;
|
||||
color: var(--subtext0);
|
||||
max-width: 420px;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 2.5rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.5s forwards;
|
||||
}
|
||||
|
||||
.nf-terminal {
|
||||
background: var(--mantle);
|
||||
border: 1px solid var(--surface0);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.5rem;
|
||||
font-size: 0.78rem;
|
||||
color: var(--subtext1);
|
||||
text-align: left;
|
||||
max-width: 440px;
|
||||
width: 100%;
|
||||
margin-bottom: 2.5rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.6s forwards;
|
||||
background: var(--mantle);
|
||||
border: 1px solid var(--surface0);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.5rem;
|
||||
font-size: 0.78rem;
|
||||
color: var(--subtext1);
|
||||
text-align: left;
|
||||
max-width: 440px;
|
||||
width: 100%;
|
||||
margin-bottom: 2.5rem;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.6s forwards;
|
||||
}
|
||||
|
||||
.nf-terminal-bar {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.nf-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.nf-dot--red {
|
||||
background: var(--red);
|
||||
opacity: 0.7;
|
||||
background: var(--red);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.nf-dot--yellow {
|
||||
background: var(--yellow);
|
||||
opacity: 0.7;
|
||||
background: var(--yellow);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.nf-dot--green {
|
||||
background: var(--green);
|
||||
opacity: 0.7;
|
||||
background: var(--green);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.nf-terminal-line {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
line-height: 1.9;
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
line-height: 1.9;
|
||||
}
|
||||
|
||||
.nf-prompt {
|
||||
color: var(--green);
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.nf-cmd {
|
||||
color: var(--text);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nf-err {
|
||||
color: var(--red);
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
.nf-comment {
|
||||
color: var(--overlay1);
|
||||
color: var(--overlay1);
|
||||
}
|
||||
|
||||
.nf-cursor {
|
||||
display: inline-block;
|
||||
width: 0.55em;
|
||||
height: 1em;
|
||||
background: var(--mauve);
|
||||
vertical-align: text-bottom;
|
||||
animation: blink 1.1s step-end infinite;
|
||||
display: inline-block;
|
||||
width: 0.55em;
|
||||
height: 1em;
|
||||
background: var(--mauve);
|
||||
vertical-align: text-bottom;
|
||||
animation: blink 1.1s step-end infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.nf-actions {
|
||||
display: flex;
|
||||
gap: 1.25rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.7s forwards;
|
||||
display: flex;
|
||||
gap: 1.25rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.6s 0.7s forwards;
|
||||
}
|
||||
|
||||
121
404.html
121
404.html
@@ -1,83 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>404 — Havox</title>
|
||||
<meta name="description" content="Page not found."/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<meta name="description" content="Page not found." />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400&family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;1,9..144,300&display=swap"
|
||||
rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css"/>
|
||||
<link rel="stylesheet" href="404.css"/>
|
||||
</head>
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400&family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;1,9..144,300&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="404.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- ─── Nav ─────────────────────────────────────────────────── -->
|
||||
<nav>
|
||||
<a href="index.html" class="nav-logo">havox</a>
|
||||
<ul class="nav-links">
|
||||
<body>
|
||||
<!-- ─── Nav ─────────────────────────────────────────────────── -->
|
||||
<nav>
|
||||
<a href="index.html" class="nav-logo">havox</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="index.html#about">about</a></li>
|
||||
<li><a href="index.html#skills">skills</a></li>
|
||||
<li><a href="index.html#projects">projects</a></li>
|
||||
<li><a href="index.html#contact">contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- ─── 404 ──────────────────────────────────────────────────── -->
|
||||
<main class="not-found">
|
||||
<!-- ─── 404 ──────────────────────────────────────────────────── -->
|
||||
<main class="not-found">
|
||||
<div class="nf-code">404</div>
|
||||
|
||||
<div class="nf-code">404</div>
|
||||
<p class="nf-eyebrow">page not found</p>
|
||||
<h1 class="nf-heading">This page <em>doesn't exist</em></h1>
|
||||
<p class="nf-desc">
|
||||
Either this URL was wrong, something got moved, or you've stumbled onto
|
||||
a dead link I haven't cleaned up yet. Wouldn't be the first time.
|
||||
</p>
|
||||
|
||||
<p class="nf-eyebrow">page not found</p>
|
||||
<h1 class="nf-heading">This page <em>doesn't exist</em></h1>
|
||||
<p class="nf-desc">
|
||||
Either this URL was wrong, something got moved, or you've stumbled onto a
|
||||
dead link I haven't cleaned up yet. Wouldn't be the first time.
|
||||
</p>
|
||||
|
||||
<div class="nf-terminal" aria-hidden="true">
|
||||
<div class="nf-terminal" aria-hidden="true">
|
||||
<div class="nf-terminal-bar">
|
||||
<span class="nf-dot nf-dot--red"></span>
|
||||
<span class="nf-dot nf-dot--yellow"></span>
|
||||
<span class="nf-dot nf-dot--green"></span>
|
||||
<span class="nf-dot nf-dot--red"></span>
|
||||
<span class="nf-dot nf-dot--yellow"></span>
|
||||
<span class="nf-dot nf-dot--green"></span>
|
||||
</div>
|
||||
<div class="nf-terminal-line">
|
||||
<span class="nf-prompt">~</span>
|
||||
<span class="nf-cmd">curl -I <span id="typed-path"></span></span>
|
||||
<span class="nf-prompt">~</span>
|
||||
<span class="nf-cmd">curl -I <span id="typed-path"></span></span>
|
||||
</div>
|
||||
<div class="nf-terminal-line" id="nf-response" style="opacity:0; transition: opacity 0.3s;">
|
||||
<span class="nf-err">HTTP/1.1 404 Not Found</span>
|
||||
<div
|
||||
class="nf-terminal-line"
|
||||
id="nf-response"
|
||||
style="opacity: 0; transition: opacity 0.3s"
|
||||
>
|
||||
<span class="nf-err">HTTP/1.1 404 Not Found</span>
|
||||
</div>
|
||||
<div class="nf-terminal-line" id="nf-hint" style="opacity:0; transition: opacity 0.3s;">
|
||||
<span class="nf-comment"># maybe try going home?</span>
|
||||
<div
|
||||
class="nf-terminal-line"
|
||||
id="nf-hint"
|
||||
style="opacity: 0; transition: opacity 0.3s"
|
||||
>
|
||||
<span class="nf-comment"># maybe try going home?</span>
|
||||
</div>
|
||||
<div class="nf-terminal-line">
|
||||
<span class="nf-prompt">~</span>
|
||||
<span class="nf-cursor"></span>
|
||||
<span class="nf-prompt">~</span>
|
||||
<span class="nf-cursor"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nf-actions">
|
||||
<div class="nf-actions">
|
||||
<a href="/" class="btn btn-primary">← Back home</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</main>
|
||||
<!-- ─── Footer ─────────────────────────────────────────────────── -->
|
||||
<footer>
|
||||
<span>umbra.mom — John Gatward</span>
|
||||
<span>
|
||||
Source code (self‑hosted):
|
||||
<a href="https://gitea.umbra.mom/jay/havox">repository</a>
|
||||
</span>
|
||||
</footer>
|
||||
|
||||
<!-- ─── Footer ─────────────────────────────────────────────────── -->
|
||||
<footer>
|
||||
<span>umbra.mom — John Gatward</span>
|
||||
<span>
|
||||
Source code (self‑hosted): <a href="https://gitea.umbra.mom/jay/havox">repository</a>
|
||||
</span>
|
||||
</footer>
|
||||
|
||||
<script src="404.js">
|
||||
</script>
|
||||
|
||||
</body>
|
||||
<script src="404.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
1011
index.html
1011
index.html
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0>
|
||||
<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>
|
||||
<script src="cPoint.js"></script>
|
||||
<script src="cLine.js"></script>
|
||||
<script src="sketch.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
</html>
|
||||
|
||||
@@ -1,184 +1,191 @@
|
||||
<DOCTYPE! html>
|
||||
<html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
</head>
|
||||
|
||||
<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;
|
||||
}
|
||||
|
||||
<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
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);
|
||||
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 10px 0;
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
} while (!pointOk);
|
||||
|
||||
<div id="container">
|
||||
<h1>Ellipse Construction</h1>
|
||||
point(ux, uy);
|
||||
getCirclePoints();
|
||||
drawTLines(ux, uy);
|
||||
}
|
||||
|
||||
<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>
|
||||
function getDist(x1, x2, y1, y2) {
|
||||
return Math.sqrt((x1 - x2) ^ (2 + (y1 - y2)) ^ 2);
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,78 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Flocking Boids - WASM</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<canvas id="canvas"></canvas>
|
||||
<aside id="controls" aria-expanded="true">
|
||||
<div id="controlsHeader">
|
||||
<h2 id="controlsTitle">Flocking Controls</h2>
|
||||
<button id="controlsToggle" type="button" aria-controls="controlsBody" aria-expanded="true" aria-label="Collapse controls">›</button>
|
||||
<canvas id="canvas"></canvas>
|
||||
<aside id="controls" aria-expanded="true">
|
||||
<div id="controlsHeader">
|
||||
<h2 id="controlsTitle">Flocking Controls</h2>
|
||||
<button
|
||||
id="controlsToggle"
|
||||
type="button"
|
||||
aria-controls="controlsBody"
|
||||
aria-expanded="true"
|
||||
aria-label="Collapse controls"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
<div id="controlsBody">
|
||||
<div class="control-group">
|
||||
<label for="alignSlider">Alignment</label>
|
||||
<input
|
||||
type="range"
|
||||
id="alignSlider"
|
||||
min="0"
|
||||
max="5"
|
||||
step="0.1"
|
||||
value="1.0"
|
||||
/>
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Weight:</span>
|
||||
<strong id="alignValue">1.00</strong>
|
||||
</div>
|
||||
<div id="controlsBody">
|
||||
<div class="control-group">
|
||||
<label for="alignSlider">Alignment</label>
|
||||
<input type="range" id="alignSlider" min="0" max="5" step="0.1" value="1.0">
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Weight:</span>
|
||||
<strong id="alignValue">1.00</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="cohesionSlider">Cohesion</label>
|
||||
<input type="range" id="cohesionSlider" min="0" max="5" step="0.1" value="0.9">
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Weight:</span>
|
||||
<strong id="cohesionValue">0.90</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="separationSlider">Separation</label>
|
||||
<input type="range" id="separationSlider" min="0" max="5" step="0.1" value="1.4">
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Weight:</span>
|
||||
<strong id="separationValue">1.40</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="addBtn">Boid Count (Step: 10)</label>
|
||||
<div class="button-group">
|
||||
<button id="removeBtn" type="button">−10 Remove</button>
|
||||
<button id="addBtn" type="button">+10 Add</button>
|
||||
</div>
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Total:</span>
|
||||
<strong id="boidCount">250</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Canvas:</span>
|
||||
<span class="stat-value" id="canvasSize">0x0</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">FPS:</span>
|
||||
<span class="stat-value" id="fps">60</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label for="cohesionSlider">Cohesion</label>
|
||||
<input
|
||||
type="range"
|
||||
id="cohesionSlider"
|
||||
min="0"
|
||||
max="5"
|
||||
step="0.1"
|
||||
value="0.9"
|
||||
/>
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Weight:</span>
|
||||
<strong id="cohesionValue">0.90</strong>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="separationSlider">Separation</label>
|
||||
<input
|
||||
type="range"
|
||||
id="separationSlider"
|
||||
min="0"
|
||||
max="5"
|
||||
step="0.1"
|
||||
value="1.4"
|
||||
/>
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Weight:</span>
|
||||
<strong id="separationValue">1.40</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="addBtn">Boid Count (Step: 10)</label>
|
||||
<div class="button-group">
|
||||
<button id="removeBtn" type="button">−10 Remove</button>
|
||||
<button id="addBtn" type="button">+10 Add</button>
|
||||
</div>
|
||||
<div class="value-display">
|
||||
<span class="stat-label">Total:</span>
|
||||
<strong id="boidCount">250</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Canvas:</span>
|
||||
<span class="stat-value" id="canvasSize">0x0</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">FPS:</span>
|
||||
<span class="stat-value" id="fps">60</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<script type="module" src="script.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -1,122 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #fafafa;
|
||||
margin: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
display: block;
|
||||
margin: 10px auto 0;
|
||||
width: 260px;
|
||||
}
|
||||
input[type="range"] {
|
||||
display: block;
|
||||
margin: 10px auto 0;
|
||||
width: 260px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<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>
|
||||
<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 class="canvas-holder"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
class dot {
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 time = 0;
|
||||
let wave = [];
|
||||
|
||||
let slider;
|
||||
let slider;
|
||||
|
||||
function setup() {
|
||||
createCanvas(900, 800);
|
||||
slider = createSlider(1, 50, 5);
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
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>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,224 +1,247 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0>
|
||||
<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;
|
||||
}
|
||||
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 {
|
||||
max-width: 900px;
|
||||
margin: 0 auto 18px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.intro h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
.intro h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
.intro p {
|
||||
margin: 4px 0 10px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.intro p {
|
||||
margin: 4px 0 10px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<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>
|
||||
<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 reso = 50;
|
||||
var cols, rows;
|
||||
|
||||
var field = [];
|
||||
var field = [];
|
||||
|
||||
var draw_lines = true;
|
||||
var draw_lines = true;
|
||||
|
||||
function setup() {
|
||||
createCanvas(800, 600);
|
||||
function setup() {
|
||||
createCanvas(800, 600);
|
||||
|
||||
cols = width / reso + 1;
|
||||
rows = height / reso + 1;
|
||||
cols = width / reso + 1;
|
||||
rows = height / reso + 1;
|
||||
|
||||
for (var i = 0; i < cols * rows; i++)
|
||||
field[i] = Math.floor(random(2));
|
||||
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);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(100);
|
||||
const cBlue = color(55, 120, 232);
|
||||
const cGreen = color(146, 232, 55);
|
||||
for (var i = 0; i < cols * rows; i++) {
|
||||
if (i + cols + 1 > cols * rows) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (var i = 0; i < cols * rows; i++) {
|
||||
let x = i % cols;
|
||||
let y = Math.floor(i / cols);
|
||||
var x = (i % cols) * reso;
|
||||
var y = Math.floor(i / cols) * reso;
|
||||
|
||||
let c = field[i] ? cBlue : cGreen;
|
||||
fill(c);
|
||||
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];
|
||||
|
||||
rect(x * reso - reso * 0.5, y * reso - reso * 0.5, reso, reso);
|
||||
}
|
||||
var s = getState(
|
||||
field[i],
|
||||
field[i + 1],
|
||||
field[i + cols + 1],
|
||||
field[i + cols],
|
||||
);
|
||||
noStroke();
|
||||
|
||||
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);}
|
||||
}
|
||||
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 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 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;
|
||||
}
|
||||
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>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0>
|
||||
<head>
|
||||
<meta
|
||||
name="viewport"
|
||||
width="device-width,"
|
||||
initial-scale="1.0,"
|
||||
maximum-scale="1.0,"
|
||||
user-scalable="0"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
<script src="sketch.js"></script>
|
||||
@@ -14,39 +19,34 @@
|
||||
<title>Maze Generator</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #fafafa;
|
||||
margin: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #fafafa;
|
||||
margin: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
|
||||
<div id="container">
|
||||
<div id="container">
|
||||
<h1>Maze Generator</h1>
|
||||
|
||||
<p>
|
||||
Toy maze generator using DFS & recursice back tracking.
|
||||
</p>
|
||||
<p>Toy maze generator using DFS & recursice back tracking.</p>
|
||||
|
||||
<h2 id="PIbox"></h2>
|
||||
<h3 id="percentage"></h3>
|
||||
</div>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</div>
|
||||
|
||||
<body></body>
|
||||
</html>
|
||||
|
||||
@@ -1,73 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Percolation</title>
|
||||
<link rel="stylesheet" href="style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/#projects" class="back-button">← Back</a>
|
||||
<a href="/#projects" class="back-button">← Back</a>
|
||||
|
||||
<header>
|
||||
<h1>Percolation</h1>
|
||||
<p class="subtitle">A simulation written in C & Raylib.</p>
|
||||
</header>
|
||||
<header>
|
||||
<h1>Percolation</h1>
|
||||
<p class="subtitle">A simulation written in C & Raylib.</p>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<p>
|
||||
<strong>Percolation</strong> is a simple model for connectivity on a grid. Each cell is either open or closed.
|
||||
This simulation stops as soon as there is a path from the top of the grid to the bottom through the open cells.
|
||||
</p>
|
||||
<div class="info-box">
|
||||
<p>
|
||||
In this simulation, cells are randomly opened on a grid. Percolation occurs when there exists
|
||||
a connected path of open cells from the top to the bottom of the grid. It is a neat way to visualise
|
||||
threshold behaviour: below a certain probability nothing connects, and above it large connected regions
|
||||
suddenly begin to appear.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<p>
|
||||
<strong>Percolation</strong> is a simple model for connectivity on a
|
||||
grid. Each cell is either open or closed. This simulation stops as
|
||||
soon as there is a path from the top of the grid to the bottom through
|
||||
the open cells.
|
||||
</p>
|
||||
<div class="info-box">
|
||||
<p>
|
||||
In this simulation, cells are randomly opened on a grid. Percolation
|
||||
occurs when there exists a connected path of open cells from the top
|
||||
to the bottom of the grid. It is a neat way to visualise threshold
|
||||
behaviour: below a certain probability nothing connects, and above
|
||||
it large connected regions suddenly begin to appear.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<p>Canvas</p>
|
||||
<button id="fullscreenButton" class="action-button" type="button">Fullscreen</button>
|
||||
</div>
|
||||
|
||||
<div class="canvas-shell">
|
||||
<canvas id="canvas" aria-label="Percolation simulation canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status">Downloading...</div>
|
||||
<progress id="progress" value="0" max="100" hidden></progress>
|
||||
|
||||
<details>
|
||||
<summary>Show console output</summary>
|
||||
<label class="visually-hidden" for="output">Console output</label>
|
||||
<textarea id="output" rows="8" readonly></textarea>
|
||||
</details>
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<p>Canvas</p>
|
||||
<button id="fullscreenButton" class="action-button" type="button">
|
||||
Fullscreen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3>Technical Details</h3>
|
||||
<p>
|
||||
The simulation uses a <strong>disjoint-set (Union-Find)</strong> style connectivity algorithm. As random cells open,
|
||||
each open cell is union-ed with its open neighbours, and two virtual nodes represent the top and bottom edges
|
||||
of the grid. Percolation is detected the moment those two virtual nodes become connected.
|
||||
</p>
|
||||
<p>
|
||||
This makes each update fast and scalable even for larger grids.
|
||||
</p>
|
||||
</section>
|
||||
<div class="canvas-shell">
|
||||
<canvas
|
||||
id="canvas"
|
||||
aria-label="Percolation simulation canvas"
|
||||
oncontextmenu="event.preventDefault()"
|
||||
></canvas>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>Built with C, Raylib, and WebAssembly</p>
|
||||
</footer>
|
||||
<div id="status" class="status">Downloading...</div>
|
||||
<progress id="progress" value="0" max="100" hidden></progress>
|
||||
|
||||
<details>
|
||||
<summary>Show console output</summary>
|
||||
<label class="visually-hidden" for="output">Console output</label>
|
||||
<textarea id="output" rows="8" readonly></textarea>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3>Technical Details</h3>
|
||||
<p>
|
||||
The simulation uses a <strong>disjoint-set (Union-Find)</strong> style
|
||||
connectivity algorithm. As random cells open, each open cell is
|
||||
union-ed with its open neighbours, and two virtual nodes represent the
|
||||
top and bottom edges of the grid. Percolation is detected the moment
|
||||
those two virtual nodes become connected.
|
||||
</p>
|
||||
<p>This makes each update fast and scalable even for larger grids.</p>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>Built with C, Raylib, and WebAssembly</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
<script async src="index.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,197 +1,197 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
color: black;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin: 20px 0 15px;
|
||||
font-size: 1.8rem;
|
||||
margin: 20px 0 15px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
margin: 15px 0;
|
||||
font-size: 1.3rem;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px 15px;
|
||||
background: #f0f0f0;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px 15px;
|
||||
background: #f0f0f0;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.back-button:hover,
|
||||
.action-button:hover {
|
||||
background: #e0e0e0;
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: #f9f9f9;
|
||||
border-left: 4px solid #333;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
background: #f9f9f9;
|
||||
border-left: 4px solid #333;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
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: #666;
|
||||
font-size: 0.95rem;
|
||||
margin: 0;
|
||||
color: #666;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
padding: 8px 12px;
|
||||
background: #f0f0f0;
|
||||
color: black;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
padding: 8px 12px;
|
||||
background: #f0f0f0;
|
||||
color: black;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.canvas-shell {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 500px;
|
||||
overflow: auto;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 500px;
|
||||
overflow: auto;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
background: black;
|
||||
outline: none;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
background: black;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 15px;
|
||||
min-height: 24px;
|
||||
color: #666;
|
||||
margin-top: 15px;
|
||||
min-height: 24px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
progress {
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
progress[hidden] {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
details {
|
||||
margin-top: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.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: 1px solid #ddd;
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 0.9rem;
|
||||
resize: vertical;
|
||||
background: white;
|
||||
color: black;
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 0.9rem;
|
||||
resize: vertical;
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #ccc;
|
||||
font-size: 0.85rem;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #ccc;
|
||||
font-size: 0.85rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 16px;
|
||||
}
|
||||
body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
padding: 15px;
|
||||
}
|
||||
.canvas-container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.canvas-shell {
|
||||
min-height: 360px;
|
||||
}
|
||||
.canvas-shell {
|
||||
min-height: 360px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
|
||||
<h1 id="PIbox"></h1>
|
||||
<h2 id="percentage"></h2>
|
||||
|
||||
<h1 id="PIbox"></h1>
|
||||
<h2 id="percentage"></h2>
|
||||
|
||||
<style>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #fafafa;
|
||||
margin: 20px;
|
||||
text-align: center;
|
||||
font-family: Arial, sans-serif;
|
||||
background: #fafafa;
|
||||
margin: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 10px 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
|
||||
<div id="container">
|
||||
<div id="container">
|
||||
<h1>Monte Carlo π Estimator</h1>
|
||||
|
||||
<p>
|
||||
A small demonstation on how the Monte Carlo method can approximate the value of π
|
||||
by randomly placing points inside a square and checking how many fall within
|
||||
the inner circle. The closer the ratio gets to π/4, the more accurate
|
||||
the estimation becomes.
|
||||
A small demonstation on how the Monte Carlo method can approximate the
|
||||
value of π by randomly placing points inside a square and checking how
|
||||
many fall within the inner circle. The closer the ratio gets to π/4, the
|
||||
more accurate the estimation becomes.
|
||||
</p>
|
||||
|
||||
<h2 id="PIbox"></h2>
|
||||
<h3 id="percentage"></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
<script>
|
||||
const r = 400;
|
||||
var circleCount, total;
|
||||
var x, y;
|
||||
@@ -52,53 +49,51 @@
|
||||
var BestPI = 50;
|
||||
|
||||
function setup() {
|
||||
createCanvas(r * 2, r * 2);
|
||||
background(0);
|
||||
stroke(255, 0, 0);
|
||||
strokeWeight(3);
|
||||
noFill()
|
||||
translate(width / 2, height / 2);
|
||||
rectMode(CENTER);
|
||||
rect(0, 0, r * 2, r * 2);
|
||||
ellipse(0, 0, r * 2, r * 2);
|
||||
circleCount = 0;
|
||||
total = 0;
|
||||
createCanvas(r * 2, r * 2);
|
||||
background(0);
|
||||
stroke(255, 0, 0);
|
||||
strokeWeight(3);
|
||||
noFill();
|
||||
translate(width / 2, height / 2);
|
||||
rectMode(CENTER);
|
||||
rect(0, 0, r * 2, r * 2);
|
||||
ellipse(0, 0, r * 2, r * 2);
|
||||
circleCount = 0;
|
||||
total = 0;
|
||||
}
|
||||
|
||||
function draw() {
|
||||
translate(width / 2, height / 2);
|
||||
x = random(-r, r);
|
||||
y = random(-r, r);
|
||||
|
||||
translate(width / 2, height / 2);
|
||||
x = random(-r, r)
|
||||
y = random(-r, r)
|
||||
stroke(0, 255, 0);
|
||||
|
||||
stroke(0, 255, 0)
|
||||
total += 1;
|
||||
|
||||
total += 1
|
||||
var d = dist(x, y, 0, 0);
|
||||
if (d < r) {
|
||||
circleCount += 1;
|
||||
stroke(0, 0, 255);
|
||||
}
|
||||
|
||||
var d = dist(x, y, 0, 0)
|
||||
if (d < r) {
|
||||
circleCount += 1
|
||||
stroke(0, 0, 255)
|
||||
}
|
||||
point(x, y);
|
||||
|
||||
point(x, y);
|
||||
calcPI = 4 * (circleCount / total);
|
||||
if (abs(PI - calcPI) < abs(PI - BestPI)) {
|
||||
DisplayPI = calcPI;
|
||||
BestPI = calcPI;
|
||||
|
||||
calcPI = 4 * (circleCount / total);
|
||||
if (abs(PI - calcPI) < abs(PI - BestPI)) {
|
||||
DisplayPI = calcPI;
|
||||
BestPI = calcPI;
|
||||
var PIDisplay = document.getElementById("PIbox");
|
||||
PIDisplay.innerHTML = DisplayPI;
|
||||
|
||||
var PIDisplay = document.getElementById("PIbox");
|
||||
PIDisplay.innerHTML = DisplayPI;
|
||||
|
||||
var ErrorDisplay = document.getElementById("percentage");
|
||||
var DisplayError = "a";
|
||||
var Perror = (calcPI - PI) / PI
|
||||
Perror *= 100;
|
||||
DisplayError = nfc(Perror, 10);
|
||||
ErrorDisplay.innerHTML = DisplayError + '%';
|
||||
}
|
||||
var ErrorDisplay = document.getElementById("percentage");
|
||||
var DisplayError = "a";
|
||||
var Perror = (calcPI - PI) / PI;
|
||||
Perror *= 100;
|
||||
DisplayError = nfc(Perror, 10);
|
||||
ErrorDisplay.innerHTML = DisplayError + "%";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
<script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
<script>
|
||||
var r = 10; //circle size
|
||||
var k = 30;
|
||||
var grid = [];
|
||||
@@ -11,94 +11,93 @@
|
||||
var ordered = [];
|
||||
|
||||
function setup() {
|
||||
createCanvas(900, 900);
|
||||
background(0);
|
||||
strokeWeight(4);
|
||||
colorMode(HSB);
|
||||
createCanvas(900, 900);
|
||||
background(0);
|
||||
strokeWeight(4);
|
||||
colorMode(HSB);
|
||||
|
||||
cols = floor(width / w);
|
||||
rows = floor(height / w);
|
||||
for (var i = 0; i < cols * rows; i++) {
|
||||
grid[i] = undefined;
|
||||
}
|
||||
|
||||
cols = floor(width / w);
|
||||
rows = floor(height / w);
|
||||
for (var i = 0; i < cols * rows; i++) {
|
||||
grid[i] = undefined;
|
||||
}
|
||||
|
||||
|
||||
var x = width / 2;
|
||||
var y = height / 2;
|
||||
var i = floor(x / w);
|
||||
var j = floor(y / w);
|
||||
var pos = createVector(x, y);
|
||||
grid[i + j * cols] = pos;
|
||||
active.push(pos);
|
||||
|
||||
var x = width / 2;
|
||||
var y = height / 2;
|
||||
var i = floor(x / w);
|
||||
var j = floor(y / w);
|
||||
var pos = createVector(x, y);
|
||||
grid[i + j * cols] = pos;
|
||||
active.push(pos);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(0);
|
||||
background(0);
|
||||
|
||||
for (var total = 0; total < 30; total++) {
|
||||
if (active.length > 0) {
|
||||
var randIndex = floor(random(active.length));
|
||||
var pos = active[randIndex];
|
||||
var found = false;
|
||||
for (var n = 0; n < k; n++) {
|
||||
var sample = p5.Vector.random2D();
|
||||
var m = random(r, 2 * r);
|
||||
sample.setMag(m);
|
||||
sample.add(pos);
|
||||
|
||||
for (var total = 0; total < 30; total++) {
|
||||
if (active.length > 0) {
|
||||
var randIndex = floor(random(active.length));
|
||||
var pos = active[randIndex];
|
||||
var found = false;
|
||||
for (var n = 0; n < k; n++) {
|
||||
var sample = p5.Vector.random2D();
|
||||
var m = random(r, 2 * r);
|
||||
sample.setMag(m);
|
||||
sample.add(pos);
|
||||
var col = floor(sample.x / w);
|
||||
var row = floor(sample.y / w);
|
||||
|
||||
var col = floor(sample.x / w);
|
||||
var row = floor(sample.y / w);
|
||||
|
||||
if (col > -1 && row > -1 && col < cols && row < rows && !grid[col + row * cols]) {
|
||||
var flag = true;
|
||||
for (var i = -1; i <= 1; i++) {
|
||||
for (var j = -1; j <= 1; j++) {
|
||||
var index = (col + i) + (row + j) * cols;
|
||||
var neighbor = grid[index];
|
||||
if (neighbor) {
|
||||
var d = p5.Vector.dist(sample, neighbor);
|
||||
if (d < r) {
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
found = true;
|
||||
grid[col + row * cols] = sample;
|
||||
active.push(sample);
|
||||
ordered.push(sample);
|
||||
|
||||
//break;
|
||||
}
|
||||
if (
|
||||
col > -1 &&
|
||||
row > -1 &&
|
||||
col < cols &&
|
||||
row < rows &&
|
||||
!grid[col + row * cols]
|
||||
) {
|
||||
var flag = true;
|
||||
for (var i = -1; i <= 1; i++) {
|
||||
for (var j = -1; j <= 1; j++) {
|
||||
var index = col + i + (row + j) * cols;
|
||||
var neighbor = grid[index];
|
||||
if (neighbor) {
|
||||
var d = p5.Vector.dist(sample, neighbor);
|
||||
if (d < r) {
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
found = true;
|
||||
grid[col + row * cols] = sample;
|
||||
active.push(sample);
|
||||
ordered.push(sample);
|
||||
|
||||
if (!found) {
|
||||
active.splice(randIndex, 1);
|
||||
}
|
||||
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < ordered.length; i++) {
|
||||
|
||||
stroke(255, 0, 255);
|
||||
strokeWeight(r * 0.5);
|
||||
point(ordered[i].x, ordered[i].y);
|
||||
if (!found) {
|
||||
active.splice(randIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < active.length; i++) {
|
||||
if (ordered[i]) {
|
||||
stroke(i % 360, 100, 100);
|
||||
strokeWeight(r * 0.5);
|
||||
point(active[i].x, active[i].y);
|
||||
}
|
||||
for (var i = 0; i < ordered.length; i++) {
|
||||
stroke(255, 0, 255);
|
||||
strokeWeight(r * 0.5);
|
||||
point(ordered[i].x, ordered[i].y);
|
||||
}
|
||||
|
||||
for (var i = 0; i < active.length; i++) {
|
||||
if (ordered[i]) {
|
||||
stroke(i % 360, 100, 100);
|
||||
strokeWeight(r * 0.5);
|
||||
point(active[i].x, active[i].y);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,72 +1,91 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="Thin Ice - a cute pixel art winter puzzle game." />
|
||||
<title>Thin Ice | havox</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/#projects" class="back-button">← Back</a>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Thin Ice - a cute pixel art winter puzzle game."
|
||||
/>
|
||||
<title>Thin Ice | havox</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/#projects" class="back-button">← Back</a>
|
||||
|
||||
<header>
|
||||
<h1>Thin Ice</h1>
|
||||
<p class="subtitle">Inspired by Club Penguin's Thin Ice mini-game.</p>
|
||||
</header>
|
||||
<header>
|
||||
<h1>Thin Ice</h1>
|
||||
<p class="subtitle">Inspired by Club Penguin's Thin Ice mini-game.</p>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<p>
|
||||
You start on safe ice, breaking ice where you walk, trying to reach the finish tile before trapping yourself.
|
||||
</p>
|
||||
<div class="info-box">
|
||||
<section>
|
||||
<p>
|
||||
See if you can get a gold medal on all 6 levels by breaking every single tile of ice!
|
||||
You start on safe ice, breaking ice where you walk, trying to reach
|
||||
the finish tile before trapping yourself.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<div class="info-box">
|
||||
<p>
|
||||
See if you can get a gold medal on all 6 levels by breaking every
|
||||
single tile of ice!
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<p>Game</p>
|
||||
<button id="fullscreenButton" class="action-button" type="button">Fullscreen</button>
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<p>Game</p>
|
||||
<button id="fullscreenButton" class="action-button" type="button">
|
||||
Fullscreen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="canvas-shell">
|
||||
<canvas
|
||||
id="canvas"
|
||||
class="emscripten"
|
||||
aria-label="Thin Ice game canvas"
|
||||
oncontextmenu="event.preventDefault()"
|
||||
tabindex="-1"
|
||||
></canvas>
|
||||
</div>
|
||||
|
||||
<div class="canvas-options">
|
||||
<label
|
||||
><input type="checkbox" id="resize" /> Resize canvas in
|
||||
fullscreen</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" id="pointerLock" checked /> Lock pointer in
|
||||
fullscreen</label
|
||||
>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status">Downloading...</div>
|
||||
<progress id="progress" value="0" max="100" hidden></progress>
|
||||
|
||||
<details>
|
||||
<summary>Show console output</summary>
|
||||
<label class="visually-hidden" for="output">Console output</label>
|
||||
<textarea id="output" rows="8" readonly></textarea>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="canvas-shell">
|
||||
<canvas id="canvas" class="emscripten" aria-label="Thin Ice game canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||
</div>
|
||||
<section>
|
||||
<h2>How to Play</h2>
|
||||
<ul class="feature-list">
|
||||
<li>Use WASM keys to move across the ice.</li>
|
||||
<li>Plan ahead so you do not strand yourself on broken tiles.</li>
|
||||
<li>Reach the end tile without falling into the water.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class="canvas-options">
|
||||
<label><input type="checkbox" id="resize" /> Resize canvas in fullscreen</label>
|
||||
<label><input type="checkbox" id="pointerLock" checked /> Lock pointer in fullscreen</label>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status">Downloading...</div>
|
||||
<progress id="progress" value="0" max="100" hidden></progress>
|
||||
|
||||
<details>
|
||||
<summary>Show console output</summary>
|
||||
<label class="visually-hidden" for="output">Console output</label>
|
||||
<textarea id="output" rows="8" readonly></textarea>
|
||||
</details>
|
||||
<footer>
|
||||
<p>Built with C++ & Raylib, compiled to WASM with emscripten.</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h2>How to Play</h2>
|
||||
<ul class="feature-list">
|
||||
<li>Use WASM keys to move across the ice.</li>
|
||||
<li>Plan ahead so you do not strand yourself on broken tiles.</li>
|
||||
<li>Reach the end tile without falling into the water.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>Built with C++ & Raylib, compiled to WASM with emscripten.</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
<script async src="thin_ice.js"></script>
|
||||
</body>
|
||||
<script src="script.js"></script>
|
||||
<script async src="thin_ice.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,74 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Travelling Salesman Problem</title>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/#projects" class="back-button">← Back</a>
|
||||
<a href="/#projects" class="back-button">← Back</a>
|
||||
|
||||
<header>
|
||||
<h1>Travelling Salesman Problem</h1>
|
||||
<p class="subtitle">An interactive route visualiser built with WebGL and WebAssembly.</p>
|
||||
</header>
|
||||
<header>
|
||||
<h1>Travelling Salesman Problem</h1>
|
||||
<p class="subtitle">
|
||||
An interactive route visualiser built with WebGL and WebAssembly.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<p>
|
||||
After writing one of my first tutorials on the Travelling Salesman Problem, I wanted to revisit it with a slightly more technical stack.
|
||||
The backend is written in Zig (compiled to WebAssembly), and the rendering is done directly in WebGL instead of p5.js or Raylib.
|
||||
</p>
|
||||
<section>
|
||||
<p>
|
||||
After writing one of my first tutorials on the Travelling Salesman
|
||||
Problem, I wanted to revisit it with a slightly more technical stack.
|
||||
The backend is written in Zig (compiled to WebAssembly), and the
|
||||
rendering is done directly in WebGL instead of p5.js or Raylib.
|
||||
</p>
|
||||
<p>
|
||||
This project was also inspired by
|
||||
<a href="https://www.youtube.com/@sphaerophoria">sphaerophoria</a> who
|
||||
started a
|
||||
<a
|
||||
href="https://www.youtube.com/watch?v=RiLjPBci6Og&list=PL980gcR1LE3L8RoIMSNBFfw4dFfS3rrsk"
|
||||
>series</a
|
||||
>
|
||||
on creating his own map.
|
||||
</p>
|
||||
|
||||
<div class="info-box">
|
||||
<p>
|
||||
This project was also inspired by <a href="https://www.youtube.com/@sphaerophoria">sphaerophoria</a> who started a <a href="https://www.youtube.com/watch?v=RiLjPBci6Og&list=PL980gcR1LE3L8RoIMSNBFfw4dFfS3rrsk">series</a> on creating his own map.
|
||||
Drag points around the canvas and watch the route update in real
|
||||
time.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="info-box">
|
||||
<p>
|
||||
Drag points around the canvas and watch the route update in real time.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<div class="control-group" aria-label="TSP controls">
|
||||
<label for="pointCountInput">Points</label>
|
||||
<input id="pointCountInput" type="number" min="5" max="100" step="1" value="36" />
|
||||
<button id="applyPointCountButton" class="action-button" type="button">Regenerate</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="canvas-shell">
|
||||
<canvas id="tsp-canvas" aria-label="Travelling Salesman Problem canvas">
|
||||
HTML5 canvas not supported in browser
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<div class="status-row">
|
||||
<div id="pointCountStatus" class="status">36 points</div>
|
||||
<p class="canvas-note">Tip: click and drag a point to recompute the route.</p>
|
||||
</div>
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<div class="control-group" aria-label="TSP controls">
|
||||
<label for="pointCountInput">Points</label>
|
||||
<input
|
||||
id="pointCountInput"
|
||||
type="number"
|
||||
min="5"
|
||||
max="100"
|
||||
step="1"
|
||||
value="36"
|
||||
/>
|
||||
<button
|
||||
id="applyPointCountButton"
|
||||
class="action-button"
|
||||
type="button"
|
||||
>
|
||||
Regenerate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3>Technical Details</h3>
|
||||
<p>
|
||||
The backend is written in Zig, and the route is built in two passes: a nearest-neighbour pass followed by a 2-opt local search.
|
||||
</p>
|
||||
<p>
|
||||
Point coordinates are stored in WebAssembly memory, and the route order is recalculated whenever the layout changes.
|
||||
Rendering is handled in WebGL to keep interaction smooth as the point count increases.
|
||||
</p>
|
||||
</section>
|
||||
<div class="canvas-shell">
|
||||
<canvas
|
||||
id="tsp-canvas"
|
||||
aria-label="Travelling Salesman Problem canvas"
|
||||
>
|
||||
HTML5 canvas not supported in browser
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>Built with Zig, WebGL2 and WebAssembly</p>
|
||||
</footer>
|
||||
<div class="status-row">
|
||||
<div id="pointCountStatus" class="status">36 points</div>
|
||||
<p class="canvas-note">
|
||||
Tip: click and drag a point to recompute the route.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3>Technical Details</h3>
|
||||
<p>
|
||||
The backend is written in Zig, and the route is built in two passes: a
|
||||
nearest-neighbour pass followed by a 2-opt local search.
|
||||
</p>
|
||||
<p>
|
||||
Point coordinates are stored in WebAssembly memory, and the route
|
||||
order is recalculated whenever the layout changes. Rendering is
|
||||
handled in WebGL to keep interaction smooth as the point count
|
||||
increases.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>Built with Zig, WebGL2 and WebAssembly</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="./point.js"></script>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -199,4 +199,3 @@ footer {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,166 +1,222 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0>
|
||||
<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;
|
||||
}
|
||||
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 {
|
||||
max-width: 900px;
|
||||
margin: 20px auto 10px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.intro h1 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.intro h1 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.intro p {
|
||||
margin: 4px 0 14px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.intro p {
|
||||
margin: 4px 0 14px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.slidecontainer {
|
||||
width: 100%;
|
||||
/* Width of the outside container */
|
||||
}
|
||||
.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: .2s;
|
||||
/* 0.2 seconds transition on hover */
|
||||
transition: opacity .2s;
|
||||
}
|
||||
/* 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::-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 */
|
||||
}
|
||||
.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>
|
||||
</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>
|
||||
<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">
|
||||
<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;
|
||||
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 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++;
|
||||
}
|
||||
|
||||
function windowResized() {
|
||||
resizeCanvas(windowWidth, windowHeight * 0.6);
|
||||
if (t == 360) {
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(43);
|
||||
angleMode(DEGREES);
|
||||
lineNum = document.getElementById('myRange').value;
|
||||
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
|
||||
|
||||
drawLine(hMargin, lineNum + 1);
|
||||
stroke(69, 98, 255);
|
||||
line(
|
||||
width / 2,
|
||||
(j * height) / vm,
|
||||
((hm - 1) * width) / hm,
|
||||
(j * height) / vm,
|
||||
); //blue line
|
||||
|
||||
if (lineMove) {
|
||||
t++;
|
||||
}
|
||||
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
|
||||
|
||||
if (t == 360) {
|
||||
t = 0;
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
function mouseClicked() {
|
||||
lineMove = !lineMove;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<DOCTYPE! html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>Convex Hull</title>
|
||||
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
h1{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
h1 {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
h2 {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p{
|
||||
margin: 1px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
p {
|
||||
margin: 1px;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
}
|
||||
|
||||
.psudoCode{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
.psudoCode {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #d9d9d9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
}
|
||||
|
||||
a{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
|
||||
a {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
|
||||
button{
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float:right;
|
||||
button {
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
@@ -58,14 +57,13 @@ button:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.examples{
|
||||
padding: 10px;
|
||||
.examples {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
html{
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
html {
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
}
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
h1{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
h1 {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
h2 {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p{
|
||||
margin: 1px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
p {
|
||||
margin: 1px;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
}
|
||||
|
||||
.psudoCode{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
.psudoCode {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #d9d9d9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
}
|
||||
|
||||
a{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
|
||||
a {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
|
||||
button{
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float:right;
|
||||
button {
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
@@ -58,14 +57,13 @@ button:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.examples{
|
||||
padding: 10px;
|
||||
.examples {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
html{
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
html {
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
}
|
||||
|
||||
@@ -1,43 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/atom-one-dark.min.css" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/atom-one-dark.min.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="script.js"></script>
|
||||
<script>
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script src="script.js"></script>
|
||||
<script>
|
||||
fetch("snippets/defineCount.js")
|
||||
.then(r => r.text())
|
||||
.then(code => {
|
||||
document.getElementById("define-count").textContent = code;
|
||||
if (window.hljs) hljs.highlightElement(document.getElementById("define-count"));
|
||||
});
|
||||
.then((r) => r.text())
|
||||
.then((code) => {
|
||||
document.getElementById("define-count").textContent = code;
|
||||
if (window.hljs)
|
||||
hljs.highlightElement(document.getElementById("define-count"));
|
||||
});
|
||||
fetch("snippets/countSquares.js")
|
||||
.then(r => r.text())
|
||||
.then(code => {
|
||||
document.getElementById("count-squares").textContent = code;
|
||||
if (window.hljs) hljs.highlightElement(document.getElementById("count-squares"));
|
||||
});
|
||||
</script>
|
||||
.then((r) => r.text())
|
||||
.then((code) => {
|
||||
document.getElementById("count-squares").textContent = code;
|
||||
if (window.hljs)
|
||||
hljs.highlightElement(document.getElementById("count-squares"));
|
||||
});
|
||||
</script>
|
||||
|
||||
<head>
|
||||
<head>
|
||||
<h1>Summed-area Table</h1>
|
||||
<p>
|
||||
A Summed-area table is a data structure and algorithm for quickly and
|
||||
efficiently generating the sum of values in a rectangular grid. In image
|
||||
processing it is also known as an integral image.
|
||||
A Summed-area table is a data structure and algorithm for quickly and
|
||||
efficiently generating the sum of values in a rectangular grid. In image
|
||||
processing it is also known as an integral image.
|
||||
</p>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
<p>
|
||||
To generate a summed-area table, each cell is given a value which is the
|
||||
sum of all values in a rectangular subset where the top left corner is the
|
||||
first cell in the grid and where the bottom right cell is the cell you're
|
||||
calculating the value for (assumuing the array starts top left).
|
||||
To generate a summed-area table, each cell is given a value which is the
|
||||
sum of all values in a rectangular subset where the top left corner is the
|
||||
first cell in the grid and where the bottom right cell is the cell you're
|
||||
calculating the value for (assumuing the array starts top left).
|
||||
</p>
|
||||
<pre>
|
||||
<code id="define-count" class="js">
|
||||
@@ -45,10 +51,10 @@
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now each cell has its own count value. This isn't much help on it's own,
|
||||
however we can use it to calculate the sum value of any subset of the grid
|
||||
by interrogating 4 cells and performing only 4 additions. NOTE: we have to
|
||||
add the 'top left' region as it as been subtracted twice.
|
||||
Now each cell has its own count value. This isn't much help on it's own,
|
||||
however we can use it to calculate the sum value of any subset of the grid
|
||||
by interrogating 4 cells and performing only 4 additions. NOTE: we have to
|
||||
add the 'top left' region as it as been subtracted twice.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -59,28 +65,26 @@
|
||||
<p><b>Click</b> a sqaure on the grid to turn it on or off.</p>
|
||||
<p><b>Shift+Click</b> two square to define a rectangle.</p>
|
||||
<p>
|
||||
Press the <b>show count</b> button to display the sum value of each cell
|
||||
(in this example a cell's value can only be 0 or 1).
|
||||
Press the <b>show count</b> button to display the sum value of each cell
|
||||
(in this example a cell's value can only be 0 or 1).
|
||||
</p>
|
||||
<p>
|
||||
Press <b>show rectangles</b> to see the different regions simplified and
|
||||
see how
|
||||
it changes the sum below.
|
||||
Press <b>show rectangles</b> to see the different regions simplified and
|
||||
see how it changes the sum below.
|
||||
</p>
|
||||
<div id="sumDiv">
|
||||
<a id="subsetOutput">0</a>
|
||||
<a> = </a>
|
||||
<a id="wholeOutput"> 0</a>
|
||||
<a> + </a>
|
||||
<a id="tlOutput"> 0</a>
|
||||
<a> - </a>
|
||||
<a id="lOutput"> 0</a>
|
||||
<a> - </a>
|
||||
<a id="tOutput"> 0</a>
|
||||
<a id="subsetOutput">0</a>
|
||||
<a> = </a>
|
||||
<a id="wholeOutput"> 0</a>
|
||||
<a> + </a>
|
||||
<a id="tlOutput"> 0</a>
|
||||
<a> - </a>
|
||||
<a id="lOutput"> 0</a>
|
||||
<a> - </a>
|
||||
<a id="tOutput"> 0</a>
|
||||
</div>
|
||||
|
||||
<button onclick="showCountToggle()">Show count</button>
|
||||
<button onclick="toggleRectMode()">Show rectangles</button>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,43 +1,54 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
|
||||
html {
|
||||
background-color: #2e3440; }
|
||||
background-color: #2e3440;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #eceff4;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 48pt; }
|
||||
font-size: 48pt;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #d8dee9;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt; }
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 14pt;
|
||||
border: 4px solid #ebcb8b; }
|
||||
border: 4px solid #ebcb8b;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #d8dee9;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 24pt; }
|
||||
font-size: 24pt;
|
||||
}
|
||||
|
||||
#subsetOutput {
|
||||
color: #88c0d0; }
|
||||
color: #88c0d0;
|
||||
}
|
||||
|
||||
#wholeOutput {
|
||||
color: #b48ead; }
|
||||
color: #b48ead;
|
||||
}
|
||||
|
||||
#tlOutput {
|
||||
color: #d08770; }
|
||||
color: #d08770;
|
||||
}
|
||||
|
||||
#tOutput {
|
||||
color: #ebcb8b; }
|
||||
color: #ebcb8b;
|
||||
}
|
||||
|
||||
#lOutput {
|
||||
color: #a3be8c; }
|
||||
color: #a3be8c;
|
||||
}
|
||||
|
||||
#sumDiv {
|
||||
text-align: center; }
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline-block;
|
||||
@@ -47,22 +58,26 @@ button {
|
||||
border: 4px solid #88c0d0;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
font-family: 'Roboto',sans-serif;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 14pt;
|
||||
color: #434c5e;
|
||||
background-color: #eceff4;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
transition: all 0.15s; }
|
||||
button:hover {
|
||||
background-color: #4c566a;
|
||||
color: #eceff4;
|
||||
border-color: #5e81ac; }
|
||||
button:focus {
|
||||
border: 4px solid #88c0d0;
|
||||
box-sizing: border-box; }
|
||||
transition: all 0.15s;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #4c566a;
|
||||
color: #eceff4;
|
||||
border-color: #5e81ac;
|
||||
}
|
||||
button:focus {
|
||||
border: 4px solid #88c0d0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#chillin {
|
||||
display: none; }
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=style.css.map */
|
||||
|
||||
@@ -1,79 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<link href="style.css" rel="stylesheet" type="text/css">
|
||||
<link href="style.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
|
||||
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
|
||||
crossorigin="anonymous"></script>
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
|
||||
<title>Travelling Sales Person</title>
|
||||
<h1>Travelling Sales Person Problem</h1>
|
||||
<button id="backButton" onclick="window.location.href='/#tutorials'">Back</button>
|
||||
<title>Travelling Sales Person</title>
|
||||
<h1>Travelling Sales Person Problem</h1>
|
||||
<button id="backButton" onclick="window.location.href = '/#tutorials'">
|
||||
Back
|
||||
</button>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<div class="pictureContainer">
|
||||
<img src="https://optimization.mccormick.northwestern.edu/images/e/ea/48StatesTSP.png" alt="TSP Problem"
|
||||
id="Conway">
|
||||
|
||||
<img
|
||||
src="https://optimization.mccormick.northwestern.edu/images/e/ea/48StatesTSP.png"
|
||||
alt="TSP Problem"
|
||||
id="Conway"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p>The travelling salesman problem (TSP) asks the following question: "Given a list of cities and the distances
|
||||
between each pair of cities, what is the shortest possible route that visits each city and returns to the origin
|
||||
city?" </p>
|
||||
<p>The problem was first formulated in 1930 and is one of the most intensively studied problems in optimization. It
|
||||
is used as a benchmark for many optimization methods. Even though the problem is computationally difficult, a
|
||||
large number of heuristics and exact algorithms are known, so that some instances with tens of thousands of
|
||||
cities can be solved completely and even problems with millions of cities can be approximated within a small
|
||||
fraction of 1%.</p>
|
||||
<p>The TSP has several applications even in its purest formulation, such as planning, logistics, and the manufacture
|
||||
of microchips. Slightly modified, it appears as a sub-problem in many areas, such as DNA sequencing. In these
|
||||
applications, the concept city represents, for example, customers, soldering points, or DNA fragments, and the
|
||||
concept distance represents travelling times or cost, or a similarity measure between DNA fragments. The TSP
|
||||
also appears in astronomy, as astronomers observing many sources will want to minimize the time spent moving the
|
||||
telescope between the sources. In many applications, additional constraints such as limited resources or time
|
||||
windows may be imposed.</p>
|
||||
<p>
|
||||
The travelling salesman problem (TSP) asks the following question: "Given
|
||||
a list of cities and the distances between each pair of cities, what is
|
||||
the shortest possible route that visits each city and returns to the
|
||||
origin city?"
|
||||
</p>
|
||||
<p>
|
||||
The problem was first formulated in 1930 and is one of the most
|
||||
intensively studied problems in optimization. It is used as a benchmark
|
||||
for many optimization methods. Even though the problem is computationally
|
||||
difficult, a large number of heuristics and exact algorithms are known, so
|
||||
that some instances with tens of thousands of cities can be solved
|
||||
completely and even problems with millions of cities can be approximated
|
||||
within a small fraction of 1%.
|
||||
</p>
|
||||
<p>
|
||||
The TSP has several applications even in its purest formulation, such as
|
||||
planning, logistics, and the manufacture of microchips. Slightly modified,
|
||||
it appears as a sub-problem in many areas, such as DNA sequencing. In
|
||||
these applications, the concept city represents, for example, customers,
|
||||
soldering points, or DNA fragments, and the concept distance represents
|
||||
travelling times or cost, or a similarity measure between DNA fragments.
|
||||
The TSP also appears in astronomy, as astronomers observing many sources
|
||||
will want to minimize the time spent moving the telescope between the
|
||||
sources. In many applications, additional constraints such as limited
|
||||
resources or time windows may be imposed.
|
||||
</p>
|
||||
<h2>These are some of the algorithms I used</h2>
|
||||
<p>Note the purple route is the best route it's found so far and the thin white lines are the routes it's trying
|
||||
real time.</p>
|
||||
</head>
|
||||
<p>
|
||||
Note the purple route is the best route it's found so far and the thin
|
||||
white lines are the routes it's trying real time.
|
||||
</p>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
<div class="canvasBody">
|
||||
<h3>Random Sort</h3>
|
||||
<span id="c1"></span>
|
||||
<p class="canvasText">This canvas sorts through random possiblities. Every frame the program chooses two random
|
||||
points (cities) and swaps them around. eg say the order was London, Paris, Madrid, the program would swap
|
||||
London and Paris so that the new order is: Paris, London, Madrid. The program then compares the distance
|
||||
against the record distance to decide whether the new order is better than the old order. This search method
|
||||
is the most inefficient way, the worst case scenario is never ending, as the point swaping is random the
|
||||
program may never reach the optimum route</p><br>
|
||||
<h3>Lexicographic Order</h3>
|
||||
<span id="c2"></span>
|
||||
<p class="canvasText">This canvas sorts through all possible orders sequentially, so after n! (where n is the
|
||||
number of points) this algorithm is guaranteed to have found the quickest possible route. However it is
|
||||
highly inefficient always taking n! frames to complete and as n increases, time taken increases
|
||||
exponentially.</p>
|
||||
<a target="_blank"
|
||||
href="https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering">Click
|
||||
here to learn more about the algorithm</a><br>
|
||||
<h3>Genetic Algorithm</h3>
|
||||
<span id="c3"></span>
|
||||
<p class="canvasText">This canvas is the most efficient at finding the quickest route, it is a mixture of the
|
||||
two methods above. It starts off by creating a population of orders, a fitness is then generated for each
|
||||
order in the population. This fitness decides how likely the order is to be picked and is based on the
|
||||
distance it takes (lower distance is better). When two orders are picked, the algorithm splices the two
|
||||
together at a random term, it's then mutated and compared against the record distance. This takes the least
|
||||
amount of time to find the shortest distance as the algorithm doesn't search through permuations that are
|
||||
obviously longer due to the order.</p><br>
|
||||
<h3>Random Sort</h3>
|
||||
<span id="c1"></span>
|
||||
<p class="canvasText">
|
||||
This canvas sorts through random possiblities. Every frame the program
|
||||
chooses two random points (cities) and swaps them around. eg say the
|
||||
order was London, Paris, Madrid, the program would swap London and Paris
|
||||
so that the new order is: Paris, London, Madrid. The program then
|
||||
compares the distance against the record distance to decide whether the
|
||||
new order is better than the old order. This search method is the most
|
||||
inefficient way, the worst case scenario is never ending, as the point
|
||||
swaping is random the program may never reach the optimum route
|
||||
</p>
|
||||
<br />
|
||||
<h3>Lexicographic Order</h3>
|
||||
<span id="c2"></span>
|
||||
<p class="canvasText">
|
||||
This canvas sorts through all possible orders sequentially, so after n!
|
||||
(where n is the number of points) this algorithm is guaranteed to have
|
||||
found the quickest possible route. However it is highly inefficient
|
||||
always taking n! frames to complete and as n increases, time taken
|
||||
increases exponentially.
|
||||
</p>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering"
|
||||
>Click here to learn more about the algorithm</a
|
||||
><br />
|
||||
<h3>Genetic Algorithm</h3>
|
||||
<span id="c3"></span>
|
||||
<p class="canvasText">
|
||||
This canvas is the most efficient at finding the quickest route, it is a
|
||||
mixture of the two methods above. It starts off by creating a population
|
||||
of orders, a fitness is then generated for each order in the population.
|
||||
This fitness decides how likely the order is to be picked and is based
|
||||
on the distance it takes (lower distance is better). When two orders are
|
||||
picked, the algorithm splices the two together at a random term, it's
|
||||
then mutated and compared against the record distance. This takes the
|
||||
least amount of time to find the shortest distance as the algorithm
|
||||
doesn't search through permuations that are obviously longer due to the
|
||||
order.
|
||||
</p>
|
||||
<br />
|
||||
</div>
|
||||
</body>
|
||||
</body>
|
||||
|
||||
<script src="sketch.js"></script>
|
||||
|
||||
<footer>
|
||||
<p>This page was inspired by The Coding Train</p><a
|
||||
href="https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw">Check him out here</a>
|
||||
</footer>
|
||||
<script src="sketch.js"></script>
|
||||
|
||||
<footer>
|
||||
<p>This page was inspired by The Coding Train</p>
|
||||
<a href="https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw"
|
||||
>Check him out here</a
|
||||
>
|
||||
</footer>
|
||||
</html>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed');
|
||||
@import url("https://fonts.googleapis.com/css?family=Roboto+Condensed");
|
||||
|
||||
a{font-family: 'Roboto Condensed', sans-serif; font-size: 18pt;}
|
||||
a {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-size: 18pt;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0 0 15px 0;
|
||||
text-align: center;
|
||||
@@ -17,89 +20,150 @@ h2 {
|
||||
}
|
||||
|
||||
@media (min-width: 350px) {
|
||||
h1 {font-size: 3.25em;}
|
||||
img{height: 40px;}
|
||||
p{font-size: 10px;}
|
||||
h2{font-size: 17px;}
|
||||
h1 {
|
||||
font-size: 3.25em;
|
||||
}
|
||||
img {
|
||||
height: 40px;
|
||||
}
|
||||
p {
|
||||
font-size: 10px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 400px) {
|
||||
h1 {font-size: 3.25em;}
|
||||
img{height: 45px;}
|
||||
p{font-size: 15px;}
|
||||
h2{font-size: 17px;}
|
||||
h1 {
|
||||
font-size: 3.25em;
|
||||
}
|
||||
img {
|
||||
height: 45px;
|
||||
}
|
||||
p {
|
||||
font-size: 15px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 440px) {
|
||||
h1 {font-size: 3.5em;}
|
||||
img {height: 100px;}
|
||||
p{font-size: 16px;}
|
||||
h2{font-size: 18px;}
|
||||
h1 {
|
||||
font-size: 3.5em;
|
||||
}
|
||||
img {
|
||||
height: 100px;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 500px) {
|
||||
h1 {font-size: 3.75em;}
|
||||
img{height: 125px;}
|
||||
p{font-size: 16px;}
|
||||
h2{font-size: 19px;}
|
||||
h1 {
|
||||
font-size: 3.75em;
|
||||
}
|
||||
img {
|
||||
height: 125px;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 19px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 630px) {
|
||||
h1 {font-size: 5em;}
|
||||
img{height: 150px;}
|
||||
p{font-size: 20px;}
|
||||
h2{font-size: 24px;}
|
||||
h1 {
|
||||
font-size: 5em;
|
||||
}
|
||||
img {
|
||||
height: 150px;
|
||||
}
|
||||
p {
|
||||
font-size: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
h1 {
|
||||
h1 {
|
||||
font-size: 5em;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
img{height: 175px;}
|
||||
p{font-size: 22px;}
|
||||
h2{font-size: 26px;}
|
||||
img {
|
||||
height: 175px;
|
||||
}
|
||||
p {
|
||||
font-size: 22px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {font-size: 8em;}
|
||||
img{height: 250px;}
|
||||
p{font-size: 24px;}
|
||||
h2{font-size: 28px;}
|
||||
h1 {
|
||||
font-size: 8em;
|
||||
}
|
||||
img {
|
||||
height: 250px;
|
||||
}
|
||||
p {
|
||||
font-size: 24px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
p{
|
||||
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
p {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
}
|
||||
|
||||
h3{
|
||||
h3 {
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
}
|
||||
|
||||
footer{
|
||||
footer {
|
||||
padding: 20px;
|
||||
background-color: #e0e0e0;
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
}
|
||||
|
||||
@keyframes dimImg{
|
||||
from {opacity: 1;
|
||||
filter: alpha(opacity=100);}
|
||||
to {opacity: 0.4;
|
||||
filter: alpha(opacity=50);}
|
||||
@keyframes dimImg {
|
||||
from {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
to {
|
||||
opacity: 0.4;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes revealText{
|
||||
from {opacity: 0.4;
|
||||
filter: alpha(opacity=50);}
|
||||
to {opacity: 1;
|
||||
filter: alpha(opacity=100);}
|
||||
@keyframes revealText {
|
||||
from {
|
||||
opacity: 0.4;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
}
|
||||
|
||||
.pictureContainer{
|
||||
float: right;
|
||||
.pictureContainer {
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pictureContainer a{
|
||||
.pictureContainer a {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
@@ -107,36 +171,36 @@ footer{
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.pictureContainer:hover img{
|
||||
|
||||
.pictureContainer:hover img {
|
||||
animation-name: dimImg;
|
||||
animation-duration: 1s;
|
||||
opacity: 0.4;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
|
||||
.pictureContainer:hover a{
|
||||
.pictureContainer:hover a {
|
||||
animation-name: revealText;
|
||||
animation-duration: 1s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.canvasText{
|
||||
.canvasText {
|
||||
margin: 0px;
|
||||
display: inline-block;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#c1, #c2, #c3{
|
||||
#c1,
|
||||
#c2,
|
||||
#c3 {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.button{
|
||||
.button {
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
@@ -156,5 +220,3 @@ footer{
|
||||
background-color: #555555;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user