Skip to content

Commit

Permalink
Merge branch 'release/0.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
caewok committed Jul 26, 2021
2 parents c788566 + 9eb19aa commit d918cc6
Show file tree
Hide file tree
Showing 7 changed files with 374 additions and 280 deletions.
10 changes: 10 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# 0.3.0
Move to libRuler 0.1 compatibility.
Improvements:
- Wraps libRuler RulerUtilities functions to 3d versions: `iterateGridUnderLine`, `calculateDistance`, `pointsAlmostEqual`.
- Adds user setting to prefer the starting token elevation when measuring.
- Revamps the projection from 3-D to 2-D to account for specific grid types and diagonal rules. This should more closely correspond to user expectations concerning vertical movement in a grid.

Breaking changes due to libRuler changes:
- Relies on the libRuler RulerUtilities functions

# 0.2.5
Correct "jumping token" issue where when the token is moved, it will appear to drift off the path and move twice.

Expand Down
8 changes: 5 additions & 3 deletions scripts/module.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { registerSettings, registerHotkeys } from "./settings.js";
import { registerRuler } from "./patching.js";
import { ProjectElevatedPoint } from "./segments.js";

import { iterateGridUnder3dLine, projectElevatedPoint, projectGridless } from "./utility.js";
export const MODULE_ID = 'elevationruler';
const FORCE_DEBUG = false; // used for logging before dev mode is set up

Expand All @@ -24,7 +24,9 @@ export function log(...args) {
Hooks.once('init', async function() {
log("Initializing Elevation Ruler Options.");

window['elevationRuler'] = { ProjectElevatedPoint: ProjectElevatedPoint };
window['elevationRuler'] = { projectElevatedPoint: projectElevatedPoint,
projectGridless: projectGridless,
iterateGridUnder3dLine: iterateGridUnder3dLine };

});

Expand Down
23 changes: 17 additions & 6 deletions scripts/patching.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ import { MODULE_ID, log } from "./module.js";
import { elevationRulerClear,
elevationRulerAddWaypoint,
elevationRulerRemoveWaypoint,

elevationRulerAnimateToken } from "./ruler.js";

import { elevationRulerAddProperties,
elevationRulerConstructPhysicalPath,
elevationRulerDistanceFunction,
elevationRulerMeasurePhysicalPath,
elevationRulerGetText } from "./segments.js";

import { calculate3dDistance,
iterateGridUnder3dLine_wrapper,
points3dAlmostEqual } from "./utility.js";

export function registerRuler() {

// segment methods (for measuring)
libWrapper.register(MODULE_ID, 'window.libRuler.Segment.prototype.addProperties', elevationRulerAddProperties, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.Segment.prototype.constructPhysicalPath', elevationRulerConstructPhysicalPath, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.Segment.prototype.distanceFunction', elevationRulerDistanceFunction, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.Segment.prototype.text', elevationRulerGetText, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.RulerSegment.prototype.addProperties', elevationRulerAddProperties, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.RulerSegment.prototype.constructPhysicalPath', elevationRulerConstructPhysicalPath, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.RulerSegment.prototype.measurePhysicalPath', elevationRulerMeasurePhysicalPath, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.RulerSegment.prototype.text', elevationRulerGetText, 'WRAPPER');

// move token methods
libWrapper.register(MODULE_ID, 'Ruler.prototype.animateToken', elevationRulerAnimateToken, 'WRAPPER');
Expand All @@ -26,5 +29,13 @@ export function registerRuler() {
libWrapper.register(MODULE_ID, 'Ruler.prototype._addWaypoint', elevationRulerAddWaypoint, 'WRAPPER');
libWrapper.register(MODULE_ID, 'Ruler.prototype._removeWaypoint', elevationRulerRemoveWaypoint, 'WRAPPER');

// utilities
libWrapper.register(MODULE_ID, 'window.libRuler.RulerUtilities.calculateDistance', calculate3dDistance, 'MIXED');
libWrapper.register(MODULE_ID, 'window.libRuler.RulerUtilities.pointsAlmostEqual', points3dAlmostEqual, 'WRAPPER');
libWrapper.register(MODULE_ID, 'window.libRuler.RulerUtilities.iterateGridUnderLine', iterateGridUnder3dLine_wrapper, 'WRAPPER');


log("registerRuler finished!");
}


157 changes: 6 additions & 151 deletions scripts/ruler.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,156 +22,6 @@ import { ElevationAtPoint, toGridDistance } from "./segments.js";
// wrapping the constructor appears not to work.
// see https://github.com/ruipin/fvtt-lib-wrapper/issues/14


/**
* Calculate a new point by projecting the elevated point back onto the 2-D surface
* If the movement on the plane is represented by moving from point A to point B,
* and you also move 'height' distance orthogonal to the plane, the distance is the
* hypotenuse of the triangle formed by A, B, and C, where C is orthogonal to B.
* Project by rotating the vertical triangle 90º, then calculate the new point C.
*
* Cx = { height * (By - Ay) / dist(A to B) } + Bx
* Cy = { height * (Bx - Ax) / dist(A to B) } + By
* @param {{x: number, y: number}} A
* @param {{x: number, y: number}} B
*/



function projectElevatedPoint(A, B, height) {
const distance = CalculateDistance(A, B);
const projected_x = B.x + ((height / distance) * (A.y - B.y));
const projected_y = B.y - ((height / distance) * (A.x - B.x));

return new PIXI.Point(projected_x, projected_y);
}

Object.defineProperty(Ruler.prototype, "projectElevatedPoint", {
value: projectElevatedPoint,
writable: true,
configurable: true
});

function CalculateDistance(A, B) {
const dx = B.x - A.x;
const dy = B.y - A.y;
return Math.hypot(dy, dx);
}

// console.log(Math.hypot(3, 4));
// // expected output: 5
//
// console.log(Math.hypot(5, 12));
// // expected output: 13
//
// let m;
// let o = {x:0, y:0}
// m = ProjectElevatedPoint(o, {x:1, y:0}, 1);
// CalculateDistance(o, m) // 1.414
//
// m = ProjectElevatedPoint(o, {x:3, y:0}, 4);
// CalculateDistance(o, m) // 5
//
// m = ProjectElevatedPoint(o, {x:0, y:3}, 4);
// CalculateDistance(o, m) // 5
//
// m = ProjectElevatedPoint(o, {x:0, y:3}, 4);

// m = distance
// n = height
// A = origin ()
// B = destination (1)
// C = destination with height (2)
// |Ay - By| / m = |Bx - Cx| / n
// |Ax - Bx| / m = |Cy - By| / n
//
// |Bx - Cx| / n = |Ay - By| / m
// |Cy - By| / n = |Ax - Bx| / m
//
// |Bx - Cx| = |Ay - By| * n/m
// |Cy - By| = |Ax - Bx| * n/m
//
// Bx - Cx = ± n/m * (Ay - By)
// Cy - By = ± n/m * (Ax - Bx)
//
// Cx = Bx ± n/m * (Ay - By)
// Cy = By ± n/m * (Ax - Bx)





// will need to update measuring to account for elevation
export function elevationRulerMeasure(wrapped, destination, {gridSpaces=true}={}) {
log("we are measuring!");
log(`${this.waypoints.length} waypoints. ${this.destination_elevation_increment} elevation increments for destination. ${this.elevation_increments.length} elevation waypoints.`, this.elevation_increments);

// if no elevation present, go with original function.
if(!this.destination_elevation_increment &&
(!this.elevation_increments ||
this.elevation_increments.every(i => i === 0))) {

log("Using original measure");
return wrapped(destination, gridSpaces);
}

// Mostly a copy from Ruler.measure, but adding in distance for elevation
// Original segments need to be retained so that the displayed path is correct.
// But the distances need to be modified to account for segment elevation.
// Project the elevated point back to the 2-D space, using a rotated right triangle.
// See, e.g. https://math.stackexchange.com/questions/927802/how-to-find-coordinates-of-3rd-vertex-of-a-right-angled-triangle-when-everything

destination = new PIXI.Point(...canvas.grid.getCenter(destination.x, destination.y));
const waypoints = this.waypoints.concat([destination]);
const waypoints_elevation = this.elevation_increments.concat([this.destination_elevation_increment]);

const r = this.ruler;
this.destination = destination;

log("Measure ruler", r);

// Iterate over waypoints and construct segment rays
// Also create elevation segments, adjusting segments for elevation
// waypoint 0 is added as the origin (see _onDragStart)
// so elevation_waypoint 0 should also be the origin, and so 0
// the for loop uses the next waypoint as destination.
// for loop will count from 0 to waypoints.length - 1

const segments = [];
const elevation_segments = [];
for ( let [i, dest] of waypoints.slice(1).entries() ) {
log(`Processing waypoint ${i}`, dest);

const origin = waypoints[i];
const label = this.labels.children[i];
const ray = new Ray(origin, dest);

// first waypoint is origin; elevation increment is 0.
// need to account for units of the grid
// canvas.scene.data.grid e.g. 140; canvas.scene.data.gridDistance e.g. 5
const elevation = waypoints_elevation[i + 1] * canvas.scene.data.grid;
log("Origin", origin);
log("Destination", dest);
log(`Elevation ${elevation} for i = ${i}.`);


const elevated_dest = this.projectElevatedPoint(origin, dest, elevation);
const ray_elevated = new Ray(origin, elevated_dest);

log("Elevated_dest", elevated_dest);
log("Ray", ray);
log("Elevated Ray", ray_elevated);

if ( ray_elevated.distance < 10 ) {
if ( label ) label.visible = false;
continue;
}
segments.push({ray, label});
elevation_segments.push({ray: ray_elevated, label: label});
}
}


// clear should reset elevation info
export function elevationRulerClear(wrapped, ...args) {
log("we are clearing!", this);
Expand Down Expand Up @@ -260,7 +110,12 @@ export async function elevationRulerAnimateToken(wrapped, token, ray, dx, dy, se
const incremental_elevation = toGridDistance(elevation_increments[segment_num]);

const current_elevation = getProperty(token, "data.elevation");
const destination_point_elevation = ElevationAtPoint(ray.B, undefined, current_elevation);

const ignore_below = game.settings.get(MODULE_ID, "prefer-token-elevation") ?
this.getFlag(MODULE_ID, "starting_token_elevation") :
undefined;

const destination_point_elevation = ElevationAtPoint(ray.B, current_elevation, ignore_below);
const end_elevation = destination_point_elevation + incremental_elevation;

log(`Current token elevation is ${current_elevation}. Will be changed to ${end_elevation}.`);
Expand Down
Loading

0 comments on commit d918cc6

Please sign in to comment.