Locating points to their nearest corner or center at different scales. The goal is to group them accordingly.
A Pen by HARUN PEHLİVAN on CodePen.
| <header> | |
| <input type="range" min="1" max="500" step="10" value="20" /> | |
| <button>Refresh</button> | |
| </header> | |
| <main> | |
| <div> | |
| <canvas height="1200" width="1200"></canvas> | |
| </div> | |
| </main> |
| console.clear(); | |
| // TODO: group points in threes at a certain zoom level | |
| class Point { | |
| constructor({ x, y }) { | |
| this.x = x; | |
| this.y = y; | |
| this.zones = {}; | |
| // how deep we zooming? | |
| let scale = this.scales[5]; | |
| for (let i = 1; i >= scale; i /= 2) | |
| this.zones[i.toFixed(5)] = this.plotter(i); | |
| } | |
| get scales() { | |
| return [ | |
| 1.00000, 0.50000, 0.25000, | |
| 0.12500, 0.06250, 0.03125 | |
| ]; | |
| } | |
| plotter(whole) { | |
| // real float distance of center being the closest point | |
| let centerProx = 0.3535533905932738; | |
| let half = whole / 2; | |
| let relx = (this.x % whole) / whole; | |
| let rely = (this.y % whole) / whole; | |
| let xoff = Math.floor(this.x / whole); | |
| let yoff = Math.floor(this.y / whole); | |
| let proxC = this.safeFloat(Math.hypot(0.5 - relx, 0.5 - rely)); | |
| let isCenter = proxC <= centerProx; | |
| let origin; | |
| // top left | |
| if (relx < 0.5 && rely < 0.5) { | |
| origin = 'nw'; | |
| // top right | |
| } else if (relx >= 0.5 && rely < 0.5) { | |
| origin = 'ne'; | |
| // bottom right | |
| } else if (relx < 0.5 && rely >= 0.5) { | |
| origin = 'sw'; | |
| // bottom left | |
| } else { | |
| origin = 'se'; | |
| } | |
| relx = this.safeFloat(relx); | |
| rely = this.safeFloat(rely); | |
| return { relx, rely, xoff, yoff, proxC, isCenter, origin }; | |
| } | |
| safeFloat(float) { | |
| let res = 1000000000000000; | |
| return Math.round(float * res) / res; | |
| } | |
| } | |
| class App { | |
| constructor() { | |
| this.initializeCanvas(); | |
| } | |
| run(pointCount) { | |
| this.generatePoints(pointCount); | |
| this.draw(); | |
| } | |
| initializeCanvas() { | |
| this.$cvs = document.querySelector('canvas'); | |
| this.$ctx = this.$cvs.getContext('2d'); | |
| this.g = 8; | |
| this.w = this.$cvs.width - this.g * 2; | |
| this.h = this.$cvs.height - this.g * 2; | |
| this.di = 6; | |
| this.rad = this.di / 2; | |
| } | |
| generatePoints(pointCount) { | |
| this.points = []; | |
| for (let i = 0; i < pointCount; i++) { | |
| let point = new Point({ x: Math.random(), y: Math.random() }); | |
| this.points.push(point); | |
| } | |
| } | |
| draw() { | |
| this.drawnZones = {}; | |
| this.$ctx.clearRect(0, 0, this.$cvs.width, this.$cvs.height); | |
| this.points.forEach((point) => { | |
| this.drawPoint(point); | |
| }); | |
| } | |
| drawPoint(point) { | |
| for (let key in point.zones) { | |
| let zone = point.zones[key]; | |
| let size = parseFloat(key); | |
| let color = `hsl(${size * 240}, 100%, 50%)`; | |
| let id = `${zone.xoff}-${zone.yoff}-${size}`; | |
| // we only want to draw the zone once | |
| if (!this.drawnZones[id]) { | |
| this.drawnZones[id] = true; | |
| this.$ctx.fillStyle = color; | |
| this.$ctx.fillRect( | |
| zone.xoff * (size * this.w) + this.g + (this.w * size / 2) - this.rad, | |
| zone.yoff * (size * this.h) + this.g + (this.h * size / 2) - this.rad, | |
| this.di, | |
| this.di | |
| ); | |
| this.$ctx.strokeStyle = color; | |
| this.$ctx.strokeRect( | |
| zone.xoff * (size * this.w) + this.g, | |
| zone.yoff * (size * this.h) + this.g, | |
| this.w * size, | |
| this.h * size | |
| ); | |
| } | |
| } | |
| this.$ctx.fillStyle = '#fff'; | |
| this.$ctx.beginPath(); | |
| this.$ctx.arc( | |
| (point.x * this.w) + this.g, | |
| (point.y * this.h) + this.g, | |
| this.rad, 0, 2 * Math.PI, false | |
| ); | |
| this.$ctx.fill(); | |
| } | |
| } | |
| let app = new App(); | |
| let count = parseInt(document.querySelector('input').value); | |
| app.run(count); | |
| document.querySelector('button').addEventListener('click', () => { | |
| app.run(count); | |
| }); | |
| document.querySelector('input').addEventListener('input', (e) => { | |
| count = parseInt(e.target.value); | |
| app.run(count) | |
| }); | |
| html,body { | |
| height: 100%; | |
| } | |
| body { | |
| background: #121212; | |
| } | |
| header, main { | |
| text-align: center; | |
| width: 95%; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 2rem 0; | |
| } | |
| button, select { | |
| border-radius: 4px; | |
| padding: 0.5rem 0.75rem; | |
| appearance: none; | |
| background: black; | |
| color: white; | |
| border: 2px solid #444; | |
| &:hover { | |
| border-color: white; | |
| } | |
| cursor: pointer; | |
| } | |
| main { | |
| user-select: none; | |
| margin: 0 auto; | |
| width: 95%; | |
| max-width: 600px; | |
| div { | |
| position: relative; | |
| width: 100%; | |
| } | |
| } | |
| canvas { | |
| display: block; | |
| height: auto; | |
| width: 100%; | |
| background: black; | |
| } |
Locating points to their nearest corner or center at different scales. The goal is to group them accordingly.
A Pen by HARUN PEHLİVAN on CodePen.