Skip to content

Instantly share code, notes, and snippets.

@harunpehlivan
Created May 30, 2021 13:37
Show Gist options
  • Save harunpehlivan/6add52f4ffa0692a315a46b374dcd54d to your computer and use it in GitHub Desktop.
Save harunpehlivan/6add52f4ffa0692a315a46b374dcd54d to your computer and use it in GitHub Desktop.
Travelling Salesman Sketches: Point-out Proximal Checker Alt-look
<div>
<select id="scale"></select>
<select id="start"></select>
</div>
<canvas height="800" width="800"></canvas>
console.clear();
const PI = Math.PI;
const PI2 = PI * 2;
class Canvas {
constructor() {
this.element = document.querySelector('canvas');
this.context = this.element.getContext('2d');
this.diameter = this.element.width;
this.gutter = this.diameter * 0.05;;
}
clear() {
this.context.clearRect(0, 0, this.diameter, this.diameter);
}
point(x, y, color = 'red', radius = 4) {
this.context.fillStyle = color;
this.context.beginPath();
this.context.arc(this.relative(x), this.relative(y), radius, 0, PI2);
this.context.fill();
}
diamond(x, y, scale, color = 'red') {
this.context.fillStyle = color;
this.context.beginPath();
let relScale = scale / 2;
let x1 = this.relative(x);
let y1 = this.relative(y - relScale);
let x2 = this.relative(x + relScale);
let y2 = this.relative(y);
let x3 = this.relative(x);
let y3 = this.relative(y + relScale);
let x4 = this.relative(x - relScale);
let y4 = this.relative(y);
this.context.moveTo(x1, y1);
this.context.lineTo(x2, y2);
this.context.lineTo(x3, y3);
this.context.lineTo(x4, y4);
this.context.fill();
}
line(from, to, color = 'red', width = 1) {
this.context.beginPath();
this.context.strokeStyle = color;
this.context.lineWidth = width;
this.context.moveTo(this.relative(from[0]), this.relative(from[1]));
this.context.lineTo(this.relative(to[0]), this.relative(to[1]));
this.context.stroke();
}
relative(pos) {
return pos * (this.diameter - (this.gutter * 2)) + this.gutter;
}
}
class Solver {
constructor({ scale, start }) {
this.canvas = new Canvas();
this.update({ scale, start });
this.draw();
}
update({ scale = 0.125, start = 'C' }) {
this.scale = scale;
this.radius = Math.round(scale * 6) + 1;
this.speed = Math.round(scale * 40);
let startPlots = {
C: [0.5, 0.5],
NE: [1 - scale, scale],
SE: [1 - scale, 1 - scale],
SW: [scale, 1 - scale],
NW: [scale, scale],
}[start];
this.startX = startPlots[0];
this.startY = startPlots[1];
this.NW = this.startX <= 0.5 && this.startY <= 0.5;
this.NE = this.startX > 0.5 && this.startY <= 0.5;
this.SE = this.startX > 0.5 && this.startY > 0.5;
this.SW = this.startX <= 0.5 && this.startY > 0.5;
this.generate();
}
generate() {
this.points = [];
let steps = 1 / this.scale;
let rad = this.scale / 2;
let points = [];
for (let y = 0; y <= steps; y++) {
let relY = y * this.scale;
for (let x = 0; x <= steps; x++) {
let relX = x * this.scale;
this.addPoint(relX, relY);
if (x < steps && y < steps) {
this.addPoint(relX + rad, relY + rad);
}
}
}
this.pointCount = this.points.length;
this.sortPoints();
this.progress = 0;
this.pointIdx = 0;
}
sortPoints() {
this.points = this.points.sort((a, b) => {
let ax = a[0], bx = b[0];
let ay = a[1], by = b[1];
let aprox = a[2], bprox = b[2];
// perfer proximity to center
if (aprox.C > bprox.C) return 1;
if (aprox.C < bprox.C) return -1;
// prefer radial proximity
if (aprox.R > bprox.R) return 1;
if (aprox.R < bprox.R) return -1;
return 0;
});
}
addPoint(x, y) {
let prox = {
C: Math.hypot(this.startX - x, this.startY - y),
R: Math.atan((0.5 - y) / (0.5 - x)) * (180 / PI)
}
this.points.push([x, y, prox]);
}
draw() {
if (this.progress % this.speed === 0) {
this.canvas.clear();
this.points.forEach((point) => {
this.canvas.point(point[0], point[1], '#222', this.radius);
});
let point1 = this.points[this.pointIdx % this.pointCount];
let x1 = point1[0],
y1 = point1[1];
this.canvas.diamond(x1, y1, this.scale, 'red');
let fuzz1 = 2;
let fuzz2 = 3;
// future points
for (let i = 1; i < 1 + fuzz1; i++) {
let id = (this.pointIdx % this.pointCount) + i;
if (id < this.pointCount) {
let point = this.points[id];
this.canvas.point(point[0], point[1], 'white', this.radius);
}
}
// previous points
for (let i = 0; i < (this.pointIdx % this.pointCount); i++) {
let point = this.points[i];
let x = point[0],
y = point[1];
if ((this.pointIdx % this.pointCount) - i < fuzz2) {
this.canvas.diamond(x, y, this.scale, 'red');
} else {
this.canvas.diamond(x, y, this.scale, 'rgba(100, 100, 100, 0.2)');
}
}
this.pointIdx++;
}
this.progress++;
window.requestAnimationFrame(this.draw.bind(this));
}
}
// let scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 1];
let scales = [0.0625, 0.125, 0.25, 0.5, 1];
let starts = ['C', 'NW', 'NE', 'SE', 'SW'];
let currScale = 1;
let currStart = 0;
let $scale = document.querySelector('#scale');
let $start = document.querySelector('#start');
buildScale();
buildStart();
$scale.addEventListener('change', update);
$start.addEventListener('change', update);
let solver = new Solver({ scale: scales[currScale], start: starts[currStart] });
function update() {
let scale = parseFloat($scale.value);
let start = $start.value;
solver.update({ scale, start });
}
function buildScale() {
for (let i = 0; i < scales.length; i++) {
let $opt = document.createElement('option');
$opt.value = scales[i];
$opt.innerHTML = scales[i];
if (i === currScale) $opt.setAttribute('selected', true);
$scale.appendChild($opt);
}
}
function buildStart() {
for (let i = 0; i < starts.length; i++) {
let $opt = document.createElement('option');
$opt.value = starts[i];
$opt.innerHTML = starts[i];
if (i === currStart) $opt.setAttribute('selected', true);
$start.appendChild($opt);
}
}
body {
background: #121212;
}
div {
margin: 1rem 0 0;
text-align: center;
}
canvas {
height: auto;
width: 95%;
max-width: 600px;
margin: 1rem auto;
display: block;
background: black;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment