Always draw the ruler from the token's center for larger tokens (even on grids). Highlight all spaces occupied by larger tokens.
This commit is contained in:
+5
-21
@@ -1,30 +1,14 @@
|
||||
import {getPixelsFromGridPosition} from "./foundry_fixes.js"
|
||||
import {getColorForDistance} from "./main.js"
|
||||
import {highlightTokenShape} from "./util.js"
|
||||
|
||||
export function getHexSizeSupportTokenGridCenter(token) {
|
||||
const tokenCenterOffset = CONFIG.hexSizeSupport.getCenterOffset(token)
|
||||
const tokenCenter = {x: token.x + tokenCenterOffset.x, y: token.y + tokenCenterOffset.y}
|
||||
if (token.getFlag("hex-size-support", "borderSize") % 2 === 1)
|
||||
return tokenCenter
|
||||
if (canvas.grid.grid.columns) {
|
||||
let hexOffset = canvas.grid.w / 2
|
||||
if (!CONFIG.hexSizeSupport.getAltOrientationFlag(token))
|
||||
hexOffset *= -1
|
||||
tokenCenter.x += hexOffset
|
||||
}
|
||||
else {
|
||||
let hexOffset = canvas.grid.h / 2
|
||||
if (CONFIG.hexSizeSupport.getAltOrientationFlag(token))
|
||||
hexOffset *= -1
|
||||
tokenCenter.y += hexOffset
|
||||
}
|
||||
return tokenCenter
|
||||
return {x: token.x + tokenCenterOffset.x, y: token.y + tokenCenterOffset.y}
|
||||
}
|
||||
|
||||
export function highlightMeasurementTerrainRuler(ray, startDistance) {
|
||||
for (const space of ray.terrainRulerVisitedSpaces) {
|
||||
const [x, y] = getPixelsFromGridPosition(space.x, space.y);
|
||||
export function highlightMeasurementTerrainRuler(ray, startDistance, tokenShape=[{x: 0, y: 0}]) {
|
||||
for (const space of ray.terrainRulerVisitedSpaces.reverse()) {
|
||||
const color = getColorForDistance.call(this, startDistance, space.distance)
|
||||
canvas.grid.highlightPosition(this.name, {x, y, color: color})
|
||||
highlightTokenShape.call(this, space, tokenShape, color)
|
||||
}
|
||||
}
|
||||
|
||||
+31
-23
@@ -1,6 +1,7 @@
|
||||
import {highlightMeasurementTerrainRuler} from "./compatibility.js";
|
||||
import {getGridPositionFromPixels} from "./foundry_fixes.js";
|
||||
import {getColorForDistance} from "./main.js"
|
||||
import {zip} from "./util.js"
|
||||
import {applyTokenSizeOffset, getSnapPointForToken, getTokenShape, getTokenSize, highlightTokenShape, zip} from "./util.js";
|
||||
|
||||
// This is a modified version of Ruler.moveToken from foundry 0.7.9
|
||||
export async function moveTokens(draggedToken, selectedTokens) {
|
||||
@@ -104,9 +105,12 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
return []
|
||||
|
||||
if (snap)
|
||||
destination = new PIXI.Point(...canvas.grid.getCenter(destination.x, destination.y));
|
||||
destination = getSnapPointForToken(destination.x, destination.y, this.draggedToken)
|
||||
|
||||
const waypoints = this.waypoints.concat([destination]);
|
||||
const centeredWaypoints = waypoints.map(w => new PIXI.Point(...canvas.grid.getCenter(w.x, w.y)))
|
||||
// Move the waypoints to the center of the grid if a size is used that measures from edge to edge
|
||||
const centeredWaypoints = applyTokenSizeOffset(waypoints, this.draggedToken)
|
||||
|
||||
const r = this.ruler;
|
||||
this.destination = destination;
|
||||
|
||||
@@ -138,10 +142,10 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
|
||||
let totalDistance = 0;
|
||||
for (let [i, d] of distances.entries()) {
|
||||
let s = segments[i];
|
||||
let s = centeredSegments[i];
|
||||
s.startDistance = totalDistance
|
||||
totalDistance += d;
|
||||
s.last = i === (segments.length - 1);
|
||||
s.last = i === (centeredSegments.length - 1);
|
||||
s.distance = d;
|
||||
s.text = this._getSegmentLabel(d, totalDistance, s.last);
|
||||
}
|
||||
@@ -157,27 +161,28 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
rulerColor = getColorForDistance.call(this, totalDistance)
|
||||
else
|
||||
rulerColor = this.color
|
||||
const shape = getTokenShape(this.draggedToken)
|
||||
for (const [s, cs] of zip(segments.reverse(), centeredSegments.reverse())) {
|
||||
const { ray, label, text, last } = s;
|
||||
const { label, text, last } = cs;
|
||||
|
||||
// Draw line segment
|
||||
r.lineStyle(6, 0x000000, 0.5).moveTo(ray.A.x, ray.A.y).lineTo(ray.B.x, ray.B.y)
|
||||
.lineStyle(4, rulerColor, 0.25).moveTo(ray.A.x, ray.A.y).lineTo(ray.B.x, ray.B.y);
|
||||
r.lineStyle(6, 0x000000, 0.5).moveTo(s.ray.A.x, s.ray.A.y).lineTo(s.ray.B.x, s.ray.B.y)
|
||||
.lineStyle(4, rulerColor, 0.25).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 = ray.project((ray.distance + 50) / ray.distance);
|
||||
let labelPosition = cs.ray.project((cs.ray.distance + 50) / cs.ray.distance);
|
||||
label.position.set(labelPosition.x, labelPosition.y);
|
||||
}
|
||||
|
||||
// Highlight grid positions
|
||||
if (terrainRulerAvailable)
|
||||
highlightMeasurementTerrainRuler.call(this, cs.ray, s.startDistance)
|
||||
highlightMeasurementTerrainRuler.call(this, cs.ray, cs.startDistance, shape)
|
||||
else
|
||||
highlightMeasurementNative.call(this, ray, s.startDistance);
|
||||
highlightMeasurementNative.call(this, cs.ray, cs.startDistance, shape);
|
||||
}
|
||||
|
||||
// Draw endpoints
|
||||
@@ -189,7 +194,7 @@ export function measure(destination, {gridSpaces=true, snap=false} = {}) {
|
||||
return segments;
|
||||
}
|
||||
|
||||
export function highlightMeasurementNative(ray, startDistance) {
|
||||
export function highlightMeasurementNative(ray, startDistance, tokenShape=[{x: 0, y: 0}]) {
|
||||
const spacer = canvas.scene.data.gridType === CONST.GRID_TYPES.SQUARE ? 1.41 : 1;
|
||||
const nMax = Math.max(Math.floor(ray.distance / (spacer * Math.min(canvas.grid.w, canvas.grid.h))), 1);
|
||||
const tMax = Array.fromRange(nMax+1).map(t => t / nMax);
|
||||
@@ -198,7 +203,7 @@ export function highlightMeasurementNative(ray, startDistance) {
|
||||
let prior = null;
|
||||
|
||||
// Iterate over ray portions
|
||||
for ( let [i, t] of tMax.entries() ) {
|
||||
for ( let [i, t] of tMax.reverse().entries() ) {
|
||||
let {x, y} = ray.project(t);
|
||||
|
||||
// Get grid position
|
||||
@@ -208,23 +213,26 @@ export function highlightMeasurementNative(ray, startDistance) {
|
||||
|
||||
// Highlight the grid position
|
||||
let [xg, yg] = canvas.grid.grid.getPixelsFromGridPosition(x1, y1);
|
||||
let subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xg, y: yg})}], {gridSpaces: true})[0]
|
||||
let color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
canvas.grid.highlightPosition(this.name, {x: xg, y: yg, color: color});
|
||||
const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xg, y: yg})}], {gridSpaces: true})[0]
|
||||
const color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedToken);
|
||||
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
|
||||
|
||||
// Skip the first one
|
||||
prior = [x1, y1];
|
||||
if ( i === 0 ) continue;
|
||||
|
||||
// If the positions are not neighbors, also highlight their halfway point
|
||||
if ( !canvas.grid.isNeighbor(x0, y0, x1, y1) ) {
|
||||
let th = tMax[i - 1] + (0.5 / nMax);
|
||||
if (i > 0 && !canvas.grid.isNeighbor(x0, y0, x1, y1)) {
|
||||
let th = tMax[i - 1] - (0.5 / nMax);
|
||||
let {x, y} = ray.project(th);
|
||||
let [x1h, y1h] = canvas.grid.grid.getGridPositionFromPixels(x, y);
|
||||
let [xgh, ygh] = canvas.grid.grid.getPixelsFromGridPosition(x1h, y1h);
|
||||
subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xgh, y: ygh})}], {gridSpaces: true})[0]
|
||||
color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
canvas.grid.highlightPosition(this.name, {x: xgh, y: ygh, color: color});
|
||||
const subDistance = canvas.grid.measureDistances([{ray: new Ray(ray.A, {x: xgh, y: ygh})}], {gridSpaces: true})[0]
|
||||
const color = dragRuler.getColorForDistance.call(this, startDistance, subDistance)
|
||||
const snapPoint = getSnapPointForToken(...canvas.grid.getTopLeft(x, y), this.draggedToken);
|
||||
const [snapX, snapY] = getGridPositionFromPixels(snapPoint.x + 1, snapPoint.y + 1);
|
||||
highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color);
|
||||
}
|
||||
|
||||
highlightTokenShape.call(this, {x: snapX, y: snapY}, tokenShape, color);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -6,6 +6,7 @@ import {measure, moveTokens, onMouseMove} from "./foundry_imports.js"
|
||||
import {performMigrations} from "./migration.js"
|
||||
import {registerSettings, settingsKey} from "./settings.js"
|
||||
import {SpeedProvider} from "./speed_provider.js"
|
||||
import { getSnapPointForToken } from "./util.js"
|
||||
|
||||
Hooks.once("init", () => {
|
||||
registerSettings()
|
||||
@@ -158,7 +159,7 @@ function onTokenLeftDragStart(event) {
|
||||
if (canvas.grid.isHex && game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(this))
|
||||
tokenCenter = getHexSizeSupportTokenGridCenter(this)
|
||||
else
|
||||
tokenCenter = {x: this.x + canvas.grid.grid.w / 2, y: this.y + canvas.grid.grid.h / 2}
|
||||
tokenCenter = this.center
|
||||
ruler.clear();
|
||||
ruler._state = Ruler.STATES.STARTING;
|
||||
ruler.rulerOffset = {x: tokenCenter.x - event.data.origin.x, y: tokenCenter.y - event.data.origin.y}
|
||||
@@ -216,10 +217,8 @@ function onRulerMoveToken(event) {
|
||||
|
||||
function addWaypoint(point, snap=true) {
|
||||
if (snap)
|
||||
point = canvas.grid.getCenter(point.x, point.y);
|
||||
else
|
||||
point = [point.x, point.y]
|
||||
this.waypoints.push(new PIXI.Point(point[0], point[1]));
|
||||
point = getSnapPointForToken(point.x, point.y, this.draggedToken)
|
||||
this.waypoints.push(new PIXI.Point(point.x, point.y));
|
||||
this.labels.addChild(new PreciseText("", CONFIG.canvasTextStyle));
|
||||
}
|
||||
|
||||
|
||||
+134
@@ -1,5 +1,139 @@
|
||||
import {getPixelsFromGridPosition} from "./foundry_fixes.js"
|
||||
|
||||
export function* zip(it1, it2) {
|
||||
for (let i = 0;i < Math.min(it1.length, it2.length);i++) {
|
||||
yield [it1[i], it2[i]]
|
||||
}
|
||||
}
|
||||
|
||||
export function getSnapPointForToken(x, y, token) {
|
||||
if (canvas.grid.isHex && game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(token)) {
|
||||
if (token.getFlag("hex-size-support", "borderSize") % 2 === 0) {
|
||||
const snapPoint = CONFIG.hexSizeSupport.findVertexSnapPoint(x, y, token, canvas.grid.grid)
|
||||
return new PIXI.Point(snapPoint.x, snapPoint.y)
|
||||
}
|
||||
else {
|
||||
return new PIXI.Point(...canvas.grid.getCenter(x, y))
|
||||
}
|
||||
}
|
||||
if (canvas.grid.isHex || token.data.width % 2 === 1) {
|
||||
return new PIXI.Point(...canvas.grid.getCenter(x, y))
|
||||
}
|
||||
const [snappedX, snappedY] = canvas.grid.getCenter(x - canvas.grid.w / 2, y - canvas.grid.h / 2)
|
||||
return new PIXI.Point(snappedX + canvas.grid.w / 2, snappedY + canvas.grid.h / 2)
|
||||
}
|
||||
|
||||
export function highlightTokenShape(position, shape, color) {
|
||||
for (const space of shape) {
|
||||
let gridX = position.x + space.x;
|
||||
let gridY = position.y + space.y;
|
||||
if (canvas.grid.isHex) {
|
||||
let shiftedRow;
|
||||
if (canvas.grid.grid.options.even)
|
||||
shiftedRow = 1
|
||||
else
|
||||
shiftedRow = 0
|
||||
if (canvas.grid.grid.options.columns) {
|
||||
if (space.x % 2 !== 0 && position.x % 2 !== shiftedRow) {
|
||||
gridY += 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (space.y % 2 !== 0 && position.y % 2 !== shiftedRow) {
|
||||
gridX += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
const [x, y] = getPixelsFromGridPosition(gridX, gridY);
|
||||
canvas.grid.highlightPosition(this.name, {x, y, color})
|
||||
}
|
||||
}
|
||||
|
||||
export function getTokenShape(token) {
|
||||
if (token.scene.data.gridType === CONST.GRID_TYPES.GRIDLESS)
|
||||
throw new Error("getTokenShape cannot be called for tokens on gridless maps")
|
||||
if (token.scene.data.gridType === CONST.GRID_TYPES.SQUARE) {
|
||||
const topOffset = -Math.floor(token.data.height / 2)
|
||||
const leftOffset = -Math.floor(token.data.width / 2)
|
||||
const shape = []
|
||||
for (let y = 0;y < token.data.height;y++) {
|
||||
for (let x = 0;x < token.data.width;x++) {
|
||||
shape.push({x: x + leftOffset, y: y + topOffset})
|
||||
}
|
||||
}
|
||||
return shape
|
||||
}
|
||||
else {
|
||||
// Hex grids
|
||||
if (game.modules.get("hex-size-support")?.active && CONFIG.hexSizeSupport.getAltSnappingFlag(token)) {
|
||||
const borderSize = token.data.flags["hex-size-support"].borderSize;
|
||||
let shape = [{x: 0, y: 0}];
|
||||
if (borderSize >= 2)
|
||||
shape = shape.concat([{x: 0, y: -1}, {x: -1, y: -1}]);
|
||||
if (borderSize >= 3)
|
||||
shape = shape.concat([{x: 0, y: 1}, {x: -1, y: 1}, {x: -1, y: 0}, {x: 1, y: 0}]);
|
||||
if (borderSize >= 4)
|
||||
shape = shape.concat([{x: -2, y: -1}, {x: 1, y: -1}, {x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}])
|
||||
|
||||
if (Boolean(CONFIG.hexSizeSupport.getAltOrientationFlag(token)) !== canvas.grid.grid.options.columns)
|
||||
shape.forEach(space => space.y *= -1);
|
||||
if (canvas.grid.grid.options.columns)
|
||||
shape = shape.map(space => {return {x: space.y, y: space.x}});
|
||||
return shape;
|
||||
}
|
||||
else {
|
||||
return [{x: 0, y: 0}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getTokenSize(token) {
|
||||
let w, h;
|
||||
const hexSizeSupportBorderSize = token.data.flags["hex-size-support"]?.borderSize;
|
||||
if (hexSizeSupportBorderSize > 0) {
|
||||
w = h = hexSizeSupportBorderSize
|
||||
}
|
||||
else {
|
||||
w = token.data.width
|
||||
h = token.data.height
|
||||
}
|
||||
return {w, h};
|
||||
}
|
||||
|
||||
// Tokens that have a size divisible by two (2x2, 4x4, 2x1) have their ruler at the edge of a cell.
|
||||
// This function applies an offset to to the waypoints that will move the ruler from the edge to the center of the cell
|
||||
export function applyTokenSizeOffset(waypoints, token) {
|
||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||
return waypoints
|
||||
}
|
||||
|
||||
const tokenSize = getTokenSize(token);
|
||||
const waypointOffset = {x: 0, y: 0};
|
||||
if (canvas.grid.isHex) {
|
||||
const isAltOrientation = CONFIG.hexSizeSupport.getAltOrientationFlag(token);
|
||||
if (canvas.grid.grid.options.columns) {
|
||||
if (tokenSize.w % 2 === 0) {
|
||||
waypointOffset.x = canvas.grid.w / 2;
|
||||
if (!isAltOrientation)
|
||||
waypointOffset.x *= -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tokenSize.h % 2 === 0) {
|
||||
waypointOffset.y = canvas.grid.h / 2;
|
||||
if (isAltOrientation)
|
||||
waypointOffset.y *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tokenSize.w % 2 === 0) {
|
||||
waypointOffset.x = canvas.grid.w / 2;
|
||||
}
|
||||
if (tokenSize.h % 2 === 0) {
|
||||
waypointOffset.y = canvas.grid.h / 2;
|
||||
}
|
||||
}
|
||||
|
||||
return waypoints.map(w => new PIXI.Point(w.x + waypointOffset.x, w.y + waypointOffset.y))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user