More refactor of firing arc module

This commit is contained in:
Catalin Constantin Mititiuc 2024-04-26 12:14:24 -07:00
parent 93ef00d180
commit 9d02591fac

View File

@ -23,6 +23,17 @@ const svgns = "http://www.w3.org/2000/svg",
let svg; let svg;
class Point {
constructor(x = 0, y = 0) {
this.x = +x;
this.y = +y;
}
toString() {
return `${this.x},${this.y}`;
}
}
function calculateAngle(xDiff, yDiff) { function calculateAngle(xDiff, yDiff) {
yDiff = -yDiff; yDiff = -yDiff;
let angle = Math.abs(Math.atan(yDiff / xDiff)); let angle = Math.abs(Math.atan(yDiff / xDiff));
@ -67,74 +78,35 @@ function edgePoint({ x: x1, y: y1 }, { x: x2, y: y2 }, { x: [minX, maxX], y: [mi
return new Point(...pointCoords); return new Point(...pointCoords);
} }
class Point { function touchSameEdge({ x: x1, y: y1 }, { x: x2, y: y2 }) {
constructor(x = 0, y = 0) { return x1 === x2 || y1 === y2;
this.x = +x;
this.y = +y;
}
toString() {
return `${this.x},${this.y}`;
}
} }
function position(e, firingArc, firingArcOutline, aimLine, grid) { function shareValue({ x: x1, y: y1 }, { x: x2, y: y2 }) {
// TODO: handle exactly horizontal and vertical lines? return x1 === x2 || y1 === y2;
}
let pointer = new DOMPoint(e.clientX, e.clientY); function touchOrthogonalEdges({ x: x1, y: y1 }, { x: x2, y: y2 }, bounds) {
let pointerPt = pointer.matrixTransform(grid.getScreenCTM().inverse()); return (bounds.x.includes(x1) && bounds.y.includes(y2)) || (bounds.y.includes(y1) && bounds.x.includes(x2));
}
let pivotPt = new Point(aimLine.getAttribute('x1'), aimLine.getAttribute('y1')); function getCornerPts({ x: [xMin, xMax], y: [yMin, yMax] }) {
const corners = [[xMin, yMin], [xMax, yMin], [xMax, yMax], [xMin, yMax]];
return corners.map(([x, y]) => new Point(x, y));
}
let { x, y, width, height } = grid.getBBox(); function getBounds({ x, y, width, height }) {
return {
const bounds = {
x: [x, x + width], x: [x, x + width],
y: [y, y + height] y: [y, y + height]
} };
}
let cornerPoints = [ // which arcpt does aimpt share a value with?
[x, y], // if they share an x value, we will look for corner y value
[x + width, y], // if they share a y value, we will look for corner x value
[x + width, y + height], // is aim pt non-shared value greater or less than arcpt non-shared value?
[x, y + height] function findWhichTwoCorners(pt, bounds, ...pts) {
].map(([x, y]) => new Point(x, y));
let aimPt = edgePoint(pivotPt, pointerPt, bounds);
aimLine.setAttributeNS(null, 'x2', aimPt.x);
aimLine.setAttributeNS(null, 'y2', aimPt.y);
let angle = calculateAngle(aimPt.x - pivotPt.x, aimPt.y - pivotPt.y);
let arcAngle = arcSize[firingArc.dataset.size];
let distance = Math.sqrt((aimPt.x - pivotPt.x) ** 2 + (aimPt.y - pivotPt.y) ** 2);
let yDelta = distance * Math.cos(angle) * Math.tan(arcAngle);
let xDelta = distance * Math.sin(angle) * Math.tan(arcAngle);
let arcPt1 = new Point(aimPt.x - xDelta, aimPt.y - yDelta);
let arcPt2 = new Point(aimPt.x + xDelta, aimPt.y + yDelta);
arcPt1 = edgePoint(pivotPt, arcPt1, bounds);
arcPt2 = edgePoint(pivotPt, arcPt2, bounds);
firingArcOutline.setAttributeNS(null, 'points', `${arcPt1} ${pivotPt} ${arcPt2}`);
function touchSameEdge({ x: x1, y: y1 }, { x: x2, y: y2 }) {
return x1 === x2 || y1 === y2;
}
function touchOrthogonalEdges({ x: x1, y: y1 }, { x: x2, y: y2 }) {
return (bounds.x.includes(x1) && bounds.y.includes(y2)) || (bounds.y.includes(y1) && bounds.x.includes(x2));
}
function shareValue({ x: x1, y: y1 }, { x: x2, y: y2 }) {
return x1 === x2 || y1 === y2;
}
// which arcpt does aimpt share a value with?
// if they share an x value, we will look for corner y value
// if they share a y value, we will look for corner x value
// is aim pt non-shared value greater or less than arcpt non-shared value?
function findWhichCorners(pt, ...pts) {
const ptVals = Object.values(pt), const ptVals = Object.values(pt),
sharedValPt = pts.find(({ x, y }) => ptVals.includes(x) || ptVals.includes(y)); sharedValPt = pts.find(({ x, y }) => ptVals.includes(x) || ptVals.includes(y));
@ -142,7 +114,7 @@ function position(e, firingArc, firingArcOutline, aimLine, grid) {
return; return;
} }
const nonSharedValKey = pt.x === sharedValPt.x ? 'x' : 'y'; const nonSharedValKey = pt.x === sharedValPt.x ? 'y' : 'x';
let cornerVal; let cornerVal;
if (pt[nonSharedValKey] < sharedValPt[nonSharedValKey]) { if (pt[nonSharedValKey] < sharedValPt[nonSharedValKey]) {
@ -151,40 +123,82 @@ function position(e, firingArc, firingArcOutline, aimLine, grid) {
cornerVal = Math.max(...bounds[nonSharedValKey]); cornerVal = Math.max(...bounds[nonSharedValKey]);
} }
return cornerPoints.filter(cp => cp[nonSharedValKey] === cornerVal); return getCornerPts(bounds).filter(cp => cp[nonSharedValKey] === cornerVal);
} }
function chooseCornerPoints(aimPt, arcPt1, arcPt2, bounds) {
const cornerPoints = getCornerPts(bounds);
let points; let points;
if (touchSameEdge(arcPt1, arcPt2)) { if (touchSameEdge(arcPt1, arcPt2)) {
// 0-corner case // 0-corner case
points = [arcPt2, pivotPt, arcPt1]; points = [];
} else if (touchOrthogonalEdges(arcPt1, arcPt2)) { } else if (touchOrthogonalEdges(arcPt1, arcPt2, bounds)) {
if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) { if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) {
// 1-corner case // 1-corner case
let cp = cornerPoints.find(cp => shareValue(cp, arcPt1) && shareValue(cp, arcPt2)); let cp = cornerPoints.find(cp => shareValue(cp, arcPt1) && shareValue(cp, arcPt2));
points = [arcPt2, pivotPt, arcPt1, cp]; points = [cp];
} else { } else {
// 3-corner case // 3-corner case
points = cornerPoints.filter(cp => !shareValue(cp, arcPt1) || !shareValue(cp, arcPt2)); points = cornerPoints.filter(cp => !shareValue(cp, arcPt1) || !shareValue(cp, arcPt2));
let index = points.findIndex(cp => shareValue(cp, arcPt2));
points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1);
} }
} else { } else {
if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) { if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) {
// 2-corner case, aim and an arc point touch the same edge // 2-corner case, aim and an arc point touch the same edge
points = findWhichCorners(aimPt, arcPt1, arcPt2); points = findWhichTwoCorners(aimPt, bounds, arcPt1, arcPt2);
let index = points.findIndex(cp => shareValue(cp, arcPt2));
points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1);
} else { } else {
// 2-corner case, aim and both arc points all touch different edges // 2-corner case, aim and both arc points all touch different edges
points = cornerPoints.filter(cp => shareValue(cp, aimPt) || shareValue(cp, aimPt)); points = cornerPoints.filter(cp => shareValue(cp, aimPt) || shareValue(cp, aimPt));
let index = points.findIndex(cp => shareValue(cp, arcPt2));
points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1);
} }
} }
firingArc.setAttributeNS(null, 'points', points.join(' ')); return points;
}
function orderPoints(arcPoints, cornerPoints) {
if (cornerPoints.length === 0) {
return arcPoints;
}
const index = cornerPoints.findIndex(cp => shareValue(cp, arcPoints.at(0)));
cornerPoints.splice(index + 1, 0, ...arcPoints);
return cornerPoints;
}
function calcArcLinePtDeltas(aimPt, pivotPt, { dataset: { size }}) {
const angle = calculateAngle(aimPt.x - pivotPt.x, aimPt.y - pivotPt.y),
arcAngle = arcSize[size],
distance = Math.sqrt((aimPt.x - pivotPt.x) ** 2 + (aimPt.y - pivotPt.y) ** 2),
yDelta = distance * Math.cos(angle) * Math.tan(arcAngle),
xDelta = distance * Math.sin(angle) * Math.tan(arcAngle);
return { xDelta, yDelta };
}
function position(e, firingArc, firingArcOutline, aimLine, grid) {
// TODO: handle exactly horizontal and vertical lines?
const pointer = new DOMPoint(e.clientX, e.clientY),
pointerPt = pointer.matrixTransform(grid.getScreenCTM().inverse()),
pivotPt = new Point(aimLine.getAttribute('x1'), aimLine.getAttribute('y1')),
bounds = getBounds(grid.getBBox()),
aimPt = edgePoint(pivotPt, pointerPt, bounds),
{ xDelta, yDelta } = calcArcLinePtDeltas(aimPt, pivotPt, firingArc),
arcPt1 = edgePoint(pivotPt, new Point(aimPt.x - xDelta, aimPt.y - yDelta), bounds),
arcPt2 = edgePoint(pivotPt, new Point(aimPt.x + xDelta, aimPt.y + yDelta), bounds),
outlinePoints = [arcPt2, pivotPt, arcPt1],
cps = chooseCornerPoints(aimPt, arcPt1, arcPt2, bounds),
arcPoints = orderPoints(outlinePoints, cps);
aimLine.setAttributeNS(null, 'x2', aimPt.x);
aimLine.setAttributeNS(null, 'y2', aimPt.y);
firingArcOutline.setAttributeNS(null, 'points', outlinePoints.join(' '));
firingArc.setAttributeNS(null, 'points', arcPoints.join(' '));
} }
function setDataAttrs({ dataset: { allegiance, number }}, el) { function setDataAttrs({ dataset: { allegiance, number }}, el) {
@ -242,7 +256,6 @@ export default function (el) {
arcLayer.appendChild(firingArc); arcLayer.appendChild(firingArc);
outlineLayer.appendChild(firingArcOutline); outlineLayer.appendChild(firingArcOutline);
// const positionListener = position.bind(svg, firingArc, firingArcOutline, aim, grid);
function positionListener(e) { function positionListener(e) {
position(e, firingArc, firingArcOutline, aimLine, grid); position(e, firingArc, firingArcOutline, aimLine, grid);
} }