Skip to content

Commit

Permalink
Yell at the user if they don't have the paths selected for Pathsplitter.
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Lindberg committed Feb 23, 2018
1 parent 3ace45f commit 35b3e47
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 1 deletion.
224 changes: 224 additions & 0 deletions PathSplitter/1.1.1/PathSplitter.js
@@ -0,0 +1,224 @@
/**
* This script provides a way for players and GMs to split paths by their
* intersections with another splitting path.
* This could especially be useful for when corrections need to be made to
* paths used for dynamic lighting.
*
* Simply draw a polygonal path intersecting the path you want to split up.
* Select the main path and then the splitting path.
* Then with the main and splitting paths selected,
* enter the command '!pathSplit'.
* The original path will be divided into new paths separated at the points
* where the splitting path intersected the original path.
*
* This script also works with paths that have been scaled and rotated.'
*
* Requires:
* VectorMath
* MatrixMath
* PathMath
*/

(() => {
'use strict';

const PATHSPLIT_CMD = '!pathSplit';
const PATHSPLIT_COLOR_CMD = '!pathSplitColor';

/**
* A 3-tuple representing a point of intersection between two line segments.
* The first element is a Vector representing the point of intersection in
* 2D homogenous coordinates.
* The second element is the parametric coefficient for the intersection
* along the first segment.
* The third element is the parametric coefficient for the intersection
* along the second segment.
* @typedef {Array} Intersection
*/

/**
* A vector used to define a homogeneous point or a direction.
* @typedef {number[]} Vector
*/

/**
* A line segment defined by two homogenous 2D points.
* @typedef {Vector[]} Segment
*/


// Initialize the script's state if it hasn't already been initialized.
state.PathSplitter = state.PathSplitter || {
splitPathColor: '#ff00ff' // pink
};


function _getSplitSegmentPaths(mainSegments, splitSegments) {
let resultSegPaths = [];
let curPathSegs = [];

_.each(mainSegments, seg1 => {

// Find the points of intersection and their parametric coefficients.
let intersections = [];
_.each(splitSegments, seg2 => {
let i = PathMath.segmentIntersection(seg1, seg2);
if(i)
intersections.push(i);
});

if(intersections.length > 0) {
// Sort the intersections in the order that they appear along seg1.
intersections.sort((a, b) => {
return a[1] - b[1];
});

let lastPt = seg1[0];
_.each(intersections, i => {
// Complete the current segment path.
curPathSegs.push([lastPt, i[0]]);
resultSegPaths.push(curPathSegs);

// Start a new segment path.
curPathSegs = [];
lastPt = i[0];
});
curPathSegs.push([lastPt, seg1[1]]);
}
else {
curPathSegs.push(seg1);
}
});
resultSegPaths.push(curPathSegs);

return resultSegPaths;
};

/**
* Splits mainPath at its intersections with splitPath. The original path
* is removed, being replaced by the new split up paths.
* @param {Path} mainPath
* @param {Path} splitPath
* @return {Path[]}
*/
function splitPathAtIntersections(mainPath, splitPath) {
let mainSegments = PathMath.toSegments(mainPath);
let splitSegments = PathMath.toSegments(splitPath);
let segmentPaths = _getSplitSegmentPaths(mainSegments, splitSegments);

// Convert the list of segment paths into paths.
let _pageid = mainPath.get('_pageid');
let controlledby = mainPath.get('controlledby');
let fill = mainPath.get('fill');
let layer = mainPath.get('layer');
let stroke = mainPath.get('stroke');
let stroke_width = mainPath.get('stroke_width');

let results = [];
_.each(segmentPaths, segments => {
let pathData = PathMath.segmentsToPath(segments);
_.extend(pathData, {
_pageid,
controlledby,
fill,
layer,
stroke,
stroke_width
});
let path = createObj('path', pathData);
results.push(path);
});

// Remove the original path and the splitPath.
mainPath.remove();
splitPath.remove();

return results;
}

on('ready', () => {
let macro = findObjs({
_type: 'macro',
name: 'Pathsplitter'
})[0];

if(!macro) {
let players = findObjs({
_type: 'player'
});
let gms = _.filter(players, player => {
return playerIsGM(player.get('_id'));
});

_.each(gms, gm => {
createObj('macro', {
_playerid: gm.get('_id'),
name: 'Pathsplitter',
action: PATHSPLIT_CMD
});
});
}
});

on('chat:message', msg => {
if(msg.type === 'api' && msg.content === PATHSPLIT_COLOR_CMD) {
try {
let selected = msg.selected;
let path = findObjs({
_type: 'path',
_id: selected[0]._id
})[0];

let stroke = path.get('stroke');
state.PathSplitter.splitPathColor = stroke;
}
catch(err) {
log('!pathSplit ERROR: ' + err.message);
log(err.stack);
}
}
else if(msg.type === 'api' && msg.content === PATHSPLIT_CMD) {
try {
let selected = msg.selected;
if(!selected || selected.length !== 2) {
let msg = `Two paths must be selected: the one you want to split, and the splitting path (color: <span style="background: ${state.PathSplitter.splitPathColor}; width: 16px; height: 16px; padding: 0.2em; font-weight: bold;">${state.PathSplitter.splitPathColor}</span>).`;
sendChat('Pathsplitter', msg);
throw new Error('Two paths must be selected.');
}

let path1 = findObjs({
_type: 'path',
_id: selected[0]._id
})[0];
let path2 = findObjs({
_type: 'path',
_id: selected[1]._id
})[0];

// Determine which path is the main path and which is the
// splitting path.
let mainPath, splitPath;
if(path1.get('stroke') === state.PathSplitter.splitPathColor) {
mainPath = path2;
splitPath = path1;
}
else if(path2.get('stroke') === state.PathSplitter.splitPathColor) {
mainPath = path1;
splitPath = path2;
}
else {
let msg = 'No splitting path selected. ';
msg += `Current split color: <span style="background: ${state.PathSplitter.splitPathColor}; width: 16px; height: 16px; padding: 0.2em; font-weight: bold;">${state.PathSplitter.splitPathColor}</span>`
sendChat('Pathsplitter', msg);

throw new Error('No splitting path selected.');
}
splitPathAtIntersections(mainPath, splitPath);
}
catch(err) {
log('!pathSplit ERROR: ' + err.message);
log(err.stack);
}
}
});
})();
2 changes: 1 addition & 1 deletion PathSplitter/script.json
@@ -1,7 +1,7 @@
{
"name": "Path Splitter",
"script": "PathSplitter.js",
"version": "1.1",
"version": "1.1.1",
"previousversions": ["1.0"],
"description": "# Path Splitter\r\r_Updates:_\r_v1.1:_\r* Pathsplitter now supports elliptical paths.\r* If a splitting path isn't selected, it will display a message in the chat with the current splitting color.\r* When the macro boots up, it installs a macro for its ```!pathsplit``` command.\r\rThis script allows players to split up a polygonal path by drawing another\rpolygonal path on top of it. The original path is split up where it intersects\rthe splitting path. This script also supports scaled and rotated paths.\r\r## To use:\r\r1) Draw a path over the path your want to split up. Set this path to\rthe splitting path color (by default this is pink: ```#ff00ff```).\r\r2) Select the path you want to split and the splitting path.\r\r3) In the chat, enter the command ```!pathSplit``` or activate the ```Pathsplitter``` macro installed with the script.\r\r## Changing the splitting path color:\r\rBy default, the reserved color for the splitting path is pink (```#ff00ff```).\rTo change it, set your splitting path to whichever color you want to use.\rThen, select it and enter the command ```!pathSplitColor``` in the chat.\r\r## Help\r\rIf you experience any issues while using this script,\rneed help using it, or if you have a neat suggestion for a new feature, please\rpost to the script's thread in the API forums or shoot me a PM:\rhttps://app.roll20.net/users/46544/stephen-l\r\r## Show Support\r\rIf you would like to show your appreciation and support for the work I do in writing,\rupdating, and maintaining my API scripts, consider buying one of my art packs from the Roll20 marketplace (https://marketplace.roll20.net/browse/search/?keywords=&sortby=newest&type=all&genre=all&author=Stephen%20Lindberg)\ror, simply leave a thank you note in the script's thread on the Roll20 forums.\rEither is greatly appreciated! Happy gaming!\r",
"authors": "Stephen Lindberg",
Expand Down

0 comments on commit 35b3e47

Please sign in to comment.