v10 compat: Split measure function into several subfunctions as suggested by Foundry v10

This commit is contained in:
Manuel Vögele
2022-09-30 14:01:07 +02:00
parent a1c10e088d
commit cf74a11f18
2 changed files with 233 additions and 214 deletions
+2 -206
View File
@@ -1,25 +1,8 @@
import {highlightMeasurementTerrainRuler, measureDistances} from "./compatibility.js";
import {
getGridPositionFromPixels,
getGridPositionFromPixelsObj,
getPixelsFromGridPositionObj,
} from "./foundry_fixes.js";
import {Line} from "./geometry.js";
import {getGridPositionFromPixels} from "./foundry_fixes.js";
import {disableSnap, moveWithoutAnimation} from "./keybindings.js";
import {trackRays} from "./movement_tracking.js";
import {findPath, isPathfindingEnabled} from "./pathfinding.js";
import {settingsKey} from "./settings.js";
import {recalculate} from "./socket.js";
import {
applyTokenSizeOffset,
enumeratedZip,
getSnapPointForEntity,
getSnapPointForToken,
getSnapPointForTokenObj,
getTokenShape,
highlightTokenShape,
sum,
} from "./util.js";
import {getSnapPointForToken, highlightTokenShape, sum} from "./util.js";
// This is a modified version of Ruler.moveToken from foundry 0.7.9
export async function moveEntities(draggedEntity, selectedEntities) {
@@ -195,193 +178,6 @@ export function cancelScheduledMeasurement() {
this.deferredMeasurementResolve?.();
}
// This is a modified version of Ruler.measure form foundry 0.7.9
export function measure(destination, options = {}) {
const isToken = this.draggedEntity instanceof Token;
if (isToken && !this.draggedEntity.isVisible) return [];
options.snap = options.snap ?? !disableSnap;
if (options.snap) {
destination = getSnapPointForEntity(destination.x, destination.y, this.draggedEntity);
}
this.dragRulerRemovePathfindingWaypoints();
if (isToken && isPathfindingEnabled.call(this)) {
const from = getGridPositionFromPixelsObj(this.waypoints[this.waypoints.length - 1]);
const to = getGridPositionFromPixelsObj(destination);
let path = findPath(from, to, this.draggedEntity, this.waypoints);
if (path) path.shift();
if (path && path.length > 0) {
path = path.map(point =>
getSnapPointForTokenObj(getPixelsFromGridPositionObj(point), this.draggedEntity),
);
// If the token is snapped to the grid, the first point of the path is already handled by the ruler
if (
path[0].x === this.waypoints[this.waypoints.length - 1].x &&
path[0].y === this.waypoints[this.waypoints.length - 1].y
)
path = path.slice(1);
// If snapping is enabled, the last point of the path is already handled by the ruler
if (options.snap) path = path.slice(0, path.length - 1);
for (const point of path) {
point.isPathfinding = true;
this.labels.addChild(new PreciseText("", CONFIG.canvasTextStyle));
}
this.waypoints = this.waypoints.concat(path);
} else {
// Don't show a path if the pathfinding yields no result to show the user that the destination is unreachable
destination = this.waypoints[this.waypoints.length - 1];
}
}
if (options.gridSpaces === undefined) {
options.gridSpaces = canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS;
}
if (canvas.grid.diagonalRule === "EUCL") {
options.gridSpaces = false;
options.ignoreGrid = true;
}
if (options.ignoreGrid === undefined) {
options.ignoreGrid = false;
}
options.enableTerrainRuler = isToken && game.modules.get("terrain-ruler")?.active;
const waypoints = this.waypoints.concat([destination]);
// Move the waypoints to the center of the grid if a size is used that measures from edge to edge
const centeredWaypoints = isToken
? applyTokenSizeOffset(waypoints, this.draggedEntity)
: duplicate(waypoints);
// Foundries native ruler requires the waypoints to sit in the dead center of the square to work properly
if (!options.enableTerrainRuler && !options.ignoreGrid)
centeredWaypoints.forEach(w => ([w.x, w.y] = canvas.grid.getCenter(w.x, w.y)));
const r = this.ruler;
this.destination = destination;
// Iterate over waypoints and construct segment rays
const segments = [];
const centeredSegments = [];
for (let [i, dest] of waypoints.slice(1).entries()) {
const centeredDest = centeredWaypoints[i + 1];
const origin = waypoints[i];
const centeredOrigin = centeredWaypoints[i];
const label = this.labels.children[i];
const ray = new Ray(origin, dest);
const centeredRay = new Ray(centeredOrigin, centeredDest);
ray.isPrevious = Boolean(origin.isPrevious);
centeredRay.isPrevious = ray.isPrevious;
ray.dragRulerVisitedSpaces = origin.dragRulerVisitedSpaces;
centeredRay.dragRulerVisitedSpaces = ray.dragRulerVisitedSpaces;
ray.dragRulerFinalState = origin.dragRulerFinalState;
centeredRay.dragRulerFinalState = ray.dragRulerFinalState;
if (ray.distance < 10) {
if (label) label.visible = false;
continue;
}
segments.push({ray, label});
centeredSegments.push({ray: centeredRay, label});
}
const shape = isToken ? getTokenShape(this.draggedEntity) : null;
// Compute measured distance
const distances = measureDistances(centeredSegments, this.draggedEntity, shape, options);
let totalDistance = 0;
for (let [i, d] of distances.entries()) {
let s = centeredSegments[i];
s.startDistance = totalDistance;
totalDistance += d;
s.last = i === centeredSegments.length - 1;
s.distance = d;
s.text = this._getSegmentLabel(d, totalDistance, s.last);
}
// Clear the grid highlight layer
const hlt = canvas.grid.highlightLayers[this.name] || canvas.grid.addHighlightLayer(this.name);
hlt.clear();
// Draw measured path
r.clear();
let rulerColor;
if (!options.gridSpaces || canvas.grid.type === CONST.GRID_TYPES.GRIDLESS)
rulerColor = this.dragRulerGetColorForDistance(totalDistance);
else rulerColor = this.color;
for (const [i, s, cs] of enumeratedZip(
[...segments].reverse(),
[...centeredSegments].reverse(),
)) {
const {label, text, last} = cs;
// Draw line segment
const opacityMultiplier = s.ray.isPrevious ? 0.33 : 1;
r.lineStyle(6, 0x000000, 0.5 * opacityMultiplier)
.moveTo(s.ray.A.x, s.ray.A.y)
.lineTo(s.ray.B.x, s.ray.B.y)
.lineStyle(4, rulerColor, 0.25 * opacityMultiplier)
.moveTo(s.ray.A.x, s.ray.A.y)
.lineTo(s.ray.B.x, s.ray.B.y);
// Draw the distance label just after the endpoint of the segment
if (label) {
label.text = text;
label.alpha = last ? 1.0 : 0.5;
label.visible = true;
let labelPosition = {x: s.ray.x0, y: s.ray.y0};
labelPosition.x -= label.width / 2;
labelPosition.y -= label.height / 2;
const rayLine = Line.fromPoints(s.ray.A, s.ray.B);
const rayLabelXHitY = rayLine.calcY(labelPosition.x);
let innerDistance;
// If ray hits top or bottom side of label
if (
rayLine.isVertical ||
rayLabelXHitY < labelPosition.y ||
rayLabelXHitY > labelPosition.y + label.height
)
innerDistance = Math.abs(label.height / 2 / Math.sin(s.ray.angle));
// If ray hits left or right side of label
else innerDistance = Math.abs(label.width / 2 / Math.cos(s.ray.angle));
labelPosition = s.ray.project((s.ray.distance + 50 + innerDistance) / s.ray.distance);
labelPosition.x -= label.width / 2;
labelPosition.y -= label.height / 2;
label.position.set(labelPosition.x, labelPosition.y);
}
// Highlight grid positions
if (isToken && canvas.grid.type !== CONST.GRID_TYPES.GRIDLESS && options.gridSpaces) {
if (options.enableTerrainRuler) {
highlightMeasurementTerrainRuler.call(
this,
cs.ray,
cs.startDistance,
shape,
opacityMultiplier,
);
} else {
const previousSegments = centeredSegments.slice(0, segments.length - 1 - i);
highlightMeasurementNative.call(this, cs.ray, previousSegments, shape, opacityMultiplier);
}
}
}
// Draw endpoints
for (let p of waypoints) {
r.lineStyle(2, 0x000000, 0.5).beginFill(rulerColor, 0.25).drawCircle(p.x, p.y, 8);
}
// Return the measured segments
return segments;
}
export function highlightMeasurementNative(
ray,
previousSegments,