formatting
This commit is contained in:
10
404.css
10
404.css
@@ -17,7 +17,7 @@ body {
|
||||
}
|
||||
|
||||
.nf-code {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: clamp(7rem, 22vw, 16rem);
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
@@ -29,7 +29,6 @@ body {
|
||||
animation: fadeUp 0.6s 0.1s forwards;
|
||||
}
|
||||
|
||||
|
||||
.nf-eyebrow {
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.2em;
|
||||
@@ -45,7 +44,7 @@ body {
|
||||
|
||||
.nf-eyebrow::before,
|
||||
.nf-eyebrow::after {
|
||||
content: '';
|
||||
content: "";
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 3rem;
|
||||
@@ -54,7 +53,7 @@ body {
|
||||
}
|
||||
|
||||
.nf-heading {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: clamp(1.6rem, 4vw, 2.8rem);
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
@@ -153,7 +152,8 @@ body {
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 100% {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
|
||||
71
404.html
71
404.html
@@ -1,23 +1,22 @@
|
||||
<!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>
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="404.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- ─── Nav ─────────────────────────────────────────────────── -->
|
||||
<nav>
|
||||
<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>
|
||||
@@ -25,18 +24,17 @@
|
||||
<li><a href="index.html#projects">projects</a></li>
|
||||
<li><a href="index.html#contact">contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- ─── 404 ──────────────────────────────────────────────────── -->
|
||||
<main class="not-found">
|
||||
</nav>
|
||||
|
||||
<!-- ─── 404 ──────────────────────────────────────────────────── -->
|
||||
<main class="not-found">
|
||||
<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.
|
||||
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">
|
||||
@@ -49,10 +47,18 @@
|
||||
<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;">
|
||||
<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;">
|
||||
<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">
|
||||
@@ -64,20 +70,17 @@
|
||||
<div class="nf-actions">
|
||||
<a href="/" class="btn btn-primary">← Back home</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- ─── Footer ─────────────────────────────────────────────────── -->
|
||||
<footer>
|
||||
<!-- ─── Footer ─────────────────────────────────────────────────── -->
|
||||
<footer>
|
||||
<span>umbra.mom — John Gatward</span>
|
||||
<span>
|
||||
Source code (self‑hosted): <a href="https://gitea.umbra.mom/jay/havox">repository</a>
|
||||
Source code (self‑hosted):
|
||||
<a href="https://gitea.umbra.mom/jay/havox">repository</a>
|
||||
</span>
|
||||
</footer>
|
||||
</footer>
|
||||
|
||||
<script src="404.js">
|
||||
</script>
|
||||
|
||||
</body>
|
||||
<script src="404.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
541
index.html
541
index.html
@@ -1,23 +1,21 @@
|
||||
<!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>Havox</title>
|
||||
<meta name="description"
|
||||
content="John Gatward's portfolio"/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<meta name="description" content="John Gatward's portfolio" />
|
||||
<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"/>
|
||||
</head>
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- ─── Nav ─────────────────────────────────────────────────── -->
|
||||
<nav>
|
||||
<body>
|
||||
<!-- ─── Nav ─────────────────────────────────────────────────── -->
|
||||
<nav>
|
||||
<a href="#hero" class="nav-logo">havox</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#about">about</a></li>
|
||||
@@ -25,130 +23,169 @@
|
||||
<li><a href="#projects">projects</a></li>
|
||||
<li><a href="#contact">contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav>
|
||||
|
||||
<!-- ─── Hero ─────────────────────────────────────────────────── -->
|
||||
<section id="hero">
|
||||
<!-- ─── Hero ─────────────────────────────────────────────────── -->
|
||||
<section id="hero">
|
||||
<p class="hero-eyebrow">// havox.org - v4</p>
|
||||
<h1 class="hero-name">John <em>Gatward</em></h1>
|
||||
<p class="hero-subtitle">Software Engineer</p>
|
||||
<p class="hero-desc">
|
||||
<span class="hero-by-line"><span class="hero-by">Backend engineer</span><span class="hero-by-role">by
|
||||
trade</span></span>
|
||||
<span class="hero-by-line"><span class="hero-by">Developer enthusiast</span><span class="hero-by-role">by
|
||||
curiosity</span></span>
|
||||
<span class="hero-by-line"><span class="hero-by">Unofficial family cloud engineer</span><span
|
||||
class="hero-by-role">by necessity</span></span>
|
||||
<span class="hero-by-line"
|
||||
><span class="hero-by">Backend engineer</span
|
||||
><span class="hero-by-role">by trade</span></span
|
||||
>
|
||||
<span class="hero-by-line"
|
||||
><span class="hero-by">Developer enthusiast</span
|
||||
><span class="hero-by-role">by curiosity</span></span
|
||||
>
|
||||
<span class="hero-by-line"
|
||||
><span class="hero-by">Unofficial family cloud engineer</span
|
||||
><span class="hero-by-role">by necessity</span></span
|
||||
>
|
||||
</p>
|
||||
<div class="hero-links">
|
||||
<a href="#projects" class="btn btn-primary">View projects</a>
|
||||
<a href="#contact" class="btn btn-ghost">Contact me</a>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- ─── About ─────────────────────────────────────────────────── -->
|
||||
<section id="about" class="about-section">
|
||||
<!-- ─── About ─────────────────────────────────────────────────── -->
|
||||
<section id="about" class="about-section">
|
||||
<div class="reveal">
|
||||
<p class="section-label">about</p>
|
||||
<h2 class="section-heading">Havox & <em>me</em></h2>
|
||||
</div>
|
||||
|
||||
<div class="about-columns">
|
||||
|
||||
<!-- Bio blocks -->
|
||||
<div class="about-grid reveal" style="transition-delay:0.05s">
|
||||
|
||||
<div class="about-grid reveal" style="transition-delay: 0.05s">
|
||||
<div class="about-block">
|
||||
<p>Havox started in 2017 as a place to dump whatever projects I had written. Four redesigns and several
|
||||
dead domains later, it's still going. Less a portfolio, more a scrapbook.</p>
|
||||
<p>
|
||||
Havox started in 2017 as a place to dump whatever projects I had
|
||||
written. Four redesigns and several dead domains later, it's still
|
||||
going. Less a portfolio, more a scrapbook.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="about-block">
|
||||
<span class="about-block-tag tag tag-blue">day job</span>
|
||||
<p>Backend engineer at <strong>Sainsbury's</strong> Supply Chain & Logistics. It's good fun.</p>
|
||||
<p>
|
||||
Backend engineer at <strong>Sainsbury's</strong> Supply Chain
|
||||
& Logistics. It's good fun.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="about-block">
|
||||
<span class="about-block-tag tag tag-teal">dev</span>
|
||||
<p>Fascinated with new languages, frameworks, and shiny tech I probably don’t need. Constantly
|
||||
learning just enough to build something slightly cooler next time.</p>
|
||||
<p>
|
||||
Fascinated with new languages, frameworks, and shiny tech I
|
||||
probably don’t need. Constantly learning just enough to build
|
||||
something slightly cooler next time.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="about-block">
|
||||
<span class="about-block-tag tag tag-yellow">linux</span>
|
||||
<p>Long-time Linux user and distro-hopper. Self-hosted server running 24/7 for friends and family who
|
||||
didn't ask for it but <s>probably</s> definitely appreciate it.</p>
|
||||
<p>
|
||||
Long-time Linux user and distro-hopper. Self-hosted server running
|
||||
24/7 for friends and family who didn't ask for it but
|
||||
<s>probably</s> definitely appreciate it.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Version timeline -->
|
||||
<div class="reveal" style="transition-delay:0.15s">
|
||||
<div class="reveal" style="transition-delay: 0.15s">
|
||||
<p class="section-label">version history</p>
|
||||
<div class="version-timeline">
|
||||
|
||||
<div class="version-item">
|
||||
<div class="v-dot v-dot--current"></div>
|
||||
<div class="v-content">
|
||||
<div class="v-title">Havox V4 <span class="v-current-badge">current</span></div>
|
||||
<div class="v-title">
|
||||
Havox V4 <span class="v-current-badge">current</span>
|
||||
</div>
|
||||
<div class="v-year">2026 → present</div>
|
||||
<div class="v-desc">Catppuccin Mocha. Single page. Professional.</div>
|
||||
<div class="v-desc">
|
||||
Catppuccin Mocha. Single page. Professional.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="version-item version-item--link" href="archive/v3/index.html">
|
||||
<a
|
||||
class="version-item version-item--link"
|
||||
href="archive/v3/index.html"
|
||||
>
|
||||
<div class="v-dot"></div>
|
||||
<div class="v-content">
|
||||
<div class="v-title">Havox V3 <span class="v-link-arrow">↗</span></div>
|
||||
<div class="v-title">
|
||||
Havox V3 <span class="v-link-arrow">↗</span>
|
||||
</div>
|
||||
<div class="v-year">2022 → 2025</div>
|
||||
<div class="v-desc">Borrowed a professional template and tried to make it suit my needs. Wasn't
|
||||
very 'me'.
|
||||
<div class="v-desc">
|
||||
Borrowed a professional template and tried to make it suit my
|
||||
needs. Wasn't very 'me'.
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="version-item version-item--link" href="archive/v2/index.html">
|
||||
<a
|
||||
class="version-item version-item--link"
|
||||
href="archive/v2/index.html"
|
||||
>
|
||||
<div class="v-dot"></div>
|
||||
<div class="v-content">
|
||||
<div class="v-title">Umbra.cyou <span class="v-link-arrow">↗</span></div>
|
||||
<div class="v-title">
|
||||
Umbra.cyou <span class="v-link-arrow">↗</span>
|
||||
</div>
|
||||
<div class="v-year">2019 → 2022</div>
|
||||
<div class="v-desc">Built from the ground up focusing on a minimal clean design. Stole my code
|
||||
VS Code theme for a colour palette.
|
||||
<div class="v-desc">
|
||||
Built from the ground up focusing on a minimal clean design.
|
||||
Stole my code VS Code theme for a colour palette.
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="version-item version-item--link" href="archive/v1/index.html">
|
||||
<a
|
||||
class="version-item version-item--link"
|
||||
href="archive/v1/index.html"
|
||||
>
|
||||
<div class="v-dot"></div>
|
||||
<div class="v-content">
|
||||
<div class="v-title">Havox.org <span class="v-link-arrow">↗</span></div>
|
||||
<div class="v-title">
|
||||
Havox.org <span class="v-link-arrow">↗</span>
|
||||
</div>
|
||||
<div class="v-year">2016 → 2019</div>
|
||||
<div class="v-desc">Wrote the entire site in one php file. Hosted on a £1/year VPS.
|
||||
<div class="v-desc">
|
||||
Wrote the entire site in one php file. Hosted on a £1/year
|
||||
VPS.
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- ─── Skills ─────────────────────────────────────────────────── -->
|
||||
<section id="skills">
|
||||
<!-- ─── Skills ─────────────────────────────────────────────────── -->
|
||||
<section id="skills">
|
||||
<p class="section-label reveal">skills</p>
|
||||
<h2 class="section-heading reveal" style="transition-delay:0.05s">What I <em>know</em></h2>
|
||||
<h2 class="section-heading reveal" style="transition-delay: 0.05s">
|
||||
What I <em>know</em>
|
||||
</h2>
|
||||
|
||||
<div class="skills-grid">
|
||||
<div class="skill-card reveal">
|
||||
<span class="skill-card-icon">☕</span>
|
||||
<h3>Backend</h3>
|
||||
<p>Professional experience designing, building and deploying services. Using a modern tech stack in the
|
||||
Supply Chain & Logistics industry.</p>
|
||||
<p>
|
||||
Professional experience designing, building and deploying services.
|
||||
Using a modern tech stack in the Supply Chain & Logistics industry.
|
||||
</p>
|
||||
<div class="tag-row">
|
||||
<span class="tag tag-blue">Java</span>
|
||||
<span class="tag tag-green">Spring Boot</span>
|
||||
@@ -157,22 +194,26 @@
|
||||
<span class="tag tag-sky">MongoDB</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card reveal" style="transition-delay:0.08s">
|
||||
<div class="skill-card reveal" style="transition-delay: 0.08s">
|
||||
<span class="skill-card-icon">🎨</span>
|
||||
<h3>Frontend & Creative</h3>
|
||||
<p>Comfortable with React and CSS. I've always enjoyed visualising algorithms interactively - it's way more
|
||||
fun than a console output.</p>
|
||||
<p>
|
||||
Comfortable with React and CSS. I've always enjoyed visualising
|
||||
algorithms interactively - it's way more fun than a console output.
|
||||
</p>
|
||||
<div class="tag-row">
|
||||
<span class="tag tag-teal">React</span>
|
||||
<span class="tag tag-green">TypeScript</span>
|
||||
<span class="tag tag-blue">HTML/CSS</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card reveal" style="transition-delay:0.16s">
|
||||
<div class="skill-card reveal" style="transition-delay: 0.16s">
|
||||
<span class="skill-card-icon">🖥</span>
|
||||
<h3>Systems & Infra</h3>
|
||||
<p>Long-term Linux & Vim user. Self-hosted web & media servers. Using industry practices when it comes to
|
||||
networks & security.</p>
|
||||
<p>
|
||||
Long-term Linux & Vim user. Self-hosted web & media servers. Using
|
||||
industry practices when it comes to networks & security.
|
||||
</p>
|
||||
<div class="tag-row">
|
||||
<span class="tag tag-green">Linux</span>
|
||||
<span class="tag tag-teal">Docker</span>
|
||||
@@ -180,12 +221,17 @@
|
||||
<span class="tag tag-peach">Wireguard</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card skill-card--wide reveal" style="transition-delay:0.24s">
|
||||
<div
|
||||
class="skill-card skill-card--wide reveal"
|
||||
style="transition-delay: 0.24s"
|
||||
>
|
||||
<span class="skill-card-icon">🎓</span>
|
||||
<h3>Academic Breadth</h3>
|
||||
<p>C, C++, x86_64 assembly, Haskell, R & MatLab from uni. Modules in Advanced networking,
|
||||
algorithms, compilers,
|
||||
graphics, cryptography, malware analysis & many more.</p>
|
||||
<p>
|
||||
C, C++, x86_64 assembly, Haskell, R & MatLab from uni. Modules
|
||||
in Advanced networking, algorithms, compilers, graphics,
|
||||
cryptography, malware analysis & many more.
|
||||
</p>
|
||||
<div class="tag-row">
|
||||
<span class="tag tag-blue">C / C++</span>
|
||||
<span class="tag tag-mauve">Haskell</span>
|
||||
@@ -195,28 +241,38 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- ─── Projects ──────────────────────────────────────────────── -->
|
||||
<section id="projects">
|
||||
<!-- ─── Projects ──────────────────────────────────────────────── -->
|
||||
<section id="projects">
|
||||
<p class="section-label reveal">projects</p>
|
||||
<h2 class="section-heading reveal" style="transition-delay:0.05s">Things I <em>built</em></h2>
|
||||
<p class="section-intro reveal" style="transition-delay:0.1s">
|
||||
A timeline of my projects and an overview of my journey as a developer. Often times getting inspired and trying
|
||||
to recreate something cool I saw.
|
||||
<h2 class="section-heading reveal" style="transition-delay: 0.05s">
|
||||
Things I <em>built</em>
|
||||
</h2>
|
||||
<p class="section-intro reveal" style="transition-delay: 0.1s">
|
||||
A timeline of my projects and an overview of my journey as a developer.
|
||||
Often times getting inspired and trying to recreate something cool I
|
||||
saw.
|
||||
</p>
|
||||
|
||||
<div class="projects-subheading reveal" style="transition-delay:0.12s">
|
||||
<div class="projects-subheading reveal" style="transition-delay: 0.12s">
|
||||
<span>Featured projects</span>
|
||||
</div>
|
||||
|
||||
<div class="card-grid card-grid--featured reveal" style="transition-delay:0.14s">
|
||||
<div
|
||||
class="card-grid card-grid--featured reveal"
|
||||
style="transition-delay: 0.14s"
|
||||
>
|
||||
<a class="card" href="https://wordlesolver.umbra.mom">
|
||||
<span class="card-date">9 Feb 2025</span>
|
||||
<span class="card-title">Wordle Solver</span>
|
||||
<span class="card-desc">Built after one too many missed 3-guess games. Now it plays marginally better than me. Uses information theory to recommend the next best guess.</span>
|
||||
<span class="card-desc"
|
||||
>Built after one too many missed 3-guess games. Now it plays
|
||||
marginally better than me. Uses information theory to recommend the
|
||||
next best guess.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-yellow">Rust</span>
|
||||
<span class="tag tag-teal">React</span>
|
||||
@@ -227,7 +283,10 @@
|
||||
<a class="card" href="https://crackthequote.umbra.mom">
|
||||
<span class="card-date">13 Apr 2024</span>
|
||||
<span class="card-title">Crack the Quote</span>
|
||||
<span class="card-desc">A substitution-cipher puzzle game with a daily challenge. Built for me and my family to play after getting bored with NYT games.</span>
|
||||
<span class="card-desc"
|
||||
>A substitution-cipher puzzle game with a daily challenge. Built for
|
||||
me and my family to play after getting bored with NYT games.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-yellow">Rust</span>
|
||||
<span class="tag tag-teal">React</span>
|
||||
@@ -237,37 +296,73 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="projects-subheading reveal" style="transition-delay:0.16s">
|
||||
<div class="projects-subheading reveal" style="transition-delay: 0.16s">
|
||||
<span>All projects</span>
|
||||
</div>
|
||||
|
||||
<fieldset class="project-filters reveal" style="transition-delay:0.18s" aria-label="Filter projects by tech stack">
|
||||
<button type="button" class="filter-chip active" data-filter="all">All</button>
|
||||
<button type="button" class="filter-chip" data-filter="javascript">JavaScript</button>
|
||||
<button type="button" class="filter-chip" data-filter="python">Python</button>
|
||||
<button type="button" class="filter-chip" data-filter="rust">Rust</button>
|
||||
<button type="button" class="filter-chip" data-filter="react">React</button>
|
||||
<button type="button" class="filter-chip" data-filter="webassembly">WebAssembly</button>
|
||||
<button type="button" class="filter-chip" data-filter="p5">p5.js</button>
|
||||
<fieldset
|
||||
class="project-filters reveal"
|
||||
style="transition-delay: 0.18s"
|
||||
aria-label="Filter projects by tech stack"
|
||||
>
|
||||
<button type="button" class="filter-chip active" data-filter="all">
|
||||
All
|
||||
</button>
|
||||
<button type="button" class="filter-chip" data-filter="javascript">
|
||||
JavaScript
|
||||
</button>
|
||||
<button type="button" class="filter-chip" data-filter="python">
|
||||
Python
|
||||
</button>
|
||||
<button type="button" class="filter-chip" data-filter="rust">
|
||||
Rust
|
||||
</button>
|
||||
<button type="button" class="filter-chip" data-filter="react">
|
||||
React
|
||||
</button>
|
||||
<button type="button" class="filter-chip" data-filter="webassembly">
|
||||
WebAssembly
|
||||
</button>
|
||||
<button type="button" class="filter-chip" data-filter="p5">
|
||||
p5.js
|
||||
</button>
|
||||
<button type="button" class="filter-chip" data-filter="zig">Zig</button>
|
||||
<button type="button" class="filter-chip" data-filter="cpp">C / C++</button>
|
||||
<button type="button" class="filter-chip" data-filter="cpp">
|
||||
C / C++
|
||||
</button>
|
||||
</fieldset>
|
||||
|
||||
<div id="project-grid" class="card-grid reveal" style="transition-delay:0.2s">
|
||||
<article class="card project-card card-link" data-tech="python" data-href="https://pubquiz.umbra.mom">
|
||||
<div
|
||||
id="project-grid"
|
||||
class="card-grid reveal"
|
||||
style="transition-delay: 0.2s"
|
||||
>
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="python"
|
||||
data-href="https://pubquiz.umbra.mom"
|
||||
>
|
||||
<span class="card-date">03 Apr 2025</span>
|
||||
<span class="card-title">Pub Quiz Dashboard</span>
|
||||
<span class="card-desc">A Python dashboard to track our local pub quiz performances.</span>
|
||||
<span class="card-desc"
|
||||
>A Python dashboard to track our local pub quiz performances.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-red">Python</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="cpp webassembly"
|
||||
data-href="projects/thin_ice/thin_ice.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="cpp webassembly"
|
||||
data-href="projects/thin_ice/thin_ice.html"
|
||||
>
|
||||
<span class="card-date">14 Nov 2024</span>
|
||||
<span class="card-title">Thin Ice</span>
|
||||
<span class="card-desc">A small browser tile game inspired by one I played growing up.</span>
|
||||
<span class="card-desc"
|
||||
>A small browser tile game inspired by one I played growing
|
||||
up.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-blue">C++</span>
|
||||
<span class="tag tag-teal">WebAssembly</span>
|
||||
@@ -275,10 +370,18 @@
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="zig webassembly" data-href="projects/tsp/index.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="zig webassembly"
|
||||
data-href="projects/tsp/index.html"
|
||||
>
|
||||
<span class="card-date">20 Mar 2024</span>
|
||||
<span class="card-title">Travelling Salesman Problem</span>
|
||||
<span class="card-desc">A nearest-neighbor + 2-opt visualiser. Wanted to revisit one of my first projects but with a super optimised tech-stack & algorithm.</span>
|
||||
<span class="card-desc"
|
||||
>A nearest-neighbor + 2-opt visualiser. Wanted to revisit one of my
|
||||
first projects but with a super optimised tech-stack &
|
||||
algorithm.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-yellow">Zig</span>
|
||||
<span class="tag tag-sky">WebGL</span>
|
||||
@@ -286,43 +389,74 @@
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="rust" data-href="projects/flocking/index.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="rust"
|
||||
data-href="projects/flocking/index.html"
|
||||
>
|
||||
<span class="card-date">31 Jan 2024</span>
|
||||
<span class="card-title">Flocking</span>
|
||||
<span class="card-desc">Rewrote the entire project in Rust from GoLang to compile to wasm. Was fun being able to use DOM controls to affect the simulation.</span>
|
||||
<span class="card-desc"
|
||||
>Rewrote the entire project in Rust from GoLang to compile to wasm.
|
||||
Was fun being able to use DOM controls to affect the
|
||||
simulation.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-yellow">Rust</span>
|
||||
<span class="tag tag-peach">Raylib</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript" data-href="https://samstoreymusic.com">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript"
|
||||
data-href="https://samstoreymusic.com"
|
||||
>
|
||||
<span class="card-date">1 Nov 2023</span>
|
||||
<span class="card-title">samstoreymusic.com</span>
|
||||
<span class="card-desc">A website design and build for a friend working in music. Saving him the hosting fees :)</span>
|
||||
<span class="card-desc"
|
||||
>A website design and build for a friend working in music. Saving
|
||||
him the hosting fees :)</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-mauve">HTML/CSS</span>
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript react"
|
||||
data-href="projects/cellular_automata/index.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript react"
|
||||
data-href="projects/cellular_automata/index.html"
|
||||
>
|
||||
<span class="card-date">21 Jul 2023</span>
|
||||
<span class="card-title">Game of Life</span>
|
||||
<span class="card-desc">After building Conway's Game of Life in p5.js as one of my first projects, I expanded it into a generic automata simulator.</span>
|
||||
<span class="card-desc"
|
||||
>After building Conway's Game of Life in p5.js as one of my first
|
||||
projects, I expanded it into a generic automata simulator.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-teal">React</span>
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="cpp webassembly"
|
||||
data-href="projects/percolation/index.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="cpp webassembly"
|
||||
data-href="projects/percolation/index.html"
|
||||
>
|
||||
<span class="card-date">5 Feb 2022</span>
|
||||
<span class="card-title">Percolation</span>
|
||||
<span class="card-desc">Watched a brilliant video by <a target="_blank"
|
||||
href="https://youtu.be/a-767WnbaCQ?si=f3NM95INeoZqzIi8">Spectral Collective</a> and just had to recreate it.</span>
|
||||
<span class="card-desc"
|
||||
>Watched a brilliant video by
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://youtu.be/a-767WnbaCQ?si=f3NM95INeoZqzIi8"
|
||||
>Spectral Collective</a
|
||||
>
|
||||
and just had to recreate it.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-blue">C</span>
|
||||
<span class="tag tag-peach">RayLib</span>
|
||||
@@ -330,89 +464,148 @@
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5"
|
||||
data-href="projects/cubic_bezier_curve/index.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/cubic_bezier_curve/index.html"
|
||||
>
|
||||
<span class="card-date">1 Oct 2021</span>
|
||||
<span class="card-title">Drawing Bézier curves</span>
|
||||
<span class="card-desc">An interactive animation for how cubic Bézier curves are constructed. Inspirated by this beautiful video by <a
|
||||
target="_blank" href="https://youtu.be/aVwxzDHniEw?si=mCylw_p-fMSdhyvN">Fraya Holmér</a></span>
|
||||
<span class="card-desc"
|
||||
>An interactive animation for how cubic Bézier curves are
|
||||
constructed. Inspirated by this beautiful video by
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://youtu.be/aVwxzDHniEw?si=mCylw_p-fMSdhyvN"
|
||||
>Fraya Holmér</a
|
||||
></span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5"
|
||||
data-href="projects/marching_squares.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/marching_squares.html"
|
||||
>
|
||||
<span class="card-date">17 Sep 2021</span>
|
||||
<span class="card-title">2D Marching Squares</span>
|
||||
<span class="card-desc">I saw the 3D version of this algorithm in a game dev log and was inspired to create this 2d land/sea map generator thing.</span>
|
||||
<span class="card-desc"
|
||||
>I saw the 3D version of this algorithm in a game dev log and was
|
||||
inspired to create this 2d land/sea map generator thing.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5" data-href="projects/warping_lines.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/warping_lines.html"
|
||||
>
|
||||
<span class="card-date">2 Apr 2020</span>
|
||||
<span class="card-title">Müller-Lyer illusion</span>
|
||||
<span class="card-desc">JavaScript visualisation of the Müller-Lyer optical illusion.</span>
|
||||
<span class="card-desc"
|
||||
>JavaScript visualisation of the Müller-Lyer optical illusion.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5" data-href="projects/fourier_series.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/fourier_series.html"
|
||||
>
|
||||
<span class="card-date">27 Feb 2019</span>
|
||||
<span class="card-title">Fourier series</span>
|
||||
<span class="card-desc">Builds a square wave from sine components to show Fourier series in motion.</span>
|
||||
<span class="card-desc"
|
||||
>Builds a square wave from sine components to show Fourier series in
|
||||
motion.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5"
|
||||
data-href="projects/ellipse_construction.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/ellipse_construction.html"
|
||||
>
|
||||
<span class="card-date">23 Feb 2019</span>
|
||||
<span class="card-title">Constructing an ellipse</span>
|
||||
<span class="card-desc">A geometric construction demo inspired by <a target="_blank"
|
||||
href="https://youtu.be/xdIjYBtnvZU?si=uI66LS_O_eAWqG4e">3Blue1Brown's video</a> on Feynman's lost lecture.</span>
|
||||
<span class="card-desc"
|
||||
>A geometric construction demo inspired by
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://youtu.be/xdIjYBtnvZU?si=uI66LS_O_eAWqG4e"
|
||||
>3Blue1Brown's video</a
|
||||
>
|
||||
on Feynman's lost lecture.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5"
|
||||
data-href="projects/pi_approximation.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/pi_approximation.html"
|
||||
>
|
||||
<span class="card-date">18 Mar 2018</span>
|
||||
<span class="card-title">Calculating PI</span>
|
||||
<span class="card-desc">A Monte Carlo approximation of pi using random sampling.</span>
|
||||
<span class="card-desc"
|
||||
>A Monte Carlo approximation of pi using random sampling.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5"
|
||||
data-href="projects/oscillations_in_3d.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/oscillations_in_3d.html"
|
||||
>
|
||||
<span class="card-date">17 Dec 2017</span>
|
||||
<span class="card-title">Oscillations in 3D</span>
|
||||
<span class="card-desc">A JavaScript recreation of a Bees and Bombs animation.</span>
|
||||
<span class="card-desc"
|
||||
>A JavaScript recreation of a Bees and Bombs animation.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
</span>
|
||||
<span class="card-arrow">→</span>
|
||||
</article>
|
||||
<article class="card project-card card-link" data-tech="javascript p5"
|
||||
data-href="projects/maze_generation/index.html">
|
||||
<article
|
||||
class="card project-card card-link"
|
||||
data-tech="javascript p5"
|
||||
data-href="projects/maze_generation/index.html"
|
||||
>
|
||||
<span class="card-date">13 Nov 2017</span>
|
||||
<span class="card-title">Maze generator</span>
|
||||
<span class="card-desc">Discovered the <a target="_blank"
|
||||
href="https://youtu.be/HyK_Q5rrcr4?si=G6g8xDHK0cc_MwhG">Coding Train's channel</a> and learnt coding through following along.</span>
|
||||
<span class="card-desc"
|
||||
>Discovered the
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://youtu.be/HyK_Q5rrcr4?si=G6g8xDHK0cc_MwhG"
|
||||
>Coding Train's channel</a
|
||||
>
|
||||
and learnt coding through following along.</span
|
||||
>
|
||||
<span class="tag-row">
|
||||
<span class="tag tag-green">JavaScript</span>
|
||||
<span class="tag tag-red">p5.js</span>
|
||||
@@ -421,51 +614,73 @@
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="projects-more-row reveal" style="transition-delay:0.22s">
|
||||
<button id="projects-see-more" type="button" class="projects-more-btn" aria-expanded="false">See more</button>
|
||||
<div class="projects-more-row reveal" style="transition-delay: 0.22s">
|
||||
<button
|
||||
id="projects-see-more"
|
||||
type="button"
|
||||
class="projects-more-btn"
|
||||
aria-expanded="false"
|
||||
>
|
||||
See more
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- ─── Contact ───────────────────────────────────────────────── -->
|
||||
<section id="contact">
|
||||
<!-- ─── Contact ───────────────────────────────────────────────── -->
|
||||
<section id="contact">
|
||||
<p class="section-label reveal">contact</p>
|
||||
<div class="contact-inner reveal" style="transition-delay:0.1s">
|
||||
<div class="contact-inner reveal" style="transition-delay: 0.1s">
|
||||
<div>
|
||||
<h2>Let's <em>talk</em></h2>
|
||||
<p>
|
||||
I am currently employed and open to the right software engineering opportunity, particularly
|
||||
backend or full-stack roles with friendly, close-knit teams.
|
||||
</p>
|
||||
<p>
|
||||
For professional enquiries, please contact me on LinkedIn.
|
||||
I am currently employed and open to the right software engineering
|
||||
opportunity, particularly backend or full-stack roles with friendly,
|
||||
close-knit teams.
|
||||
</p>
|
||||
<p>For professional enquiries, please contact me on LinkedIn.</p>
|
||||
</div>
|
||||
<div class="contact-links">
|
||||
<a href="https://github.com/jayo60013" class="contact-link" target="_blank" rel="noopener">
|
||||
<span class="contact-link-icon contact-link-icon--github" aria-hidden="true"></span>
|
||||
<a
|
||||
href="https://github.com/jayo60013"
|
||||
class="contact-link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<span
|
||||
class="contact-link-icon contact-link-icon--github"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="contact-link-label">GitHub</span>
|
||||
<span class="contact-link-handle">jayo60013</span>
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/in/jaygatward" class="contact-link" target="_blank" rel="noopener">
|
||||
<span class="contact-link-icon contact-link-icon--linkedin" aria-hidden="true"></span>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/jaygatward"
|
||||
class="contact-link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<span
|
||||
class="contact-link-icon contact-link-icon--linkedin"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="contact-link-label">LinkedIn</span>
|
||||
<span class="contact-link-handle">jaygatward</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<!-- ─── Footer ─────────────────────────────────────────────────── -->
|
||||
<footer>
|
||||
<!-- ─── Footer ─────────────────────────────────────────────────── -->
|
||||
<footer>
|
||||
<span>umbra.mom - John Gatward</span>
|
||||
<span>
|
||||
Source code (self‑hosted): <a href="https://gitea.umbra.mom/jay/havox">repository</a>
|
||||
Source code (self‑hosted):
|
||||
<a href="https://gitea.umbra.mom/jay/havox">repository</a>
|
||||
</span>
|
||||
</footer>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</footer>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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,6 +1,5 @@
|
||||
<DOCTYPE! html>
|
||||
<html>
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
|
||||
</head>
|
||||
@@ -29,8 +28,14 @@
|
||||
<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>.
|
||||
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>
|
||||
|
||||
@@ -38,7 +43,6 @@
|
||||
<h3 id="percentage"></h3>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
class dot {
|
||||
constructor(x, y) {
|
||||
@@ -63,21 +67,19 @@
|
||||
stroke(75, 75, 215);
|
||||
strokeWeight(3);
|
||||
noFill();
|
||||
ellipse(0, 0, width - (0.0625 * width), height - (0.0625 * height));
|
||||
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;
|
||||
@@ -89,7 +91,7 @@
|
||||
}
|
||||
|
||||
function getCirclePoints() {
|
||||
var r = (width - (0.0625 * width)) / 2;
|
||||
var r = (width - 0.0625 * width) / 2;
|
||||
var step = 1 / numericValue;
|
||||
var index = 0;
|
||||
for (var i = 0; i < TWO_PI; i += step) {
|
||||
@@ -125,19 +127,27 @@
|
||||
var cy = (y1 + y2) / 2;
|
||||
|
||||
//move line to the center on the origin
|
||||
x1 -= cx; y1 -= cy;
|
||||
x2 -= cx; y2 -= cy;
|
||||
x1 -= cx;
|
||||
y1 -= cy;
|
||||
x2 -= cx;
|
||||
y2 -= cy;
|
||||
|
||||
//rotate both points
|
||||
xtemp = x1; ytemp = y1;
|
||||
x1 = -ytemp; y1 = xtemp;
|
||||
xtemp = x1;
|
||||
ytemp = y1;
|
||||
x1 = -ytemp;
|
||||
y1 = xtemp;
|
||||
|
||||
xtemp = x2; ytemp = y2;
|
||||
x2 = -ytemp; y2 = 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;
|
||||
x1 += cx;
|
||||
y1 += cy;
|
||||
x2 += cx;
|
||||
y2 += cy;
|
||||
stroke(255, 0, 0);
|
||||
line(x1, y1, x2, y2);
|
||||
stroke(255);
|
||||
@@ -148,11 +158,10 @@
|
||||
do {
|
||||
ux = random(width);
|
||||
uy = random(height);
|
||||
if (getDist(0, ux, 0, uy) <= (width - (0.0625 * width)) / 2) {
|
||||
if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
|
||||
pointOk = true;
|
||||
}
|
||||
}
|
||||
while (!pointOk);
|
||||
} while (!pointOk);
|
||||
|
||||
point(ux, uy);
|
||||
getCirclePoints();
|
||||
@@ -165,11 +174,10 @@
|
||||
do {
|
||||
ux = random(width);
|
||||
uy = random(height);
|
||||
if (getDist(0, ux, 0, uy) <= (width - (0.0625 * width)) / 2) {
|
||||
if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
|
||||
pointOk = true;
|
||||
}
|
||||
}
|
||||
while (!pointOk);
|
||||
} while (!pointOk);
|
||||
|
||||
point(ux, uy);
|
||||
getCirclePoints();
|
||||
@@ -177,8 +185,7 @@
|
||||
}
|
||||
|
||||
function getDist(x1, x2, y1, y2) {
|
||||
return Math.sqrt((x1 - x2) ^ 2 + (y1 - y2) ^ 2);
|
||||
return Math.sqrt((x1 - x2) ^ (2 + (y1 - y2)) ^ 2);
|
||||
}
|
||||
</script>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
<!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>
|
||||
<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">
|
||||
<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>
|
||||
@@ -26,7 +41,14 @@
|
||||
|
||||
<div class="control-group">
|
||||
<label for="cohesionSlider">Cohesion</label>
|
||||
<input type="range" id="cohesionSlider" min="0" max="5" step="0.1" value="0.9">
|
||||
<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>
|
||||
@@ -35,7 +57,14 @@
|
||||
|
||||
<div class="control-group">
|
||||
<label for="separationSlider">Separation</label>
|
||||
<input type="range" id="separationSlider" min="0" max="5" step="0.1" value="1.4">
|
||||
<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>
|
||||
@@ -73,6 +102,5 @@
|
||||
</div>
|
||||
|
||||
<script type="module" src="script.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!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" />
|
||||
@@ -31,20 +30,20 @@
|
||||
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.
|
||||
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>
|
||||
@@ -117,6 +116,5 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
<!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;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
Segoe UI,
|
||||
Roboto,
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
margin: 16px;
|
||||
background: #fafafa;
|
||||
color: #222;
|
||||
@@ -34,23 +46,21 @@
|
||||
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.
|
||||
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>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.
|
||||
<b>Right‑click</b> shows or hides the contour lines produced by the
|
||||
algorithm.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -68,8 +78,7 @@
|
||||
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() {
|
||||
@@ -88,17 +97,24 @@
|
||||
}
|
||||
|
||||
for (var i = 0; i < cols * rows; i++) {
|
||||
if (i + cols + 1 > cols * rows) {break;}
|
||||
if (i + cols + 1 > cols * rows) {
|
||||
break;
|
||||
}
|
||||
|
||||
var x = (i % cols) * reso;
|
||||
var y = (Math.floor(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]);
|
||||
var s = getState(
|
||||
field[i],
|
||||
field[i + 1],
|
||||
field[i + cols + 1],
|
||||
field[i + cols],
|
||||
);
|
||||
noStroke();
|
||||
|
||||
switch (s) {
|
||||
@@ -162,8 +178,10 @@
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
if (draw_lines) {drawLines(s, a, b, c, d);}
|
||||
}
|
||||
if (draw_lines) {
|
||||
drawLines(s, a, b, c, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,26 +193,32 @@
|
||||
stroke(232, 229, 55);
|
||||
strokeWeight(reso * 0.1);
|
||||
switch (s) {
|
||||
case 1: case 14:
|
||||
case 1:
|
||||
case 14:
|
||||
line(c[0], c[1], d[0], d[1]);
|
||||
break;
|
||||
case 2: case 13:
|
||||
case 2:
|
||||
case 13:
|
||||
line(b[0], b[1], c[0], c[1]);
|
||||
break;
|
||||
case 3: case 12:
|
||||
case 3:
|
||||
case 12:
|
||||
line(b[0], b[1], d[0], d[1]);
|
||||
break;
|
||||
case 4: case 11:
|
||||
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:
|
||||
case 6:
|
||||
case 9:
|
||||
line(a[0], a[1], c[0], c[1]);
|
||||
break;
|
||||
case 7: case 8:
|
||||
case 7:
|
||||
case 8:
|
||||
line(a[0], a[1], d[0], d[1]);
|
||||
break;
|
||||
case 10:
|
||||
@@ -203,7 +227,7 @@
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
noStroke();
|
||||
}
|
||||
|
||||
@@ -219,6 +243,5 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<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;
|
||||
@@ -32,21 +37,16 @@
|
||||
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,12 +1,12 @@
|
||||
<!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>
|
||||
|
||||
@@ -17,15 +17,18 @@
|
||||
|
||||
<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.
|
||||
<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.
|
||||
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>
|
||||
@@ -33,11 +36,17 @@
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<p>Canvas</p>
|
||||
<button id="fullscreenButton" class="action-button" type="button">Fullscreen</button>
|
||||
<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>
|
||||
<canvas
|
||||
id="canvas"
|
||||
aria-label="Percolation simulation canvas"
|
||||
oncontextmenu="event.preventDefault()"
|
||||
></canvas>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status">Downloading...</div>
|
||||
@@ -53,13 +62,13 @@
|
||||
<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.
|
||||
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>
|
||||
@@ -69,5 +78,5 @@
|
||||
|
||||
<script src="script.js"></script>
|
||||
<script async src="index.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<!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;
|
||||
@@ -24,25 +23,23 @@
|
||||
p {
|
||||
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;
|
||||
@@ -56,7 +53,7 @@
|
||||
background(0);
|
||||
stroke(255, 0, 0);
|
||||
strokeWeight(3);
|
||||
noFill()
|
||||
noFill();
|
||||
translate(width / 2, height / 2);
|
||||
rectMode(CENTER);
|
||||
rect(0, 0, r * 2, r * 2);
|
||||
@@ -66,19 +63,18 @@
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
translate(width / 2, height / 2);
|
||||
x = random(-r, r)
|
||||
y = random(-r, r)
|
||||
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)
|
||||
var d = dist(x, y, 0, 0);
|
||||
if (d < r) {
|
||||
circleCount += 1
|
||||
stroke(0, 0, 255)
|
||||
circleCount += 1;
|
||||
stroke(0, 0, 255);
|
||||
}
|
||||
|
||||
point(x, y);
|
||||
@@ -93,12 +89,11 @@
|
||||
|
||||
var ErrorDisplay = document.getElementById("percentage");
|
||||
var DisplayError = "a";
|
||||
var Perror = (calcPI - PI) / PI
|
||||
var Perror = (calcPI - PI) / PI;
|
||||
Perror *= 100;
|
||||
DisplayError = nfc(Perror, 10);
|
||||
ErrorDisplay.innerHTML = DisplayError + '%';
|
||||
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 = [];
|
||||
@@ -16,14 +16,12 @@
|
||||
strokeWeight(4);
|
||||
colorMode(HSB);
|
||||
|
||||
|
||||
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);
|
||||
@@ -31,13 +29,11 @@
|
||||
var pos = createVector(x, y);
|
||||
grid[i + j * cols] = pos;
|
||||
active.push(pos);
|
||||
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(0);
|
||||
|
||||
|
||||
for (var total = 0; total < 30; total++) {
|
||||
if (active.length > 0) {
|
||||
var randIndex = floor(random(active.length));
|
||||
@@ -52,11 +48,17 @@
|
||||
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]) {
|
||||
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 index = col + i + (row + j) * cols;
|
||||
var neighbor = grid[index];
|
||||
if (neighbor) {
|
||||
var d = p5.Vector.dist(sample, neighbor);
|
||||
@@ -80,12 +82,10 @@
|
||||
if (!found) {
|
||||
active.splice(randIndex, 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < ordered.length; i++) {
|
||||
|
||||
stroke(255, 0, 255);
|
||||
strokeWeight(r * 0.5);
|
||||
point(ordered[i].x, ordered[i].y);
|
||||
@@ -99,6 +99,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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." />
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/#projects" class="back-button">← Back</a>
|
||||
|
||||
@@ -18,11 +21,13 @@
|
||||
|
||||
<section>
|
||||
<p>
|
||||
You start on safe ice, breaking ice where you walk, trying to reach the finish tile before trapping yourself.
|
||||
You start on safe ice, breaking ice where you walk, trying to reach
|
||||
the finish tile before trapping yourself.
|
||||
</p>
|
||||
<div class="info-box">
|
||||
<p>
|
||||
See if you can get a gold medal on all 6 levels by breaking every single tile of ice!
|
||||
See if you can get a gold medal on all 6 levels by breaking every
|
||||
single tile of ice!
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
@@ -30,16 +35,30 @@
|
||||
<div class="canvas-container">
|
||||
<div class="canvas-toolbar">
|
||||
<p>Game</p>
|
||||
<button id="fullscreenButton" class="action-button" type="button">Fullscreen</button>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
@@ -68,5 +87,5 @@
|
||||
|
||||
<script src="script.js"></script>
|
||||
<script async src="thin_ice.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,32 +1,44 @@
|
||||
<!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>
|
||||
|
||||
<header>
|
||||
<h1>Travelling Salesman Problem</h1>
|
||||
<p class="subtitle">An interactive route visualiser built with WebGL and WebAssembly.</p>
|
||||
<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.
|
||||
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.
|
||||
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>
|
||||
Drag points around the canvas and watch the route update in real time.
|
||||
Drag points around the canvas and watch the route update in real
|
||||
time.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
@@ -35,31 +47,52 @@
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
<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.
|
||||
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.
|
||||
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>
|
||||
|
||||
@@ -70,5 +103,5 @@
|
||||
|
||||
<script src="./point.js"></script>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -199,4 +199,3 @@ footer {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
<!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;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
Segoe UI,
|
||||
Roboto,
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
@@ -49,9 +61,9 @@
|
||||
/* Remove outline */
|
||||
opacity: 1;
|
||||
/* Set transparency (for mouse-over effects on hover) */
|
||||
-webkit-transition: .2s;
|
||||
-webkit-transition: 0.2s;
|
||||
/* 0.2 seconds transition on hover */
|
||||
transition: opacity .2s;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
@@ -79,25 +91,36 @@
|
||||
/* 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.
|
||||
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>
|
||||
Increasing the value adds more horizontal slices, creating a denser and more detailed pattern.
|
||||
Tap or click to pause and resume the motion.
|
||||
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>
|
||||
@@ -119,7 +142,7 @@
|
||||
function draw() {
|
||||
background(43);
|
||||
angleMode(DEGREES);
|
||||
lineNum = document.getElementById('myRange').value;
|
||||
lineNum = document.getElementById("myRange").value;
|
||||
|
||||
drawLine(hMargin, lineNum + 1);
|
||||
|
||||
@@ -137,23 +160,57 @@
|
||||
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
|
||||
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
|
||||
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(
|
||||
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 + 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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +218,5 @@
|
||||
lineMove = !lineMove;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
116
style.css
116
style.css
@@ -28,7 +28,9 @@
|
||||
--rosewater: #f5e0dc;
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -39,15 +41,34 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
background: radial-gradient(rgba(205, 214, 244, 0.028) 0.5px, transparent 0.6px),
|
||||
background:
|
||||
radial-gradient(rgba(205, 214, 244, 0.028) 0.5px, transparent 0.6px),
|
||||
radial-gradient(rgba(17, 17, 27, 0.032) 0.5px, transparent 0.6px),
|
||||
radial-gradient(1200px 800px at 12% 8%, rgba(137, 180, 250, 0.07), transparent 62%),
|
||||
radial-gradient(1000px 720px at 88% 92%, rgba(203, 166, 247, 0.07), transparent 64%),
|
||||
radial-gradient(
|
||||
1200px 800px at 12% 8%,
|
||||
rgba(137, 180, 250, 0.07),
|
||||
transparent 62%
|
||||
),
|
||||
radial-gradient(
|
||||
1000px 720px at 88% 92%,
|
||||
rgba(203, 166, 247, 0.07),
|
||||
transparent 64%
|
||||
),
|
||||
linear-gradient(180deg, #1e1e2e 0%, #1b1b2b 55%, #1a1a29 100%);
|
||||
background-size: 3px 3px, 3px 3px, auto, auto, auto;
|
||||
background-position: 0 0, 1px 1px, 0 0, 0 0, 0 0;
|
||||
background-size:
|
||||
3px 3px,
|
||||
3px 3px,
|
||||
auto,
|
||||
auto,
|
||||
auto;
|
||||
background-position:
|
||||
0 0,
|
||||
1px 1px,
|
||||
0 0,
|
||||
0 0,
|
||||
0 0;
|
||||
color: var(--text);
|
||||
font-family: 'JetBrains Mono', 'Apple Color Emoji', monospace;
|
||||
font-family: "JetBrains Mono", "Apple Color Emoji", monospace;
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
overflow-x: hidden;
|
||||
@@ -56,7 +77,7 @@ body {
|
||||
|
||||
body::before,
|
||||
body::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: fixed;
|
||||
width: 52rem;
|
||||
height: 42rem;
|
||||
@@ -71,14 +92,22 @@ body::before {
|
||||
top: -18rem;
|
||||
right: -16rem;
|
||||
transform: rotate(14deg);
|
||||
background: radial-gradient(closest-side at 38% 42%, rgba(203, 166, 247, 0.42), transparent 72%);
|
||||
background: radial-gradient(
|
||||
closest-side at 38% 42%,
|
||||
rgba(203, 166, 247, 0.42),
|
||||
transparent 72%
|
||||
);
|
||||
}
|
||||
|
||||
body::after {
|
||||
bottom: -20rem;
|
||||
left: -18rem;
|
||||
transform: rotate(-12deg);
|
||||
background: radial-gradient(closest-side at 60% 58%, rgba(137, 180, 250, 0.36), transparent 72%);
|
||||
background: radial-gradient(
|
||||
closest-side at 60% 58%,
|
||||
rgba(137, 180, 250, 0.36),
|
||||
transparent 72%
|
||||
);
|
||||
}
|
||||
|
||||
::selection {
|
||||
@@ -122,7 +151,7 @@ nav {
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: 1.3rem;
|
||||
color: var(--mauve);
|
||||
text-decoration: none;
|
||||
@@ -146,7 +175,7 @@ nav {
|
||||
}
|
||||
|
||||
.nav-links a::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
left: 0;
|
||||
@@ -193,7 +222,7 @@ section.full-width {
|
||||
}
|
||||
|
||||
.section-label::after {
|
||||
content: '';
|
||||
content: "";
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 3rem;
|
||||
@@ -222,7 +251,7 @@ section.full-width {
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: clamp(3.5rem, 9vw, 7.5rem);
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
@@ -238,7 +267,7 @@ section.full-width {
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: clamp(1.2rem, 3vw, 2.2rem);
|
||||
font-weight: 300;
|
||||
color: var(--subtext1);
|
||||
@@ -270,7 +299,7 @@ section.full-width {
|
||||
}
|
||||
|
||||
.hero-by-role {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-style: italic;
|
||||
font-size: 0.85rem;
|
||||
color: var(--mauve);
|
||||
@@ -299,7 +328,7 @@ section.full-width {
|
||||
gap: 0.5rem;
|
||||
padding: 0.6rem 1.4rem;
|
||||
border-radius: 4px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.05em;
|
||||
text-decoration: none;
|
||||
@@ -311,7 +340,9 @@ section.full-width {
|
||||
.btn-primary {
|
||||
background: var(--mauve);
|
||||
color: var(--crust);
|
||||
box-shadow: 0 0 0 1px rgba(203, 166, 247, 0.25), 0 10px 26px rgba(17, 17, 27, 0.4);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(203, 166, 247, 0.25),
|
||||
0 10px 26px rgba(17, 17, 27, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
@@ -333,10 +364,11 @@ section.full-width {
|
||||
|
||||
/* Decorative grid bg */
|
||||
#hero::before {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: linear-gradient(var(--surface0) 1px, transparent 1px),
|
||||
background-image:
|
||||
linear-gradient(var(--surface0) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--surface0) 1px, transparent 1px);
|
||||
background-size: 60px 60px;
|
||||
opacity: 0.18;
|
||||
@@ -377,7 +409,11 @@ section.full-width {
|
||||
}
|
||||
|
||||
.about-grid .about-block:first-child {
|
||||
background: linear-gradient(90deg, rgba(203, 166, 247, 0.07), rgba(203, 166, 247, 0.03));
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(203, 166, 247, 0.07),
|
||||
rgba(203, 166, 247, 0.03)
|
||||
);
|
||||
border-left: 2px solid rgba(203, 166, 247, 0.65);
|
||||
box-shadow: inset 0 0 0 1px rgba(203, 166, 247, 0.08);
|
||||
}
|
||||
@@ -423,7 +459,7 @@ section.full-width {
|
||||
}
|
||||
|
||||
.version-timeline::before {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0.65rem;
|
||||
top: 0.5rem;
|
||||
@@ -447,7 +483,9 @@ section.full-width {
|
||||
border: 2px solid var(--surface1);
|
||||
flex-shrink: 0;
|
||||
margin-top: 0.15rem;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
background 0.2s;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -496,7 +534,9 @@ section.full-width {
|
||||
font-size: 0.7rem;
|
||||
color: var(--overlay1);
|
||||
margin-left: 0.3rem;
|
||||
transition: color 0.2s, transform 0.2s;
|
||||
transition:
|
||||
color 0.2s,
|
||||
transform 0.2s;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -525,7 +565,7 @@ section.full-width {
|
||||
|
||||
/* ─── Skills ───────────────────────────────────────────── */
|
||||
#skills h2 {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: 2.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
@@ -641,7 +681,7 @@ section.full-width {
|
||||
|
||||
/* ─── Tutorials / Projects shared ─────────────────────── */
|
||||
.section-heading {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: 2.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
@@ -707,7 +747,7 @@ section.full-width {
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
@@ -746,7 +786,7 @@ section.full-width {
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
@@ -790,7 +830,7 @@ section.full-width {
|
||||
}
|
||||
|
||||
.card::before {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -848,7 +888,9 @@ section.full-width {
|
||||
.card-arrow {
|
||||
font-size: 0.8rem;
|
||||
color: var(--overlay0);
|
||||
transition: color 0.2s, transform 0.2s;
|
||||
transition:
|
||||
color 0.2s,
|
||||
transform 0.2s;
|
||||
align-self: flex-end;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
@@ -876,7 +918,7 @@ section.full-width {
|
||||
}
|
||||
|
||||
.contact-inner h2 {
|
||||
font-family: 'Fraunces', serif;
|
||||
font-family: "Fraunces", serif;
|
||||
font-size: 2.4rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
@@ -935,13 +977,13 @@ section.full-width {
|
||||
}
|
||||
|
||||
.contact-link-icon--github {
|
||||
-webkit-mask-image: url('icons/github.svg');
|
||||
mask-image: url('icons/github.svg');
|
||||
-webkit-mask-image: url("icons/github.svg");
|
||||
mask-image: url("icons/github.svg");
|
||||
}
|
||||
|
||||
.contact-link-icon--linkedin {
|
||||
-webkit-mask-image: url('icons/linkedin.svg');
|
||||
mask-image: url('icons/linkedin.svg');
|
||||
-webkit-mask-image: url("icons/linkedin.svg");
|
||||
mask-image: url("icons/linkedin.svg");
|
||||
}
|
||||
|
||||
.contact-link-label {
|
||||
@@ -988,7 +1030,9 @@ footer a:hover {
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(24px);
|
||||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||||
transition:
|
||||
opacity 0.6s ease,
|
||||
transform 0.6s ease;
|
||||
}
|
||||
|
||||
.reveal.visible {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<DOCTYPE! html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>Convex Hull</title>
|
||||
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
h1{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
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;
|
||||
h2 {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p{
|
||||
p {
|
||||
margin: 1px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
}
|
||||
|
||||
.psudoCode{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
.psudoCode {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
background-color: #d9d9d9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
}
|
||||
|
||||
a{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
a {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
|
||||
button{
|
||||
button {
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
@@ -50,7 +49,7 @@ button{
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float:right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
@@ -58,12 +57,11 @@ button:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.examples{
|
||||
.examples {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
html{
|
||||
html {
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
h1{
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
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;
|
||||
h2 {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 0;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p{
|
||||
p {
|
||||
margin: 1px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 19px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
}
|
||||
|
||||
.psudoCode{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
.psudoCode {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
background-color: #d9d9d9;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
box-shadow: 5px 5px #888888;
|
||||
}
|
||||
|
||||
a{
|
||||
font-family: 'Inconsolata', monospace;
|
||||
a {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-size: 19px;
|
||||
background-color: #D9D9D9;
|
||||
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
|
||||
button{
|
||||
button {
|
||||
background-color: white;
|
||||
border: 2px solid #555555;
|
||||
color: black;
|
||||
@@ -50,7 +49,7 @@ button{
|
||||
-webkit-transition-duration: 0.4s; /* Safari */
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
float:right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
@@ -58,12 +57,11 @@ button:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.examples{
|
||||
.examples {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
html{
|
||||
html {
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
|
||||
@@ -1,38 +1,44 @@
|
||||
<!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 => {
|
||||
.then((r) => r.text())
|
||||
.then((code) => {
|
||||
document.getElementById("define-count").textContent = code;
|
||||
if (window.hljs) hljs.highlightElement(document.getElementById("define-count"));
|
||||
if (window.hljs)
|
||||
hljs.highlightElement(document.getElementById("define-count"));
|
||||
});
|
||||
fetch("snippets/countSquares.js")
|
||||
.then(r => r.text())
|
||||
.then(code => {
|
||||
.then((r) => r.text())
|
||||
.then((code) => {
|
||||
document.getElementById("count-squares").textContent = code;
|
||||
if (window.hljs) hljs.highlightElement(document.getElementById("count-squares"));
|
||||
if (window.hljs)
|
||||
hljs.highlightElement(document.getElementById("count-squares"));
|
||||
});
|
||||
</script>
|
||||
</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.
|
||||
</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
|
||||
@@ -64,8 +70,7 @@
|
||||
</p>
|
||||
<p>
|
||||
Press <b>show rectangles</b> to see the different regions simplified and
|
||||
see how
|
||||
it changes the sum below.
|
||||
see how it changes the sum below.
|
||||
</p>
|
||||
<div id="sumDiv">
|
||||
<a id="subsetOutput">0</a>
|
||||
@@ -81,6 +86,5 @@
|
||||
|
||||
<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 {
|
||||
transition: all 0.15s;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #4c566a;
|
||||
color: #eceff4;
|
||||
border-color: #5e81ac; }
|
||||
button:focus {
|
||||
border-color: #5e81ac;
|
||||
}
|
||||
button:focus {
|
||||
border: 4px solid #88c0d0;
|
||||
box-sizing: border-box; }
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<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 {
|
||||
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{
|
||||
.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