UNPKG

41.8 kBJavaScriptView Raw
1/*
2 * I need to do the following:
3 * diff angles between vectors
4 * find perpendicular vector to a point on a path
5 * find tangent to a point on a path
6 * transform (translate, rotate) for nodes and edges
7 * es modules so I can pull out just what I need
8 *
9 * Specs to compare:
10 * tests
11 * typescript
12 * maintained (open issues unresolved for a long time?)
13 * node and browser
14*/
15import { assign as assignM } from "lodash";
16import { fromPairs, isFinite, isUndefined, last, toPairs } from "lodash/fp";
17import { degreesToRadians, distance, fromSlope, normalize } from "./spinoffs/Angle";
18import { position } from "points";
19// TODO why doesn't the following work?
20// Also, why doesn't ../node_modules/kaavio/lib/drawers/edges/ exist?
21//import * as edgeDrawers from "kaavio/src/drawers/edges/index";
22//import * as edgeDrawers from "../node_modules/kaavio/src/drawers/edges/index";
23//import * as edgeDrawers from "kaavio/src/drawers/edges/index";
24//import * as edgeDrawers from "kaavio/lib/drawers/edges/index";
25import * as edgeDrawers from "./edge/edgeDrawers";
26// We are using the standard SVG coordinate system where:
27// the origin is the upper-left-most point
28// positive x is to the right
29// positive y is down
30// uses left hand rule, so positive angle is clockwise,
31// starting with 0 pointing to the right
32// The orientation is a unit vector that indicates the orientation of an
33// at a point. When it is attached to a rectangle, we almost always want it to
34// point away from the side to which it is attached.
35export const START_SIDE_TO_ORIENTATION_MAP = {
36 right: [1, 0],
37 bottom: [0, 1],
38 left: [-1, 0],
39 top: [0, -1]
40};
41export const START_SIDE_TO_EMANATION_ANGLE_MAPPINGS = fromPairs(toPairs(START_SIDE_TO_ORIENTATION_MAP).map(function ([startSide, orientation]) {
42 return [startSide, fromSlope([0, 0], orientation)];
43}));
44export const EMANATION_ANGLE_TO_START_SIDE_MAPPINGS = toPairs(START_SIDE_TO_EMANATION_ANGLE_MAPPINGS).reduce(function (acc, [side, angle]) {
45 acc.set(angle, side);
46 return acc;
47}, new Map());
48export const START_SEGMENT_DETAILS_MAPS = toPairs(START_SIDE_TO_ORIENTATION_MAP).map(function ([startSide, orientation]) {
49 const [orientationX, orientationY] = orientation;
50 return {
51 sideAttachedTo: startSide,
52 orientation: orientation,
53 angle: normalize(Math.atan2(orientationY, orientationX))
54 };
55});
56export class SmartPoint {
57 //orientationVector?: SmartVector;
58 constructor(point) {
59 this.angle = () => {
60 return fromSlope([0, 0], this.orientation);
61 };
62 this.fromArray = ([x, y]) => {
63 this.x = x;
64 this.y = y;
65 };
66 this.toArray = () => {
67 return [this.x, this.y];
68 };
69 assignM(this, point);
70 /*
71 if (!isUndefined(this.orientation)) {
72 this.orientationVector = new SmartVector(
73 { x: 0, y: 0 },
74 { x: this.orientation[0], y: this.orientation[1] }
75 );
76 }
77 //*/
78 }
79}
80export class SmartVector {
81 constructor(p0, p1) {
82 this.angleDistance = vector2 => {
83 return distance(this.angle, vector2.angle);
84 };
85 this.p0 = new SmartPoint(p0);
86 this.p1 = new SmartPoint(p1);
87 this.angle = fromSlope(this.p0.toArray(), this.p1.toArray());
88 }
89}
90export class SmartPath {
91 constructor(points, edge) {
92 this.position = (scalar, accuracy) => {
93 const { x, y, angle: degreesFromNorth } = position(this.path.points, scalar, accuracy);
94 /* the points library returns the angle from north, in degrees, increasing CW, so
95 * this has an angle of 0 deg.:
96 *
97 * ^
98 * |
99 * |
100 * |
101 *
102 * and this has an angle of 90 deg.:
103 *
104 * ------->
105 */
106 return {
107 x,
108 y,
109 // convert to radians and use angle orientation of SVG coordinate system
110 angle: normalize(degreesToRadians(degreesFromNorth + 270))
111 };
112 };
113 const smartPoints = points.map(point => new SmartPoint(point));
114 this.points = smartPoints;
115 this.sum = new SmartVector(smartPoints[0], last(smartPoints));
116 if (!isUndefined(edge)) {
117 const { points, markerStart, markerEnd } = edge;
118 this.path = new edgeDrawers[edge.drawAs](smartPoints, markerStart, markerEnd);
119 }
120 }
121}
122// TODO explore using the packages points and angles (and maybe vectory) together
123const smartPath1 = new SmartPath([
124 { x: 50, y: 30, moveTo: true },
125 { x: 50, y: 70, curve: { type: "arc", rx: 20, ry: 20, sweepFlag: 1 } },
126 { x: 150, y: 100, curve: { type: "arc", rx: 20, ry: 20, sweepFlag: 1 } }
127]);
128const smartPath2 = new SmartPath([
129 { x: 100, y: 50, moveTo: true },
130 { x: 50, y: 70, curve: { type: "arc", rx: 20, ry: 20, sweepFlag: 1 } }
131 //{ x: 200, y: 100 }
132]);
133/* OLD CODE BELOW */
134export function addAngles(angle1, angle2) {
135 const sum = angle1 + angle2;
136 const singleRevolutionSum = sum % (2 * Math.PI);
137 return Math.sign(singleRevolutionSum) === -1
138 ? 2 * Math.PI + singleRevolutionSum
139 : singleRevolutionSum;
140}
141// see https://gist.github.com/ahwolf/4349166 and
142// http://www.blackpawn.com/texts/pointinpoly/default.html
143export function crossProduct(u, v) {
144 return u[0] * v[1] - v[0] * u[1];
145}
146export function flipOrientation(orientation) {
147 return orientation.map(orientationScalar => -1 * orientationScalar);
148}
149export function flipSide(side) {
150 return EMANATION_ANGLE_TO_START_SIDE_MAPPINGS.get(reverseAngle(START_SIDE_TO_EMANATION_ANGLE_MAPPINGS[side]));
151}
152export function getMinimumAngleBetweenVectors(vectorDirectionAngle1, vectorDirectionAngle2) {
153 const vectors = [vectorDirectionAngle1, vectorDirectionAngle2];
154 const minVector = Math.min.apply(undefined, vectors);
155 const maxVector = Math.max.apply(undefined, vectors);
156 if (minVector < 0 || maxVector >= 2 * Math.PI) {
157 throw new Error(`getMinimumAngleBetweenVectors(${vectorDirectionAngle1}, ${vectorDirectionAngle2})
158 inputs must be in interval [0, 2 * Math.PI).`);
159 }
160 return (Math.max(vectorDirectionAngle1, vectorDirectionAngle2) -
161 Math.min(vectorDirectionAngle1, vectorDirectionAngle2));
162 /*
163 const diff = addAngles(vectorDirectionAngle1, -1 * vectorDirectionAngle2);
164 return diff <= Math.PI ? diff : diff % Math.PI;
165 //*/
166 //return diff > Math.PI ? diff - Math.PI : diff;
167}
168export function getAngleOfEmanationFromPoint(point) {
169 const [orientationX, orientationY] = point.orientation;
170 return Math.atan2(orientationY, orientationX);
171}
172export function reverseAngle(angle) {
173 return addAngles(angle, Math.PI);
174}
175export function getAngleAtPoint(edge, positionX) {
176 const { id, points, markerStart, markerEnd } = edge;
177 const referencedPath = new edgeDrawers[(edge.drawAs.toLowerCase())](points, markerStart, markerEnd);
178 const tangentLength = 0.02;
179 const firstPointOfTangent = referencedPath.getPointAtPosition(Math.max(0, positionX - tangentLength / 2));
180 const lastPointOfTangent = referencedPath.getPointAtPosition(Math.min(1, positionX + tangentLength / 2));
181 return getAngleFromPointToPoint(firstPointOfTangent, lastPointOfTangent);
182}
183export function getAngleFromPointToPoint({ x: x0, y: y0 }, { x: x1, y: y1 }) {
184 return Math.atan2(y1 - y0, x1 - x0);
185}
186export function getStartSideByOrientation([orientationX, orientationY]) {
187 if (Math.abs(orientationX) > Math.abs(orientationY)) {
188 if (orientationX > 0) {
189 return "right"; //East
190 }
191 else {
192 return "left"; //West
193 }
194 }
195 else {
196 if (orientationY > 0) {
197 return "bottom"; //South
198 }
199 else {
200 return "top"; //North
201 }
202 }
203}
204// see http://blog.acipo.com/matrix-inversion-in-javascript/
205/**
206 * Calculate the inverse matrix.
207 * @returns {Matrix}
208 */
209export function invertMatrix(M) {
210 // I use Guassian Elimination to calculate the inverse:
211 // (1) 'augment' the matrix (left) by the identity (on the right)
212 // (2) Turn the matrix on the left into the identity by elemetry row ops
213 // (3) The matrix on the right is the inverse (was the identity matrix)
214 // There are 3 elemtary row ops: (I combine b and c in my code)
215 // (a) Swap 2 rows
216 // (b) Multiply a row by a scalar
217 // (c) Add 2 rows
218 //if the matrix isn't square: exit (error)
219 if (M.length !== M[0].length) {
220 return;
221 }
222 //create the identity matrix (I), and a copy (C) of the original
223 var i = 0, ii = 0, j = 0, dim = M.length, e = 0, t = 0;
224 var I = [], C = [];
225 for (i = 0; i < dim; i += 1) {
226 // Create the row
227 I[I.length] = [];
228 C[C.length] = [];
229 for (j = 0; j < dim; j += 1) {
230 //if we're on the diagonal, put a 1 (for identity)
231 if (i === j) {
232 I[i][j] = 1;
233 }
234 else {
235 I[i][j] = 0;
236 }
237 // Also, make the copy of the original
238 C[i][j] = M[i][j];
239 }
240 }
241 // Perform elementary row operations
242 for (i = 0; i < dim; i += 1) {
243 // get the element e on the diagonal
244 e = C[i][i];
245 // if we have a 0 on the diagonal (we'll need to swap with a lower row)
246 if (e === 0) {
247 //look through every row below the i'th row
248 for (ii = i + 1; ii < dim; ii += 1) {
249 //if the ii'th row has a non-0 in the i'th col
250 if (C[ii][i] !== 0) {
251 //it would make the diagonal have a non-0 so swap it
252 for (j = 0; j < dim; j++) {
253 e = C[i][j]; //temp store i'th row
254 C[i][j] = C[ii][j]; //replace i'th row by ii'th
255 C[ii][j] = e; //repace ii'th by temp
256 e = I[i][j]; //temp store i'th row
257 I[i][j] = I[ii][j]; //replace i'th row by ii'th
258 I[ii][j] = e; //repace ii'th by temp
259 }
260 //don't bother checking other rows since we've swapped
261 break;
262 }
263 }
264 //get the new diagonal
265 e = C[i][i];
266 //if it's still 0, not invertable (error)
267 if (e === 0) {
268 return;
269 }
270 }
271 // Scale this row down by e (so we have a 1 on the diagonal)
272 for (j = 0; j < dim; j++) {
273 C[i][j] = C[i][j] / e; //apply to original matrix
274 I[i][j] = I[i][j] / e; //apply to identity
275 }
276 // Subtract this row (scaled appropriately for each row) from ALL of
277 // the other rows so that there will be 0's in this column in the
278 // rows above and below this one
279 for (ii = 0; ii < dim; ii++) {
280 // Only apply to other rows (we want a 1 on the diagonal)
281 if (ii === i) {
282 continue;
283 }
284 // We want to change this element to 0
285 e = C[ii][i];
286 // Subtract (the row above(or below) scaled by e) from (the
287 // current row) but start at the i'th column and assume all the
288 // stuff left of diagonal is 0 (which it should be if we made this
289 // algorithm correctly)
290 for (j = 0; j < dim; j++) {
291 C[ii][j] -= e * C[i][j]; //apply to original matrix
292 I[ii][j] -= e * I[i][j]; //apply to identity
293 }
294 }
295 }
296 //we've done all operations, C should be the identity
297 //matrix I should be the inverse:
298 return I;
299}
300// from http://tech.pro/tutorial/1527/matrix-multiplication-in-functional-javascript
301export function multiplyMatrices(m1, m2) {
302 var result = [];
303 for (var i = 0; i < m1.length; i++) {
304 result[i] = [];
305 for (var j = 0; j < m2[0].length; j++) {
306 var sum = 0;
307 for (var k = 0; k < m1[0].length; k++) {
308 sum += m1[i][k] * m2[k][j];
309 }
310 result[i][j] = sum;
311 }
312 }
313 return result;
314}
315/**
316 * rotate
317 *
318 * @param theta (float): rotation angle in radians, measured clockwise
319 * @return transformation matrix for rotation
320 *
321 * Note that for Canvas and SVG, the y axis points down:
322 *
323 * *---------> x
324 * |
325 * |
326 * |
327 * v
328 *
329 * y
330 *
331 * The transformation matrix returned takes this into account and is intentionally
332 * different from the transformation matrix that would be returned if the y-axis
333 * pointed up, as is common in many math classes.
334 */
335export function rotate(theta) {
336 if (!isFinite(theta)) {
337 throw new Error(`Invalid input: rotate(${theta}). Requires a finite number.`);
338 }
339 return [
340 [Math.cos(theta), -1 * Math.sin(theta), 0],
341 [Math.sin(theta), Math.cos(theta), 0],
342 [0, 0, 1]
343 ];
344}
345export function scale([xScale, yScale]) {
346 if (!isFinite(xScale) || !isFinite(yScale)) {
347 throw new Error(`Invalid input: rotate([${xScale}, ${yScale}]). Requires array of two finite numbers.`);
348 }
349 return [[xScale, 0, 0], [0, yScale, 0], [0, 0, 1]];
350}
351export function translate([xTranslation, yTranslation]) {
352 if (!isFinite(xTranslation) || !isFinite(yTranslation)) {
353 throw new Error(`Invalid input: translate([${xTranslation}, ${yTranslation}]). Requires array of two finite numbers.`);
354 }
355 return [[1, 0, xTranslation], [0, 1, yTranslation], [0, 0, 1]];
356}
357const transformations = {
358 rotate,
359 scale,
360 translate
361};
362export function getTransformationMatrix(transformationSequence) {
363 // Start with identity matrix
364 var concatenatedTransformationMatrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
365 transformationSequence.forEach(function (transformation) {
366 var thisTransformationMatrix = transformations[transformation.key](transformation.value);
367 concatenatedTransformationMatrix = multiplyMatrices(concatenatedTransformationMatrix, thisTransformationMatrix);
368 });
369 return concatenatedTransformationMatrix;
370}
371export function multiplyMatrixByVector(transformationMatrix, vector) {
372 var x = vector[0][0] * transformationMatrix[0][0] +
373 vector[1][0] * transformationMatrix[0][1] +
374 vector[2][0] * transformationMatrix[0][2], y = vector[0][0] * transformationMatrix[1][0] +
375 vector[1][0] * transformationMatrix[1][1] +
376 vector[2][0] * transformationMatrix[1][2], z = vector[0][0] * transformationMatrix[2][0] +
377 vector[1][0] * transformationMatrix[2][1] +
378 vector[2][0] * transformationMatrix[2][2];
379 return [[x], [y], [z]];
380}
381/**
382 * sameSide
383 *
384 * Calculate whether the current edge's second point, a, (end of first segment)
385 * and its final point, b, are both on the same side of the referenced edge.
386 *
387 * current edge: pipes/hyphens
388 * referenced edge: dots
389 *
390 * Example of True
391 *
392 * p1
393 * .
394 * .
395 * *------------a
396 * . |
397 * . |
398 * . |
399 * . |
400 * . |
401 * . |
402 * . |
403 * . |
404 * . |
405 * . |
406 * . *-----b
407 * .
408 * .
409 * p2
410 *
411 *
412 * Example of False
413 *
414 * p1
415 * .
416 * *------------a
417 * . |
418 * . |
419 * . |
420 * . |
421 * . |
422 * .|
423 * |.
424 * | .
425 * | .
426 * | .
427 * *-----b .
428 * .
429 * p2
430 *
431 *
432 * @param {Object} p1 - first point of the referenced edge
433 * @param {Object} p2 - last point of the referenced edge
434 * @param {Object} a - last point of the first segment of the current edge (the point following the start point)
435 * @param {Object} b - point where the current edge ends
436 * @return {Boolean) - whether the last point of the first segment of the current edge is on the same side as the last point of the current edge
437 */
438export function sameSide(p1, p2, a, b) {
439 const bMinusA = [b.x - a.x, b.y - a.y];
440 const p1MinusA = [p1.x - a.x, p1.y - a.y];
441 const p2MinusA = [p2.x - a.x, p2.y - a.y];
442 const crossProduct1 = crossProduct(bMinusA, p1MinusA);
443 const crossProduct2 = crossProduct(bMinusA, p2MinusA);
444 return Math.sign(crossProduct1) === Math.sign(crossProduct2);
445}
446export function transform({ element, transformOrigin, transformationSequence }) {
447 const { x, y, width, height } = element;
448 (transformOrigin = transformOrigin || "50% 50%"), (transformationSequence =
449 transformationSequence || []);
450 var transformOriginKeywordMappings = {
451 left: "0%",
452 center: "50%",
453 right: "100%",
454 top: "0%",
455 bottom: "100%"
456 };
457 var transformOriginKeywordMappingsKeys = Object.keys(transformOriginKeywordMappings);
458 var transformOriginPoint = transformOrigin
459 .split(" ")
460 .map(function (value, i) {
461 let numericOrPctValue;
462 let numericValue;
463 if (transformOriginKeywordMappingsKeys.indexOf(value) > -1) {
464 numericOrPctValue = transformOriginKeywordMappings[value];
465 }
466 else {
467 numericOrPctValue = value;
468 }
469 if (numericOrPctValue.indexOf("%") > -1) {
470 var decimalPercent = parseFloat(numericOrPctValue) / 100;
471 if (i === 0) {
472 numericValue = decimalPercent * width;
473 }
474 else {
475 numericValue = decimalPercent * height;
476 }
477 }
478 else if (value.indexOf("em") > -1) {
479 // TODO refactor. this is hacky.
480 numericValue = parseFloat(numericOrPctValue) * 12;
481 }
482 else {
483 numericValue = parseFloat(numericOrPctValue);
484 }
485 if (i === 0) {
486 numericValue += x;
487 }
488 else {
489 numericValue += y;
490 }
491 return numericValue;
492 });
493 // shift origin from top left corner of element bounding box to point specified by transformOrigin (default: center of bounding box)
494 transformationSequence.unshift({
495 key: "translate",
496 value: [transformOriginPoint[0], transformOriginPoint[1]]
497 });
498 // shift origin back to top left corner of element bounding box
499 transformationSequence.push({
500 key: "translate",
501 value: [-1 * transformOriginPoint[0], -1 * transformOriginPoint[1]]
502 });
503 var transformationMatrix = getTransformationMatrix(transformationSequence);
504 var topLeftPoint = [[x], [y], [1]];
505 var bottomRightPoint = [[x + width], [y + height], [1]];
506 var topLeftPointTransformed = multiplyMatrixByVector(transformationMatrix, topLeftPoint);
507 var bottomRightPointTransformed = multiplyMatrixByVector(transformationMatrix, bottomRightPoint);
508 element.x = topLeftPointTransformed[0][0];
509 element.y = topLeftPointTransformed[1][0];
510 element.width = bottomRightPointTransformed[0][0] - element.x;
511 element.height = bottomRightPointTransformed[1][0] - element.y;
512 return element;
513}
514//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VvbS11dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9nZW9tLXV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7O0VBYUU7QUFFRixPQUFPLEVBQUUsTUFBTSxJQUFJLE9BQU8sRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUMzQyxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUU1RSxPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLFFBQVEsRUFDUixTQUFTLEVBQ1QsU0FBUyxFQUNWLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUNsQyx1Q0FBdUM7QUFDdkMscUVBQXFFO0FBQ3JFLGdFQUFnRTtBQUNoRSxnRkFBZ0Y7QUFDaEYsZ0VBQWdFO0FBQ2hFLGdFQUFnRTtBQUNoRSxPQUFPLEtBQUssV0FBVyxNQUFNLG9CQUFvQixDQUFDO0FBV2xELHlEQUF5RDtBQUN6RCw0Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHVCQUF1QjtBQUN2Qix5REFBeUQ7QUFDekQsNENBQTRDO0FBRTVDLHdFQUF3RTtBQUN4RSw4RUFBOEU7QUFDOUUsb0RBQW9EO0FBQ3BELE1BQU0sQ0FBQyxNQUFNLDZCQUE2QixHQUFHO0lBQzNDLEtBQUssRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDYixNQUFNLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ2QsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ2IsR0FBRyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0NBQ2IsQ0FBQztBQUVGLE1BQU0sQ0FBQyxNQUFNLHNDQUFzQyxHQUFHLFNBQVMsQ0FDN0QsT0FBTyxDQUFDLDZCQUE2QixDQUFDLENBQUMsR0FB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
\No newline at end of file