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; } function createElement({ x: x1, y: y1 }, { x: x2, y: y2 }) { const sightLine = document.createElementNS(svgns, 'line'); sightLine.classList.add('sight-line'); sightLine.classList.add(activeClassName); sightLine.setAttributeNS(null, 'x1', x1); sightLine.setAttributeNS(null, 'y1', y1); sightLine.setAttributeNS(null, 'x2', x2); sightLine.setAttributeNS(null, 'y2', y2); return sightLine; } export default function(svg, board) { let activeHexes = [], lockTarget, distanceCallback; function drawHexes(...coords) { clearHexes(); if (distanceCallback) { 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(', '); activeHexes = svg.querySelectorAll(selector); activeHexes.forEach(p => p.classList.add(activeClassName)); } function clearHexes() { if (distanceCallback) { distanceCallback(); } activeHexes.forEach(el => el.classList.remove(activeClassName)); activeHexes = []; } this.sightLine; this.clear = function () { if (this.sightLine) { this.sightLine.remove(); delete this.sightLine; } if (lockTarget) { lockTarget.classList.remove(targetClassName); lockTarget = null; } clearHexes(); } this.draw = function (source, target) { this.clear(); this.sightLine = createElement(source.position, target.position); board.appendChild(this.sightLine); drawHexes(+source.index.x, +source.index.y, +target.index.x, +target.index.y); } this.update = function(source) { const { dataset: { x }, parentElement: { dataset: { y }}} = lockTarget; this.sightLine.setAttributeNS(null, 'x1', source.position.x); this.sightLine.setAttributeNS(null, 'y1', source.position.y); drawHexes(+source.index.x, +source.index.y, +x, +y); } this.toggleLock = function (cell) { if (lockTarget) { this.sightLine.classList.add(activeClassName); lockTarget.classList.remove(targetClassName); lockTarget = null; } else { this.sightLine.classList.remove(activeClassName); cell.classList.add(targetClassName); lockTarget = cell; } } this.setDistanceCallback = function(callback) { distanceCallback = callback; } }