From 1972498b054910214c36f4a2ce9bee34e1d71706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20V=C3=B6gele?= Date: Sun, 30 Jan 2022 10:16:19 +0100 Subject: [PATCH 1/3] Avoid unnecessary diagonal calculation on hex grids --- src/pathfinding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pathfinding.js b/src/pathfinding.js index 52afdce..97e82ae 100644 --- a/src/pathfinding.js +++ b/src/pathfinding.js @@ -83,7 +83,7 @@ function calculatePath(from, to, previousWaypoints) { if (canvas.grid.diagonalRule === "5105") use5105 = true; let startLayer = 0; - if (use5105) { + if (use5105 && canvas.grid.type === CONST.GRID_TYPES.SQUARE) { previousWaypoints = previousWaypoints.map(w => getGridPositionFromPixelsObj(w)); startLayer = calcNoDiagonals(previousWaypoints) % 2; } From 7ac1c828b69622d252270eca28d28ada176eadaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20V=C3=B6gele?= Date: Sun, 30 Jan 2022 10:51:58 +0100 Subject: [PATCH 2/3] Perform pathfinding on grid-corner to grid-corner basis for tokens with size divisible by two (fixes #144) --- src/foundry_imports.js | 10 ++++++---- src/pathfinding.js | 34 ++++++++++++++++++++-------------- src/util.js | 4 ++++ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/foundry_imports.js b/src/foundry_imports.js index 4eca1d7..a9c90dc 100644 --- a/src/foundry_imports.js +++ b/src/foundry_imports.js @@ -1,12 +1,12 @@ import {highlightMeasurementTerrainRuler, measureDistances} from "./compatibility.js"; -import {getCenterFromGridPositionObj, getGridPositionFromPixels, getGridPositionFromPixelsObj} from "./foundry_fixes.js"; +import {getGridPositionFromPixels, getGridPositionFromPixelsObj, getPixelsFromGridPositionObj} from "./foundry_fixes.js"; import {Line} from "./geometry.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, getTokenShape, highlightTokenShape, sum} from "./util.js"; +import {applyTokenSizeOffset, enumeratedZip, getSnapPointForEntity, getSnapPointForToken, getSnapPointForTokenObj, getTokenShape, highlightTokenShape, sum} from "./util.js"; // This is a modified version of Ruler.moveToken from foundry 0.7.9 export async function moveEntities(draggedEntity, selectedEntities) { @@ -169,9 +169,11 @@ export function measure(destination, options={}) { this.dragRulerRemovePathfindingWaypoints(); if (isToken && isPathfindingEnabled()) { - let path = findPath(getGridPositionFromPixelsObj(this.waypoints[this.waypoints.length - 1]), getGridPositionFromPixelsObj(destination), this.waypoints); + 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 = path.map(point => getCenterFromGridPositionObj(point)) + 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) diff --git a/src/pathfinding.js b/src/pathfinding.js index 97e82ae..35e533f 100644 --- a/src/pathfinding.js +++ b/src/pathfinding.js @@ -1,8 +1,8 @@ -import {getCenterFromGridPositionObj, getGridPositionFromPixelsObj} from "./foundry_fixes.js"; +import {getGridPositionFromPixelsObj, getPixelsFromGridPositionObj} from "./foundry_fixes.js"; import {togglePathfinding} from "./keybindings.js"; import {debugGraphics} from "./main.js"; import {settingsKey} from "./settings.js"; -import {iterPairs} from "./util.js"; +import {getSnapPointForTokenObj, iterPairs} from "./util.js"; let cachedNodes = undefined; let use5105 = false; @@ -15,16 +15,16 @@ export function isPathfindingEnabled() { return game.settings.get(settingsKey, "autoPathfinding") != togglePathfinding; } -export function findPath(from, to, previousWaypoints) { - const lastNode = calculatePath(from, to, previousWaypoints); +export function findPath(from, to, token, previousWaypoints) { + const lastNode = calculatePath(from, to, token, previousWaypoints); if (!lastNode) return null; - paintPathfindingDebug(lastNode); + paintPathfindingDebug(lastNode, token); const path = []; let currentNode = lastNode; while (currentNode) { // TODO Check if the distance doesn't change - if (path.length >= 2 && !canvas.walls.checkCollision(new Ray(getCenterFromGridPositionObj(currentNode.node), getCenterFromGridPositionObj(path[path.length - 2])))) + if (path.length >= 2 && !stepCollidesWithWall(currentNode.node, path[path.length - 2], token)) // Replace last waypoint if the current waypoint leads to a valid path path[path.length - 1] = {x: currentNode.node.x, y: currentNode.node.y}; else @@ -38,7 +38,7 @@ export function wipePathfindingCache() { cachedNodes = undefined; } -function getNode(pos, initialize=true) { +function getNode(pos, token, initialize=true) { pos = {layer: 0, ...pos}; // Copy pos and set pos.layer to the default value if it's unset if (!cachedNodes) cachedNodes = new Array(2); @@ -57,12 +57,12 @@ function getNode(pos, initialize=true) { if (neighborPos.x < 0 || neighborPos.y < 0) continue; // TODO Work with pixels instead of grid locations - if (!canvas.walls.checkCollision(new Ray(getCenterFromGridPositionObj(pos), getCenterFromGridPositionObj(neighborPos)))) { + if (!stepCollidesWithWall(pos, neighborPos, token)) { const isDiagonal = node.x !== neighborPos.x && node.y !== neighborPos.y && canvas.grid.type === CONST.GRID_TYPES.SQUARE; let targetLayer = pos.layer; if (use5105 && isDiagonal) targetLayer = 1 - targetLayer; - const neighbor = getNode({...neighborPos, layer: targetLayer}, false); + const neighbor = getNode({...neighborPos, layer: targetLayer}, token, false); // TODO We currently assume a cost of one or two for all transitions. Change this for difficult terrain support let edgeCost = 1; @@ -77,7 +77,7 @@ function getNode(pos, initialize=true) { return node; } -function calculatePath(from, to, previousWaypoints) { +function calculatePath(from, to, token, previousWaypoints) { if (game.system.id === "pf2e") use5105 = true; if (canvas.grid.diagonalRule === "5105") @@ -87,7 +87,7 @@ function calculatePath(from, to, previousWaypoints) { previousWaypoints = previousWaypoints.map(w => getGridPositionFromPixelsObj(w)); startLayer = calcNoDiagonals(previousWaypoints) % 2; } - const nextNodes = [{node: getNode({...to, layer: startLayer}), cost: 0, estimated: estimateCost(to, from), previous: null}]; + const nextNodes = [{node: getNode({...to, layer: startLayer}, token), cost: 0, estimated: estimateCost(to, from), previous: null}]; const previousNodes = new Set(); while (nextNodes.length > 0) { // Sort by estimated cost, high to low @@ -99,7 +99,7 @@ function calculatePath(from, to, previousWaypoints) { return currentNode; previousNodes.add(currentNode.node); for (const edge of currentNode.node.edges) { - const neighborNode = getNode(edge.target); + const neighborNode = getNode(edge.target, token); if (previousNodes.has(neighborNode)) continue; const neighbor = {node: neighborNode, cost: currentNode.cost + edge.cost, estimated: currentNode.cost + edge.cost + estimateCost(neighborNode, from), previous: currentNode}; @@ -129,7 +129,13 @@ function estimateCost(pos, target) { return Math.max(Math.abs(pos.x - target.x), Math.abs(pos.y - target.y)); } -function paintPathfindingDebug(lastNode) { +function stepCollidesWithWall(from, to, token) { + const stepStart = getSnapPointForTokenObj(getPixelsFromGridPositionObj(from), token); + const stepEnd = getSnapPointForTokenObj(getPixelsFromGridPositionObj(to), token); + return canvas.walls.checkCollision(new Ray(stepStart, stepEnd)); +} + +function paintPathfindingDebug(lastNode, token) { if (!CONFIG.debug.dragRuler) return; @@ -137,7 +143,7 @@ function paintPathfindingDebug(lastNode) { let currentNode = lastNode; while (currentNode) { let text = new PIXI.Text(currentNode.cost.toFixed(0)); - let pixels = getCenterFromGridPositionObj(currentNode.node); + let pixels = getSnapPointForTokenObj(getPixelsFromGridPositionObj(currentNode.node), token); text.anchor.set(0.5, 1.0); text.x = pixels.x; text.y = pixels.y; diff --git a/src/util.js b/src/util.js index dd0eee1..f9bf828 100644 --- a/src/util.js +++ b/src/util.js @@ -86,6 +86,10 @@ export function getSnapPointForToken(x, y, token) { return new PIXI.Point(snapX, snapY); } +export function getSnapPointForTokenObj(pos, token) { + return getSnapPointForToken(pos.x, pos.y, token); +} + export function getSnapPointForMeasuredTemplate(x, y) { if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) { return new PIXI.Point(x, y); From f3e1492edd84234dbc75080d24d141331d175a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20V=C3=B6gele?= Date: Sun, 30 Jan 2022 10:55:10 +0100 Subject: [PATCH 3/3] Release v1.11.1 --- CHANGELOG.md | 5 +++++ module.json | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65facb5..bbfb1d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.11.1 +### Bugfixes +- Fixed a bug that would cause the pathfinding algorithm to make tokens of size 2 and 4 to take an unnecessary step + + ## 1.11.0 ### New features - Drag Ruler now supports pathfinding. Pressing the assigned button will automatically calculate the shortest route to the point you're dragging your token to and add the necessary waypoints to the ruler. diff --git a/module.json b/module.json index 951ff98..1f33ae9 100644 --- a/module.json +++ b/module.json @@ -2,7 +2,7 @@ "name": "drag-ruler", "title": "Drag Ruler", "description": "When dragging a token displays a ruler showing how far you've moved that token.", - "version": "1.11.0", + "version": "1.11.1", "minimumCoreVersion" : "9.245", "compatibleCoreVersion" : "9", "authors": [ @@ -65,7 +65,7 @@ ], "socket": true, "url": "https://github.com/manuelVo/foundryvtt-drag-ruler", - "download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.11.0.zip", + "download": "https://github.com/manuelVo/foundryvtt-drag-ruler/archive/v1.11.1.zip", "manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-drag-ruler/master/module.json", "readme": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/README.md", "changelog": "https://github.com/manuelVo/foundryvtt-drag-ruler/blob/master/CHANGELOG.md",