Web Dev Solutions

Catalin Mititiuc

const svgns = "http://www.w3.org/2000/svg", targetClassName = 'sight-line-target', activeClassName = 'active'; function evenr_to_axial(x, y) { return { q: x - (y + (y & 1)) / 2, r: y }; } function axial_to_evenr(q, r) { return { x: q + (r + (r & 1)) / 2, y: r }; } function axial_distance(q1, r1, q2, r2) { return (Math.abs(q1 - q2) + Math.abs(q1 + r1 - q2 - r2) + Math.abs(r1 - r2)) / 2; } function offset_distance(x1, y1, x2, y2) { const { q: q1, r: r1 } = evenr_to_axial(x1, y1), { q: q2, r: r2 } = evenr_to_axial(x2, y2); return axial_distance(q1, r1, q2, r2); } function cube_to_axial(q, r, _) { return { q, r }; } function axial_to_cube(q, r) { return { q, r, s: -q - r }; } function cube_round(q, r, s) { let rQ = Math.round(q), rR = Math.round(r), rS = Math.round(s); const q_diff = Math.abs(rQ - q), r_diff = Math.abs(rR - r), s_diff = Math.abs(rS - s); if (q_diff > r_diff && q_diff > s_diff) { rQ = -rR - rS; } else if (r_diff > s_diff) { rR = -rQ - rS; } else { rS = -rQ - rR; } return { q: rQ, r: rR, s: rS }; } function axial_round(q, r) { const cube = axial_to_cube(q, r), round = cube_round(cube.q, cube.r, cube.s), axial = cube_to_axial(round.q, round.r, round.s); return { q: axial.q, r: axial.r }; } function lerp(a, b, t) { return a + (b - a) * t; } function axial_lerp(q1, r1, q2, r2, t) { return { q: lerp(q1, q2, t), r: lerp(r1, r2, t) }; } function linedraw(x1, y1, x2, y2) { const axial1 = evenr_to_axial(x1, y1), axial2 = evenr_to_axial(x2, y2), n = offset_distance(x1, y1, x2, y2), results = []; for (let i = 0; i <= n; i++) { const lerp = axial_lerp(axial1.q, axial1.r, axial2.q, axial2.r, 1.0 / n * i), round = axial_round(lerp.q, lerp.r), { x, y } = axial_to_evenr(round.q, round.r); results.push([x, y]); } return results; } export default class SightLine { #board; #lockTarget; #activeHexes = []; sightLine; constructor(svg, board) { this.svg = svg; this.#board = board; } #drawHexes(...coords) { this.#clearHexes() if (this.distanceCallback) { this.distanceCallback(offset_distance(...coords)); } const lineCoords = linedraw(...coords), selector = lineCoords .map(([x, y]) => `g[data-y="${y}"] g[data-x="${x}"] use[href="#hex"]`) .join(', '), hexes = this.svg.querySelectorAll(selector); hexes.forEach(p => p.classList.add(activeClassName)); this.#activeHexes = hexes; } #clearHexes() { if (this.distanceCallback) { this.distanceCallback(); } this.#activeHexes.forEach(el => el.classList.remove(activeClassName)); this.#activeHexes = []; } clear() { if (this.sightLine) { this.sightLine.remove(); delete this.sightLine; } if (this.#lockTarget) { this.#lockTarget.classList.remove(targetClassName); this.#lockTarget = undefined; } this.#clearHexes(); } draw(source, target) { this.clear(); const sightLine = document.createElementNS(svgns, 'line'); sightLine.classList.add('sight-line'); sightLine.classList.add(activeClassName); sightLine.setAttributeNS(null, 'x1', source.position.x); sightLine.setAttributeNS(null, 'y1', source.position.y); sightLine.setAttributeNS(null, 'x2', target.position.x); sightLine.setAttributeNS(null, 'y2', target.position.y); this.sightLine = sightLine; this.#board.appendChild(sightLine); this.#drawHexes(+source.index.x, +source.index.y, +target.index.x, +target.index.y); } update(source) { const { dataset: { x }, parentElement: { dataset: { y }}} = this.#lockTarget; this.sightLine.setAttributeNS(null, 'x1', source.position.x); this.sightLine.setAttributeNS(null, 'y1', source.position.y); this.#drawHexes(+source.index.x, +source.index.y, +x, +y); } toggleLock(cell) { if (this.#lockTarget) { this.sightLine.classList.add(activeClassName); this.#lockTarget.classList.remove(targetClassName); this.#lockTarget = undefined; } else { this.sightLine.classList.remove(activeClassName); cell.classList.add(targetClassName); this.#lockTarget = cell; } } }