Skip to content

Instantly share code, notes, and snippets.

@jcreedcmu
Created May 9, 2022 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcreedcmu/bbe8c9eda2d7d37ac7fe72505c6b614c to your computer and use it in GitHub Desktop.
Save jcreedcmu/bbe8c9eda2d7d37ac7fe72505c6b614c to your computer and use it in GitHub Desktop.
penrose tiles
import { Point, rot90, rotate, vdiv, vdot, vplus, vscale } from './point';
const NARROW_COLOR = '#ffffff';
const WIDE_COLOR = '#7777ff';
const BG_COLOR = WIDE_COLOR; // '#dff';
const LINE_WIDTH = 0.25;
const width = 1024;
const height = 768;
const OFF_MIN = -9;
const OFF_MAX = 8;
const NUM_DIRS = 5;
const offsetOfDirection = [0.5, 0.5, 0.5, 0.5, 0.5];
const vec = { x: 1, y: 0 };
const PIX_PER_UNIT = 15;
type Line = {
n: Point, // we assume throughout this is a unit vector
length: number,
off: number
};
function getNormal(dir: number): Point {
return rotate(vec, dir * Math.PI / NUM_DIRS);
}
function getLine(dir: number, off: number): Line {
return {
length: 5,
n: getNormal(dir),
off: off + offsetOfDirection[dir],
}
}
function solve(line1: Line, line2: Line): Point {
return {
x: (line2.n.y * line1.off - line1.n.y * line2.off) /
(line2.n.y * line1.n.x - line1.n.y * line2.n.x),
y: (line2.n.x * line1.off - line1.n.x * line2.off) /
(line2.n.x * line1.n.y - line1.n.x * line2.n.y),
}
}
function drawLine(d: CanvasRenderingContext2D, line: Line) {
const nn = rot90(line.n);
const offp = vscale(line.n, PIX_PER_UNIT * line.off);
const p1 = vplus(offp, vscale(nn, PIX_PER_UNIT * line.length));
const p2 = vplus(offp, vscale(nn, -PIX_PER_UNIT * line.length));
d.beginPath();
d.moveTo(p1.x, p1.y);
d.lineTo(p2.x, p2.y);
d.stroke();
}
function drawAxes(d: CanvasRenderingContext2D) {
d.beginPath();
d.moveTo(-width / 2, 0.5);
d.lineTo(width / 2, 0.5);
d.stroke();
d.beginPath();
d.moveTo(0.5, -height / 2);
d.lineTo(0.5, height / 2);
d.stroke();
}
function drawLines(d: CanvasRenderingContext2D) {
for (let off = OFF_MIN; off <= OFF_MAX; off++) {
for (let dir = 0; dir < NUM_DIRS; dir++) {
drawLine(d, getLine(dir, off));
}
}
}
function drawPoint(d: CanvasRenderingContext2D, p: Point) {
const RADIUS = 3;
const pp = vscale(p, PIX_PER_UNIT);
d.beginPath();
d.arc(pp.x, pp.y, RADIUS, 0, 2 * Math.PI);
d.fill();
}
function drawPoints(d: CanvasRenderingContext2D) {
for (let off1 = OFF_MIN; off1 <= OFF_MAX; off1++) {
for (let off2 = OFF_MIN; off2 <= OFF_MAX; off2++) {
for (let dir1 = 0; dir1 < NUM_DIRS; dir1++) {
for (let dir2 = dir1 + 1; dir2 < NUM_DIRS; dir2++) {
drawPoint(d, solve(getLine(dir1, off1), getLine(dir2, off2)));
}
}
}
}
}
function getFullCoords(dir1: number, off1: number, dir2: number, off2: number): number[] {
const p = solve(getLine(dir1, off1), getLine(dir2, off2));
const rv: number[] = [];
for (let ix = 0; ix < NUM_DIRS; ix++) {
const off = offsetOfDirection[ix];
rv[ix] = Math.floor(vdot(getNormal(ix), p) - off);
}
rv[dir1] = off1 - 1;
rv[dir2] = off2 - 1;
return rv;
}
function drawCell(d: CanvasRenderingContext2D, d1: number, d2: number, coords: number[]) {
let origin: Point = { x: 0, y: 0 };
coords.forEach((coord, dir) => {
origin = vplus(origin, vscale(getNormal(dir), PIX_PER_UNIT * coord));
});
const vv1 = vscale(getNormal(d1), PIX_PER_UNIT);
const vv2 = vscale(getNormal(d2), PIX_PER_UNIT)
const pa = vplus(origin, vv1);
const pb = vplus(pa, vv2);
const pc = vplus(origin, vv2);
d.beginPath()
d.moveTo(origin.x, origin.y);
d.lineTo(pa.x, pa.y);
d.lineTo(pb.x, pb.y);
d.lineTo(pc.x, pc.y);
d.lineTo(origin.x, origin.y);
d.fill();
if (LINE_WIDTH > 0)
d.stroke();
}
function drawCells(d: CanvasRenderingContext2D) {
for (let off1 = OFF_MIN; off1 <= OFF_MAX; off1++) {
for (let off2 = OFF_MIN; off2 <= OFF_MAX; off2++) {
for (let dir1 = 0; dir1 < NUM_DIRS; dir1++) {
for (let dir2 = dir1 + 1; dir2 < NUM_DIRS; dir2++) {
const diff = Math.abs(dir1 - dir2);
if (diff == 1 || diff == 4) d.fillStyle = NARROW_COLOR;
if (diff == 2 || diff == 3) d.fillStyle = WIDE_COLOR;
drawCell(d, dir1, dir2, getFullCoords(dir1, off1, dir2, off2));
}
}
}
}
}
function go() {
const c = document.getElementsByTagName('canvas')[0];
c.width = width;
c.height = height;
const d = c.getContext('2d')!;
d.fillStyle = BG_COLOR;
d.fillRect(0, 0, width, height);
d.translate(width / 2, height / 2);
d.scale(1, -1);
d.strokeStyle = '#9dd';
//drawAxes(d);
d.strokeStyle = "#000";
d.lineWidth = LINE_WIDTH;
// drawLines(d);
// d.fillStyle = '#333';
// drawPoints(d);
drawCells(d);
}
go();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment