Web Dev Solutions

Catalin Mititiuc

From 9d02591fac098f9f49eab33f7412da33a62be77e Mon Sep 17 00:00:00 2001 From: Catalin Mititiuc Date: Fri, 26 Apr 2024 12:14:24 -0700 Subject: More refactor of firing arc module --- src/modules/game/firingArc.js | 179 ++++++++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 83 deletions(-) diff --git a/src/modules/game/firingArc.js b/src/modules/game/firingArc.js index cbde0d4..6dfd8cb 100644 --- a/src/modules/game/firingArc.js +++ b/src/modules/game/firingArc.js @@ -23,6 +23,17 @@ const svgns = "http://www.w3.org/2000/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) { yDiff = -yDiff; let angle = Math.abs(Math.atan(yDiff / xDiff)); @@ -67,124 +78,127 @@ function edgePoint({ x: x1, y: y1 }, { x: x2, y: y2 }, { x: [minX, maxX], y: [mi return new Point(...pointCoords); } -class Point { - constructor(x = 0, y = 0) { - this.x = +x; - this.y = +y; - } - - toString() { - return `${this.x},${this.y}`; - } +function touchSameEdge({ x: x1, y: y1 }, { x: x2, y: y2 }) { + return x1 === x2 || y1 === y2; } -function position(e, firingArc, firingArcOutline, aimLine, grid) { - // TODO: handle exactly horizontal and vertical lines? - - let pointer = new DOMPoint(e.clientX, e.clientY); - let pointerPt = pointer.matrixTransform(grid.getScreenCTM().inverse()); +function shareValue({ x: x1, y: y1 }, { x: x2, y: y2 }) { + return x1 === x2 || y1 === y2; +} - let pivotPt = new Point(aimLine.getAttribute('x1'), aimLine.getAttribute('y1')); +function touchOrthogonalEdges({ x: x1, y: y1 }, { x: x2, y: y2 }, bounds) { + return (bounds.x.includes(x1) && bounds.y.includes(y2)) || (bounds.y.includes(y1) && bounds.x.includes(x2)); +} - let { x, y, width, height } = grid.getBBox(); +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)); +} - const bounds = { +function getBounds({ x, y, width, height }) { + return { x: [x, x + width], y: [y, y + height] - } - - let cornerPoints = [ - [x, y], - [x + width, y], - [x + width, y + height], - [x, y + height] - ].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}`); +// 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 findWhichTwoCorners(pt, bounds, ...pts) { + const ptVals = Object.values(pt), + sharedValPt = pts.find(({ x, y }) => ptVals.includes(x) || ptVals.includes(y)); - function touchSameEdge({ x: x1, y: y1 }, { x: x2, y: y2 }) { - return x1 === x2 || y1 === y2; + if (!sharedValPt) { + return; } - 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)); - } + const nonSharedValKey = pt.x === sharedValPt.x ? 'y' : 'x'; + let cornerVal; - function shareValue({ x: x1, y: y1 }, { x: x2, y: y2 }) { - return x1 === x2 || y1 === y2; + if (pt[nonSharedValKey] < sharedValPt[nonSharedValKey]) { + cornerVal = Math.min(...bounds[nonSharedValKey]); + } else { + cornerVal = Math.max(...bounds[nonSharedValKey]); } - // 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), - sharedValPt = pts.find(({ x, y }) => ptVals.includes(x) || ptVals.includes(y)); - - if (!sharedValPt) { - return; - } - - const nonSharedValKey = pt.x === sharedValPt.x ? 'x' : 'y'; - let cornerVal; - - if (pt[nonSharedValKey] < sharedValPt[nonSharedValKey]) { - cornerVal = Math.min(...bounds[nonSharedValKey]); - } else { - cornerVal = Math.max(...bounds[nonSharedValKey]); - } + return getCornerPts(bounds).filter(cp => cp[nonSharedValKey] === cornerVal); +} - return cornerPoints.filter(cp => cp[nonSharedValKey] === cornerVal); - } +function chooseCornerPoints(aimPt, arcPt1, arcPt2, bounds) { + const cornerPoints = getCornerPts(bounds); let points; if (touchSameEdge(arcPt1, arcPt2)) { // 0-corner case - points = [arcPt2, pivotPt, arcPt1]; - } else if (touchOrthogonalEdges(arcPt1, arcPt2)) { + points = []; + } else if (touchOrthogonalEdges(arcPt1, arcPt2, bounds)) { if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) { // 1-corner case let cp = cornerPoints.find(cp => shareValue(cp, arcPt1) && shareValue(cp, arcPt2)); - points = [arcPt2, pivotPt, arcPt1, cp]; + points = [cp]; } else { // 3-corner case 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 { if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) { // 2-corner case, aim and an arc point touch the same edge - points = findWhichCorners(aimPt, arcPt1, arcPt2); - let index = points.findIndex(cp => shareValue(cp, arcPt2)); - points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); + points = findWhichTwoCorners(aimPt, bounds, arcPt1, arcPt2); } else { // 2-corner case, aim and both arc points all touch different edges 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) { @@ -242,7 +256,6 @@ export default function (el) { arcLayer.appendChild(firingArc); outlineLayer.appendChild(firingArcOutline); - // const positionListener = position.bind(svg, firingArc, firingArcOutline, aim, grid); function positionListener(e) { position(e, firingArc, firingArcOutline, aimLine, grid); } -- cgit v1.2.3