Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terrific Toothpick Patterns #4

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This repository contains visualization of various [Numberphile](https://www.yout

1. Trapped Knight - [Video](https://www.youtube.com/watch?v=RGQe8waGJ4w) - [Illustration](./jumping-knight)
2. Prime Spirals - [Video](https://www.youtube.com/watch?v=iFuR97YcSLM) - [Illustration](./prime-spirals)
3. Terrific Toothpick Patterns - [Video](https://www.youtube.com/watch?v=_UtCli1SgjI) - [Illustration](./toothpick)

## Contribute

Expand Down
53 changes: 53 additions & 0 deletions toothpick/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>

<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>Numberphilephile - Terrific Toothpick Patterns</title>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css" rel="stylesheet" type="text/css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script>
<link href="main.css" rel="stylesheet">
<script src="toothpick.js"></script>

</head>
<body onload="toothpick.init()">

<div class="ui raised container">
<div class="ui centered grid">
<div class="three wide column">
<div class="ui center aligned inverted raised segment">
<h4 class="ui inverted orange header">
<a href="https://www.youtube.com/watch?v=_UtCli1SgjI" target="_blank"
title="Youtube Numberphile video of Terrific Toothpick Patterns">
Watch Video
</a>
</h4>
</div>
<div class="ui center aligned inverted raised segment">
<div class="ui basic segment">
<h4 class="ui inverted orange header">
Run Options
</h4>
<div class="ui buttons vertical">
<button class="ui button inverted olive" id="next-trigger">One Step</button>
<button class="ui button inverted green" id="auto-trigger">Auto Run</button>
<button class="ui button inverted secondary" id="reset-grid">Reset</button>
</div>
</div>
</div>
</div>
<div class="eleven wide column">
<div class="root-grid">
<canvas id="root"></canvas>
</div>
</div>
</div>
</div>


</body>
</html>

41 changes: 41 additions & 0 deletions toothpick/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
body {
padding-top: 50px;
background-color: #4b5d67;
}

.block {
background: #feffc4;
align-items: center;
color: #FFFFFFE6;
}

.block.dotted {
border: 1px dotted #4b5d67;
}

.rowBlock {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(1px, 1fr));
text-align: center;
}

.root-grid {
display: grid;
width: 100%;
}

.block.black {
background-color: #1b1c1d;
}

.block.red {
background-color: #db2828;
}

.block.grey {
background-color: #767676;
}

.block.olive {
background-color: #b5cc18;
}
113 changes: 113 additions & 0 deletions toothpick/toothpick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* Created By : Lalit Umbarkar
* Created On : 11/10/20
*/

const Utl = (function () {

const oliveColor = "#b5cc18", blackColor = "#1b1c1d", greyColor = "#767676", pickWidth = 10, pickHeight = 100;

const rgbToHex = (r, g, b) => {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}

return {
blackColor, oliveColor, greyColor,
pickWidth, pickHeight,
getColorInd: (ctx, x, y) => {
let imageData = ctx.getImageData(x, y, 1, 1).data;
return "#" + ("000000" + rgbToHex(imageData[0], imageData[1], imageData[2])).slice(-6);
},
clearCanvas: (ctx) => {
ctx.clearRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight);
ctx.fillStyle = blackColor;
ctx.fillRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight);
},
drawVerticalToothpick: (ctx, cx, cy, color, latest = []) => {
ctx.fillStyle = color;
ctx.fillRect(cx, cy - (pickHeight / 2), pickWidth, pickHeight);
latest.push([cx, cy]);
},
drawHorizontalToothpick: (ctx, cx, cy, color, latest = []) => {
ctx.fillStyle = color;
ctx.fillRect(cx - (pickHeight / 2), cy, pickHeight, pickWidth);
latest.push([cx, cy]);
}
}
})();

const toothpick = (function () {

let ctx, latestVertical = [], latestHorizontal = [];

const addEdgeToothpicks = () => {

let halfDiff = Utl.pickHeight / 2,
upcomingVerticals = [], upcomingHorizontals = [];
while (latestVertical.length) {
let eachPick = latestVertical.pop(),
ex = eachPick[0], ey = eachPick[1],
abovePick = [ex, ey - halfDiff],
belowPick = [ex, ey + halfDiff];
Utl.drawVerticalToothpick(ctx, ...eachPick, Utl.greyColor);
if (Utl.getColorInd(ctx, ex, ey - halfDiff - Utl.pickWidth) === Utl.blackColor)
Utl.drawHorizontalToothpick(ctx, ...abovePick, Utl.oliveColor, upcomingHorizontals);
if (Utl.getColorInd(ctx, ex, ey + halfDiff + Utl.pickWidth) === Utl.blackColor)
Utl.drawHorizontalToothpick(ctx, ...belowPick, Utl.oliveColor, upcomingHorizontals);
}
while (latestHorizontal.length) {
let eachPick = latestHorizontal.pop(),
ex = eachPick[0], ey = eachPick[1],
leftPick = [ex - halfDiff, ey],
rightPick = [ex + halfDiff, ey];
Utl.drawHorizontalToothpick(ctx, ...eachPick, Utl.greyColor);
if (Utl.getColorInd(ctx, ex - halfDiff - Utl.pickWidth, ey) === Utl.blackColor)
Utl.drawVerticalToothpick(ctx, ...leftPick, Utl.oliveColor, upcomingVerticals);
if (Utl.getColorInd(ctx, ex + halfDiff + Utl.pickWidth, ey) === Utl.blackColor)
Utl.drawVerticalToothpick(ctx, ...rightPick, Utl.oliveColor, upcomingVerticals);
}
latestHorizontal = upcomingHorizontals;
latestVertical = upcomingVerticals;
};

const resetAndKeepOneToothpick = () => {
if (!ctx) {
console.error("Context not setup");
return;
}

let canvasCenter = [ctx.canvas.clientWidth / 2, ctx.canvas.clientHeight / 2];
latestVertical = [];
latestHorizontal = [];
Utl.clearCanvas(ctx);
Utl.drawVerticalToothpick(ctx, ...canvasCenter, Utl.oliveColor, latestVertical);
};

const registerListeners = () => {
document.getElementById("reset-grid").onclick = resetAndKeepOneToothpick;
document.getElementById("next-trigger").onclick = addEdgeToothpicks;
};

return {
init: () => {
registerListeners();
// let rootEle = document.getElementsByClassName("root-grid")[0];
// rootEle.setAttribute("style", "height: " + rootEle.clientWidth + "px");
const canvas = document.getElementById("root");
let cH = canvas.parentElement.clientHeight, cW = canvas.parentElement.clientWidth,
smallerDimension = cW > cH ? cW : cH;
canvas.setAttribute("height", smallerDimension + "px");
canvas.setAttribute("width", smallerDimension + "px");
if (canvas.getContext)
ctx = canvas.getContext('2d');
resetAndKeepOneToothpick();
},
};

})();