Refactor sight line module to remove almost all querying for elements

This commit is contained in:
Catalin Constantin Mititiuc 2025-06-16 22:41:29 -07:00
parent e385798667
commit 72b1b8adfc
5 changed files with 126 additions and 146 deletions

View File

@ -35,7 +35,7 @@ use[href="#hex"].active {
stroke: black; stroke: black;
} }
use[href="#hex"].sight-line-target { .sight-line-target use[href="#hex"] {
opacity: 1; opacity: 1;
stroke: orangered; stroke: orangered;
fill-opacity: 0.04; fill-opacity: 0.04;

View File

@ -140,13 +140,6 @@ use[href="#point"].active {
opacity: 0.2; opacity: 0.2;
} }
use[href="#point"].sight-line-target {
opacity: 1;
stroke: orangered;
/* stroke-width: 1px; */
fill-opacity: 0.04;
}
polyline.move-trace { polyline.move-trace {
stroke: gray; stroke: gray;
stroke-dasharray: 2; stroke-dasharray: 2;

View File

@ -10,13 +10,16 @@ export default class Game {
constructor(svg) { constructor(svg) {
this.svg = svg; this.svg = svg;
this.firingArc = new FiringArc(svg);
this.sightLine = new SightLine(svg); const board = this.getBoard();
this.firingArc = new FiringArc(svg, board);
this.sightLine = new SightLine(svg, board);
this.counter = new Counter(svg); this.counter = new Counter(svg);
this.setUpCells(); this.setUpCells();
let counter = this.counter.getCounter({ dataset: { allegiance: 'davion', number: '1' }}); // debug
const counter = this.counter.getCounter({ dataset: { allegiance: 'davion', number: '1' }});
this.counter.place(counter, this.getCell(17, 25)); this.counter.place(counter, this.getCell(17, 25));
this.select(counter); this.select(counter);
} }
@ -58,7 +61,7 @@ export default class Game {
} }
getGridIndex({ parentElement: { dataset: { x }, parentElement: { dataset: { y }}}}) { getGridIndex({ parentElement: { dataset: { x }, parentElement: { dataset: { y }}}}) {
return { x, y }; return { x: +x, y: +y };
} }
getCounterAtGridIndex(x, y) { getCounterAtGridIndex(x, y) {
@ -177,7 +180,7 @@ export default class Game {
if (!lockedSl) { if (!lockedSl) {
this.sightLine.clear(); this.sightLine.clear();
} else { } else {
this.sightLine.update(cell, this.getCellPosition(cell)); this.updateSightLine(cell);
} }
} else if (toPlace && state.occupant) { } else if (toPlace && state.occupant) {
if (toPlace === state.occupant) { if (toPlace === state.occupant) {
@ -200,7 +203,7 @@ export default class Game {
if (!lockedSl) { if (!lockedSl) {
this.sightLine.clear(); this.sightLine.clear();
} else { } else {
this.sightLine.update(toPlace.parentElement, this.getCellPosition(toPlace.parentElement)); this.updateSightLine(toPlace.parentElement);
} }
} else { } else {
this.unSelect(); this.unSelect();
@ -221,7 +224,7 @@ export default class Game {
if (!lockedSl) { if (!lockedSl) {
this.sightLine.clear(); this.sightLine.clear();
} else { } else {
this.sightLine.update(cell, this.getCellPosition(cell)); this.updateSightLine(cell);
} }
} else { } else {
const index = this.getGridIndex(state.occupant), const index = this.getGridIndex(state.occupant),
@ -266,19 +269,8 @@ export default class Game {
cell.addEventListener('contextmenu', e => { cell.addEventListener('contextmenu', e => {
e.preventDefault(); e.preventDefault();
let sl = this.getSightLine(); this.sightLine.toggleLock(cell);
cell.dispatchEvent(new MouseEvent('pointerover'));
if (sl) {
sl.classList.toggle('active');
if (sl.classList.contains('active')) {
this.sightLine.clear();
} else {
this.getHex(cell).classList.add('sight-line-target');
}
cell.dispatchEvent(new MouseEvent('pointerover'));
}
}); });
cell.addEventListener('pointerover', e => { cell.addEventListener('pointerover', e => {
@ -290,7 +282,7 @@ export default class Game {
sourceCell = selected.parentElement; sourceCell = selected.parentElement;
if (isOnBoard && (!sl || sl.classList.contains('active')) && sourceCell != cell) { if (isOnBoard && (!sl || sl.classList.contains('active')) && sourceCell != cell) {
this.sightLine.draw(sourceCell, cell); this.drawSightLine(sourceCell, cell);
} }
} }
@ -332,4 +324,20 @@ export default class Game {
this.placing.push(counter); this.placing.push(counter);
} }
updateSightLine(cell) {
const { dataset: { x: sX }, parentElement: { dataset: { y: sY }}} = cell,
source = { index: { x: sX, y: sY }, position: this.getCellPosition(cell) };
this.sightLine.update(source);
}
drawSightLine(sourceCell, targetCell) {
const { dataset: { x: sX }, parentElement: { dataset: { y: sY }}} = sourceCell,
{ dataset: { x: tX }, parentElement: { dataset: { y: tY }}} = targetCell,
source = { index: { x: sX, y: sY }, position: this.getCellPosition(sourceCell) },
target = { index: { x: tX, y: tY }, position: this.getCellPosition(targetCell) };
this.sightLine.draw(source, target);
}
} }

View File

@ -1,8 +1,11 @@
const svgns = "http://www.w3.org/2000/svg"; const svgns = "http://www.w3.org/2000/svg";
export default class Counter { export default class Counter {
constructor(svg) { #board;
constructor(svg, board) {
this.svg = svg; this.svg = svg;
this.#board = board;
this.selectedClass = 'selected'; this.selectedClass = 'selected';
} }

View File

@ -1,4 +1,6 @@
const svgns = "http://www.w3.org/2000/svg"; const svgns = "http://www.w3.org/2000/svg",
targetClassName = 'sight-line-target',
activeClassName = 'active';
function evenr_to_axial(x, y) { function evenr_to_axial(x, y) {
return { q: x - (y + (y & 1)) / 2, r: y }; return { q: x - (y + (y & 1)) / 2, r: y };
@ -13,26 +15,26 @@ function axial_distance(q1, r1, q2, r2) {
} }
function offset_distance(x1, y1, x2, y2) { function offset_distance(x1, y1, x2, y2) {
let { q: q1, r: r1 } = evenr_to_axial(x1, y1), const { q: q1, r: r1 } = evenr_to_axial(x1, y1),
{ q: q2, r: r2 } = evenr_to_axial(x2, y2); { q: q2, r: r2 } = evenr_to_axial(x2, y2);
return axial_distance(q1, r1, q2, r2); return axial_distance(q1, r1, q2, r2);
} }
function cube_to_axial(q, r, s) { function cube_to_axial(q, r, _) {
return { q: q, r: r }; return { q, r };
} }
function axial_to_cube(q, r) { function axial_to_cube(q, r) {
return { q: q, r: r, s: -q - r }; return { q, r, s: -q - r };
} }
function cube_round(q, r, s) { function cube_round(q, r, s) {
rQ = Math.round(q); let rQ = Math.round(q),
rR = Math.round(r); rR = Math.round(r),
rS = Math.round(s); rS = Math.round(s);
let q_diff = Math.abs(rQ - q), const q_diff = Math.abs(rQ - q),
r_diff = Math.abs(rR - r), r_diff = Math.abs(rR - r),
s_diff = Math.abs(rS - s); s_diff = Math.abs(rS - s);
@ -48,7 +50,7 @@ function cube_round(q, r, s) {
} }
function axial_round(q, r) { function axial_round(q, r) {
let cube = axial_to_cube(q, r), const cube = axial_to_cube(q, r),
round = cube_round(cube.q, cube.r, cube.s), round = cube_round(cube.q, cube.r, cube.s),
axial = cube_to_axial(round.q, round.r, round.s); axial = cube_to_axial(round.q, round.r, round.s);
@ -64,13 +66,13 @@ function axial_lerp(q1, r1, q2, r2, t) {
} }
function linedraw(x1, y1, x2, y2) { function linedraw(x1, y1, x2, y2) {
let axial1 = evenr_to_axial(x1, y1), const axial1 = evenr_to_axial(x1, y1),
axial2 = evenr_to_axial(x2, y2), axial2 = evenr_to_axial(x2, y2),
n = offset_distance(x1, y1, x2, y2), n = offset_distance(x1, y1, x2, y2),
results = []; results = [];
for (let i = 0; i <= n; i++) { for (let i = 0; i <= n; i++) {
let lerp = axial_lerp(axial1.q, axial1.r, axial2.q, axial2.r, 1.0 / 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), round = axial_round(lerp.q, lerp.r),
{ x, y } = axial_to_evenr(round.q, round.r); { x, y } = axial_to_evenr(round.q, round.r);
@ -81,119 +83,93 @@ function linedraw(x1, y1, x2, y2) {
} }
export default class SightLine { export default class SightLine {
constructor(svg) { #board;
#lockTarget;
#activeHexes = [];
sightLine;
constructor(svg, board) {
this.svg = svg; this.svg = svg;
this.#board = board;
} }
getBoard() { #drawHexes(...coords) {
return this.svg.querySelector('.board'); this.#clearHexes()
}
getActiveHexes() {
return this.svg.querySelectorAll('use[href="#hex"].active');
}
clear() {
const board = this.getBoard(),
sl = board.querySelector('line.sight-line'),
target = board.querySelector(`use[href="#hex"].sight-line-target`);
if (sl) {
sl.remove();
}
if (target) {
target.classList.remove('sight-line-target');
}
this.clearHexes();
}
clearHexes() {
if (this.distanceCallback) {
this.distanceCallback();
}
this.getActiveHexes().forEach(el => el.classList.remove('active'));
}
draw(source, target) {
this.clear();
let pt = new DOMPoint(0, 0),
transform = getComputedStyle(source).transform.match(/-?\d+\.?\d*/g),
mtx = new DOMMatrix(transform);
pt = pt.matrixTransform(mtx);
transform = getComputedStyle(source.parentElement).transform.match(/-?\d+\.?\d*/g);
mtx = new DOMMatrix(transform);
pt = pt.matrixTransform(mtx);
let slX1 = pt.x,
slY1 = pt.y;
pt = new DOMPoint(0, 0);
transform = getComputedStyle(target).transform.match(/-?\d+\.?\d*/g);
mtx = new DOMMatrix(transform);
pt = pt.matrixTransform(mtx);
transform = getComputedStyle(target.parentElement).transform.match(/-?\d+\.?\d*/g);
mtx = new DOMMatrix(transform);
pt = pt.matrixTransform(mtx);
let slX2 = pt.x,
slY2 = pt.y;
let sightLine = document.createElementNS(svgns, 'line');
sightLine.classList.add('sight-line');
sightLine.classList.add('active');
sightLine.setAttributeNS(null, 'x1', slX1);
sightLine.setAttributeNS(null, 'y1', slY1);
sightLine.setAttributeNS(null, 'x2', slX2);
sightLine.setAttributeNS(null, 'y2', slY2);
this.getBoard().appendChild(sightLine);
let coords = [
source.dataset.x,
source.parentElement.dataset.y,
target.dataset.x,
target.parentElement.dataset.y
].map(n => parseInt(n));
this.drawHexes(...coords);
}
update(cell, { x, y }) {
const sl = this.svg.querySelector('.sight-line'),
target = this.svg.querySelector('.sight-line-target').parentElement,
x1 = cell.dataset.x,
y1 = cell.parentElement.dataset.y,
x2 = target.dataset.x,
y2 = target.parentElement.dataset.y;
sl.setAttributeNS(null, 'x1', x);
sl.setAttributeNS(null, 'y1', y);
this.drawHexes(...[x1, y1, x2, y2].map(n => parseInt(n)));
}
drawHexes(...coords) {
this.clearHexes()
if (this.distanceCallback) { if (this.distanceCallback) {
this.distanceCallback(offset_distance(...coords)); this.distanceCallback(offset_distance(...coords));
} }
let lineCoords = linedraw(...coords); const lineCoords = linedraw(...coords),
let s = lineCoords selector = lineCoords
.map(([x, y]) => `g[data-y="${y}"] g[data-x="${x}"] use[href="#hex"]`) .map(([x, y]) => `g[data-y="${y}"] g[data-x="${x}"] use[href="#hex"]`)
.join(', '); .join(', '),
this.svg.querySelectorAll(s).forEach(p => p.classList.add('active')); 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;
}
} }
} }