bpmn-js 18.26.0 → 18.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,7 @@
1
1
  import inherits from 'inherits-browser';
2
2
 
3
+ import { forEach } from 'min-dash';
4
+
3
5
  import KeyboardBindings from 'diagram-js/lib/features/keyboard/KeyboardBindings';
4
6
 
5
7
  /**
@@ -24,6 +26,24 @@ BpmnKeyboardBindings.$inject = [
24
26
  ];
25
27
 
26
28
 
29
+ /**
30
+ * The keyboard binding for each editor action: the `key` that triggers it,
31
+ * optionally requiring the `cmd` modifier (CTRL/CMD).
32
+ *
33
+ * @type {Record<string, { key: string, modifier?: 'cmd' }>}
34
+ */
35
+ export var SHORTCUTS_KEY_BINDINGS = {
36
+ selectElements: { key: 'A', modifier: 'cmd' },
37
+ find: { key: 'F', modifier: 'cmd' },
38
+ spaceTool: { key: 'S' },
39
+ lassoTool: { key: 'L' },
40
+ handTool: { key: 'H' },
41
+ globalConnectTool: { key: 'C' },
42
+ directEditing: { key: 'E' },
43
+ replaceElement: { key: 'R' }
44
+ };
45
+
46
+
27
47
  /**
28
48
  * Register available keyboard bindings.
29
49
  *
@@ -35,146 +55,35 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio
35
55
  // inherit default bindings
36
56
  KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions);
37
57
 
38
- /**
39
- * Add keyboard binding if respective editor action
40
- * is registered.
41
- *
42
- * @param {string} action name
43
- * @param {Function} fn that implements the key binding
44
- */
45
- function addListener(action, fn) {
46
-
47
- if (editorActions.isRegistered(action)) {
48
- keyboard.addListener(fn);
49
- }
50
- }
51
-
52
- // select all elements
53
- // CTRL + A
54
- addListener('selectElements', function(context) {
55
-
56
- var event = context.keyEvent;
58
+ forEach(SHORTCUTS_KEY_BINDINGS, function(binding, action) {
57
59
 
58
- if (keyboard.isKey([ 'a', 'A' ], event) && keyboard.isCmd(event)) {
59
- editorActions.trigger('selectElements');
60
-
61
- return true;
62
- }
63
- });
64
-
65
- // search labels
66
- // CTRL + F
67
- addListener('find', function(context) {
68
-
69
- var event = context.keyEvent;
70
-
71
- if (keyboard.isKey([ 'f', 'F' ], event) && keyboard.isCmd(event)) {
72
- editorActions.trigger('find');
73
-
74
- return true;
75
- }
76
- });
77
-
78
- // activate space tool
79
- // S
80
- addListener('spaceTool', function(context) {
81
-
82
- var event = context.keyEvent;
83
-
84
- if (keyboard.hasModifier(event)) {
60
+ if (!editorActions.isRegistered(action)) {
85
61
  return;
86
62
  }
87
63
 
88
- if (keyboard.isKey([ 's', 'S' ], event)) {
89
- editorActions.trigger('spaceTool');
64
+ var keys = [ binding.key.toLowerCase(), binding.key.toUpperCase() ],
65
+ requiresCmd = binding.modifier === 'cmd';
90
66
 
91
- return true;
92
- }
93
- });
67
+ keyboard.addListener(function(context) {
94
68
 
95
- // activate lasso tool
96
- // L
97
- addListener('lassoTool', function(context) {
69
+ var event = context.keyEvent;
98
70
 
99
- var event = context.keyEvent;
71
+ if (!keyboard.isKey(keys, event)) {
72
+ return;
73
+ }
100
74
 
101
- if (keyboard.hasModifier(event)) {
102
- return;
103
- }
75
+ if (requiresCmd) {
76
+ if (!keyboard.isCmd(event)) {
77
+ return;
78
+ }
79
+ } else if (keyboard.hasModifier(event)) {
80
+ return;
81
+ }
104
82
 
105
- if (keyboard.isKey([ 'l', 'L' ], event)) {
106
- editorActions.trigger('lassoTool');
83
+ editorActions.trigger(action, event);
107
84
 
108
85
  return true;
109
- }
110
- });
111
-
112
- // activate hand tool
113
- // H
114
- addListener('handTool', function(context) {
115
-
116
- var event = context.keyEvent;
117
-
118
- if (keyboard.hasModifier(event)) {
119
- return;
120
- }
121
-
122
- if (keyboard.isKey([ 'h', 'H' ], event)) {
123
- editorActions.trigger('handTool');
124
-
125
- return true;
126
- }
127
- });
128
-
129
- // activate global connect tool
130
- // C
131
- addListener('globalConnectTool', function(context) {
132
-
133
- var event = context.keyEvent;
134
-
135
- if (keyboard.hasModifier(event)) {
136
- return;
137
- }
138
-
139
- if (keyboard.isKey([ 'c', 'C' ], event)) {
140
- editorActions.trigger('globalConnectTool');
141
-
142
- return true;
143
- }
144
- });
145
-
146
- // activate direct editing
147
- // E
148
- addListener('directEditing', function(context) {
149
-
150
- var event = context.keyEvent;
151
-
152
- if (keyboard.hasModifier(event)) {
153
- return;
154
- }
155
-
156
- if (keyboard.isKey([ 'e', 'E' ], event)) {
157
- editorActions.trigger('directEditing');
158
-
159
- return true;
160
- }
161
- });
162
-
163
- // activate replace element
164
- // R
165
- addListener('replaceElement', function(context) {
166
-
167
- var event = context.keyEvent;
168
-
169
- if (keyboard.hasModifier(event)) {
170
- return;
171
- }
172
-
173
- if (keyboard.isKey([ 'r', 'R' ], event)) {
174
- editorActions.trigger('replaceElement', event);
175
-
176
- return true;
177
- }
86
+ });
178
87
  });
179
88
 
180
89
  };
@@ -1 +1 @@
1
- {"version":3,"file":"LabelLink.d.ts","mappings":";;;;;AA8CAA;IA6IAC;;;;;;;IA7IAC;MAAAA;;;;;;;gBA7Ba,OAAO,8BAA8B,EAAE;cACvC,OAAO,4BAA4B,EAAE;uBACrC,OAAO,qCAAqC,EAAE;eAC9C,OAAO,4BAA4B,EAAE;;eAGrC,OAAO,4BAA4B,EAAE","names":["LabelLink class","LabelLink#$inject","LabelLink constructor"],"ignoreList":[],"sources":["LabelLink.js"]}
1
+ {"version":3,"file":"LabelLink.d.ts","mappings":";;;;;AA+CAA;IA2JAC;;;;;;;IA3JAC;MAAAA;;;;;;;gBA7Ba,OAAO,8BAA8B,EAAE;cACvC,OAAO,4BAA4B,EAAE;uBACrC,OAAO,qCAAqC,EAAE;eAC9C,OAAO,4BAA4B,EAAE;;eAGrC,OAAO,4BAA4B,EAAE","names":["LabelLink class","LabelLink#$inject","LabelLink constructor"],"ignoreList":[],"sources":["LabelLink.js"]}
@@ -9,9 +9,10 @@ import {
9
9
  import { createLine, updateLine } from 'diagram-js/lib/util/RenderUtil';
10
10
  import { getMid, getElementLineIntersection } from 'diagram-js/lib/layout/LayoutUtil';
11
11
  import { getDistancePointPoint } from 'diagram-js/lib/features/bendpoints/GeometricUtil';
12
- import { isLabel } from 'diagram-js/lib/util/ModelUtil';
12
+ import { isConnection, isLabel } from 'diagram-js/lib/util/ModelUtil';
13
13
 
14
14
  import { isAny } from '../modeling/util/ModelingUtil';
15
+ import { getReferencePoint, asLines } from '../../util/LineUtil';
15
16
  import { getRoundRectPath, getCirclePath } from '../../draw/BpmnRenderUtil';
16
17
 
17
18
  /**
@@ -96,9 +97,17 @@ export default function LabelLink(eventBus, canvas, graphicsFactory, outline, se
96
97
  */
97
98
  function createLink(label, target) {
98
99
 
99
- // Create an auxiliary line between label and target mid points
100
+ const labelMid = getMid(label);
101
+
102
+ // For connections, aim at the closest point on the visible connection
103
+ // instead of the geometric mid point of the whole connection.
104
+ const targetReference = isConnection(target)
105
+ ? getReferencePoint(labelMid, asLines(target.waypoints))
106
+ : getMid(target);
107
+
108
+ // Create an auxiliary line between label and target reference points
100
109
  const line = createLine(
101
- [ getMid(target), getMid(label) ],
110
+ [ targetReference, labelMid ],
102
111
  LINE_STYLE
103
112
  );
104
113
  const linePath = line.getAttribute('d');
@@ -113,12 +122,18 @@ export default function LabelLink(eventBus, canvas, graphicsFactory, outline, se
113
122
  return;
114
123
  }
115
124
 
116
- // Calculate the intersection point between line and label
117
- // If the target is a sequence flow, there is no intersection,
118
- // so we link to the middle of it.
119
- const targetSelected = selection.isSelected(target);
120
- const targetPath = targetSelected ? getElementOutlinePath(target) : getElementPath(target);
121
- const targetInter = getElementLineIntersection(targetPath, linePath) || getMid(target);
125
+ // Calculate the intersection point between line and target.
126
+ // For connections there is no shape to intersect, so we link to the
127
+ // closest point on the connection computed above.
128
+ let targetInter;
129
+
130
+ if (isConnection(target)) {
131
+ targetInter = targetReference;
132
+ } else {
133
+ const targetSelected = selection.isSelected(target);
134
+ const targetPath = targetSelected ? getElementOutlinePath(target) : getElementPath(target);
135
+ targetInter = getElementLineIntersection(targetPath, linePath) || getMid(target);
136
+ }
122
137
 
123
138
  // Do not draw a link if the points are too close
124
139
  const distance = getDistancePointPoint(targetInter, labelInter);
@@ -10,26 +10,6 @@
10
10
  */
11
11
  export function getReferencePointDelta(referencePoint: Point, oldBounds: Rect, newBounds: Rect): Point;
12
12
 
13
- /**
14
- * Generates the nearest point (reference point) for a given point
15
- * onto given set of lines
16
- *
17
- * @param point
18
- * @param lines
19
- *
20
- * @return
21
- */
22
- export function getReferencePoint(point: Point, lines: Line[]): Point;
23
-
24
- /**
25
- * Convert the given bounds to a lines array containing all edges
26
- *
27
- * @param bounds
28
- *
29
- * @return
30
- */
31
- export function asEdges(bounds: Rect | Point): Line[];
32
-
33
13
  /**
34
14
  * A component that makes sure that external labels are added
35
15
  * together with respective elements and properly updated (DI wise)
@@ -53,6 +33,7 @@ type TextRenderer = import("../../../draw/TextRenderer").default;
53
33
  type Point = import("diagram-js/lib/util/Types").Point;
54
34
  type Rect = import("diagram-js/lib/util/Types").Rect;
55
35
  export type Line = Point[];
56
-
57
36
  import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
37
+
38
+ export { getReferencePoint, asEdges } from "../../../util/LineUtil";
58
39
  //# sourceMappingURL=LabelBehavior.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LabelBehavior.d.ts","mappings":";;;;;;;;;;AAqSA,uDANW,KAAK,aACL,IAAI,aACJ,IAAI,GAEH,KAAK;;;;;;;;;;;AAkBjB,yCALW,KAAK,SACL,IAAI,EAAE,GAEL,KAAK;;;;;;;;;AAoBjB,gCAJW,KAAI,EAAC,KAAK,GAET,IAAI,EAAE;;;;;;;;qBA9PlBA;;;;;;;IAAAC;;;gBArBa,OAAO,8BAA8B,EAAE;gBACvC,OAAO,aAAa,EAAE;mBACtB,OAAO,gBAAgB,EAAE;oBACzB,OAAO,4BAA4B,EAAE;aAErC,OAAO,2BAA2B,EAAE;YACpC,OAAO,2BAA2B,EAAE;mBAEpC,KAAK,EAAE;;+BApCW,2CAA2C","names":["LabelBehavior class","LabelBehavior constructor"],"ignoreList":[],"sources":["LabelBehavior.js"]}
1
+ {"version":3,"file":"LabelBehavior.d.ts","mappings":";;;;;;;;;;AAiSA,uDANW,KAAK,aACL,IAAI,aACJ,IAAI,GAEH,KAAK;;;;;;;;qBA5NjBA;;;;;;;IAAAC;;;gBArBa,OAAO,8BAA8B,EAAE;gBACvC,OAAO,aAAa,EAAE;mBACtB,OAAO,gBAAgB,EAAE;oBACzB,OAAO,4BAA4B,EAAE;aAErC,OAAO,2BAA2B,EAAE;YACpC,OAAO,2BAA2B,EAAE;mBAEpC,KAAK,EAAE;+BAhCW,2CAA2C","names":["LabelBehavior class","LabelBehavior constructor"],"ignoreList":[],"sources":["LabelBehavior.js"]}
@@ -36,13 +36,9 @@ import {
36
36
  } from 'diagram-js/lib/util/PositionUtil';
37
37
 
38
38
  import {
39
- sortBy
40
- } from 'min-dash';
41
-
42
- import {
43
- getDistancePointLine,
44
- perpendicularFoot
45
- } from './util/GeometricUtil';
39
+ getReferencePoint,
40
+ asEdges
41
+ } from '../../../util/LineUtil';
46
42
 
47
43
  var NAME_PROPERTY = 'name';
48
44
  var TEXT_PROPERTY = 'text';
@@ -299,94 +295,8 @@ export function getReferencePointDelta(referencePoint, oldBounds, newBounds) {
299
295
  }
300
296
 
301
297
  /**
302
- * Generates the nearest point (reference point) for a given point
303
- * onto given set of lines
304
- *
305
- * @param {Point} point
306
- * @param {Line[]} lines
307
- *
308
- * @return {Point}
309
- */
310
- export function getReferencePoint(point, lines) {
311
-
312
- if (!lines.length) {
313
- return;
314
- }
315
-
316
- var nearestLine = getNearestLine(point, lines);
317
-
318
- return perpendicularFoot(point, nearestLine);
319
- }
320
-
321
- /**
322
- * Convert the given bounds to a lines array containing all edges
323
- *
324
- * @param {Rect|Point} bounds
325
- *
326
- * @return {Line[]}
327
- */
328
- export function asEdges(bounds) {
329
- return [
330
- [ // top
331
- {
332
- x: bounds.x,
333
- y: bounds.y
334
- },
335
- {
336
- x: bounds.x + (bounds.width || 0),
337
- y: bounds.y
338
- }
339
- ],
340
- [ // right
341
- {
342
- x: bounds.x + (bounds.width || 0),
343
- y: bounds.y
344
- },
345
- {
346
- x: bounds.x + (bounds.width || 0),
347
- y: bounds.y + (bounds.height || 0)
348
- }
349
- ],
350
- [ // bottom
351
- {
352
- x: bounds.x,
353
- y: bounds.y + (bounds.height || 0)
354
- },
355
- {
356
- x: bounds.x + (bounds.width || 0),
357
- y: bounds.y + (bounds.height || 0)
358
- }
359
- ],
360
- [ // left
361
- {
362
- x: bounds.x,
363
- y: bounds.y
364
- },
365
- {
366
- x: bounds.x,
367
- y: bounds.y + (bounds.height || 0)
368
- }
369
- ]
370
- ];
371
- }
372
-
373
- /**
374
- * Returns the nearest line for a given point by distance
375
- * @param {Point} point
376
- * @param {Line[]} lines
298
+ * Re-exported for backwards compatibility.
377
299
  *
378
- * @return {Line}
300
+ * @see module:lib/util/LineUtil
379
301
  */
380
- function getNearestLine(point, lines) {
381
-
382
- var distances = lines.map(function(l) {
383
- return {
384
- line: l,
385
- distance: getDistancePointLine(point, l)
386
- };
387
- });
388
-
389
- var sorted = sortBy(distances, 'distance');
390
-
391
- return sorted[0].line;
392
- }
302
+ export { getReferencePoint, asEdges } from '../../../util/LineUtil';
@@ -1 +1 @@
1
- {"version":3,"file":"PaletteProvider.d.ts","mappings":";;;;qBA6BAA;EAiBAC;;;;;;;;;;;;EAjBAC;;;;;EA+B0B,qBAFd,cAAc;;;eArDb,OAAO,yCAAyC,EAAE;cAClD,OAAO,uCAAuC,EAAE;sBAChD,OAAO,oCAAoC,EAAE;iBAC7C,OAAO,6BAA6B,EAAE;iBACtC,OAAO,8CAA8C,EAAE;gBACvD,OAAO,4CAA4C,EAAE;qBACrD,OAAO,sDAAsD,EAAE;;sBAG/D,OAAO,yCAAyC,EAAE","names":["PaletteProvider class","PaletteProvider#$inject","PaletteProvider constructor"],"ignoreList":[],"sources":["PaletteProvider.js"]}
1
+ {"version":3,"file":"PaletteProvider.d.ts","mappings":";;;;qBA+BAA;EAiBAC;;;;;;;;;;;;EAjBAC;;;;;EA+B0B,qBAFd,cAAc;;;eArDb,OAAO,yCAAyC,EAAE;cAClD,OAAO,uCAAuC,EAAE;sBAChD,OAAO,oCAAoC,EAAE;iBAC7C,OAAO,6BAA6B,EAAE;iBACtC,OAAO,8CAA8C,EAAE;gBACvD,OAAO,4CAA4C,EAAE;qBACrD,OAAO,sDAAsD,EAAE;;sBAG/D,OAAO,yCAAyC,EAAE","names":["PaletteProvider class","PaletteProvider#$inject","PaletteProvider constructor"],"ignoreList":[],"sources":["PaletteProvider.js"]}
@@ -2,6 +2,8 @@ import {
2
2
  assign
3
3
  } from 'min-dash';
4
4
 
5
+ import { SHORTCUTS_KEY_BINDINGS } from '../keyboard/BpmnKeyboardBindings';
6
+
5
7
  /**
6
8
  * @typedef {import('diagram-js/lib/features/palette/Palette').default} Palette
7
9
  * @typedef {import('diagram-js/lib/features/create/Create').default} Create
@@ -118,6 +120,7 @@ PaletteProvider.prototype.getPaletteEntries = function() {
118
120
  group: 'tools',
119
121
  className: 'bpmn-icon-hand-tool',
120
122
  title: translate('Activate hand tool'),
123
+ shortcut: SHORTCUTS_KEY_BINDINGS.handTool.key,
121
124
  action: {
122
125
  click: function(event) {
123
126
  handTool.activateHand(event);
@@ -128,6 +131,7 @@ PaletteProvider.prototype.getPaletteEntries = function() {
128
131
  group: 'tools',
129
132
  className: 'bpmn-icon-lasso-tool',
130
133
  title: translate('Activate lasso tool'),
134
+ shortcut: SHORTCUTS_KEY_BINDINGS.lassoTool.key,
131
135
  action: {
132
136
  click: function(event) {
133
137
  lassoTool.activateSelection(event);
@@ -138,6 +142,7 @@ PaletteProvider.prototype.getPaletteEntries = function() {
138
142
  group: 'tools',
139
143
  className: 'bpmn-icon-space-tool',
140
144
  title: translate('Activate create/remove space tool'),
145
+ shortcut: SHORTCUTS_KEY_BINDINGS.spaceTool.key,
141
146
  action: {
142
147
  click: function(event) {
143
148
  spaceTool.activateSelection(event);
@@ -148,6 +153,7 @@ PaletteProvider.prototype.getPaletteEntries = function() {
148
153
  group: 'tools',
149
154
  className: 'bpmn-icon-connection-multi',
150
155
  title: translate('Activate global connect tool'),
156
+ shortcut: SHORTCUTS_KEY_BINDINGS.globalConnectTool.key,
151
157
  action: {
152
158
  click: function(event) {
153
159
  globalConnect.start(event);
@@ -8,6 +8,7 @@ declare namespace _default {
8
8
  createPreview: (string | typeof import("diagram-js/lib/features/create/CreatePreview").default)[];
9
9
  } | {
10
10
  palette: (string | typeof import("diagram-js/lib/features/palette/Palette").default)[];
11
+ paletteTooltip: (string | typeof import("diagram-js/lib/features/palette/PaletteTooltip").default)[];
11
12
  } | {
12
13
  lassoTool: (string | typeof import("diagram-js/lib/features/lasso-tool/LassoTool").default)[];
13
14
  } | {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","mappings":";;;;;;;;;;;;;;;;;;;;;4BAQ4B,mBAAmB","names":[],"ignoreList":[],"sources":["index.js"]}
1
+ {"version":3,"file":"index.d.ts","mappings":";;;;;;;;;;;;;;;;;;;;;;4BAQ4B,mBAAmB","names":[],"ignoreList":[],"sources":["index.js"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Generates the nearest point (reference point) for a given point
3
+ * onto a given set of lines.
4
+ *
5
+ * The reference point is clamped to the actual line segments, i.e. it
6
+ * always lies on one of the given lines (docking to a segment end /
7
+ * bendpoint if necessary) instead of on the infinite line extending a
8
+ * segment.
9
+ *
10
+ * @param point
11
+ * @param lines
12
+ *
13
+ * @return
14
+ */
15
+ export function getReferencePoint(point: Point, lines: Line[]): Point | undefined;
16
+
17
+ /**
18
+ * Convert the given bounds to a lines array containing all edges.
19
+ *
20
+ * @param bounds
21
+ *
22
+ * @return
23
+ */
24
+ export function asEdges(bounds: Rect | Point): Line[];
25
+
26
+ /**
27
+ * Convert the given waypoints to a lines array containing all segments.
28
+ *
29
+ * @param waypoints
30
+ *
31
+ * @return
32
+ */
33
+ export function asLines(waypoints: Point[]): Line[];
34
+
35
+ type Point = import("diagram-js/lib/util/Types").Point;
36
+ type Rect = import("diagram-js/lib/util/Types").Rect;
37
+
38
+ export type Line = Point[];
39
+ //# sourceMappingURL=LineUtil.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LineUtil.d.ts","mappings":";;;;;;;;;;;;;;gBA6BA,yBALW,OAAK,OACL,IAAI,GAAE,EAEL,MAAK,EAAC;;;;;;;;;AAoBlB,gCAJW,KAAI,EAAC,KAAK,GAET,IAAI,EAAE;;;;;;;;;AAsDlB,mCAJW,KAAK,EAAE,GAEN,IAAI,EAAE;;aAxFL,OAAO,2BAA2B,EAAE;YACpC,OAAO,2BAA2B,EAAE;;mBAEpC,KAAK,EAAE","names":[],"ignoreList":[],"sources":["LineUtil.js"]}
@@ -0,0 +1,163 @@
1
+ import {
2
+ sortBy
3
+ } from 'min-dash';
4
+
5
+ import {
6
+ getDistancePointPoint
7
+ } from 'diagram-js/lib/features/bendpoints/GeometricUtil';
8
+
9
+ /**
10
+ * @typedef {import('diagram-js/lib/util/Types').Point} Point
11
+ * @typedef {import('diagram-js/lib/util/Types').Rect} Rect
12
+ *
13
+ * @typedef {Point[]} Line
14
+ */
15
+
16
+ /**
17
+ * Generates the nearest point (reference point) for a given point
18
+ * onto a given set of lines.
19
+ *
20
+ * The reference point is clamped to the actual line segments, i.e. it
21
+ * always lies on one of the given lines (docking to a segment end /
22
+ * bendpoint if necessary) instead of on the infinite line extending a
23
+ * segment.
24
+ *
25
+ * @param {Point} point
26
+ * @param {Line[]} lines
27
+ *
28
+ * @return {Point|undefined}
29
+ */
30
+ export function getReferencePoint(point, lines) {
31
+
32
+ if (!lines.length) {
33
+ return;
34
+ }
35
+
36
+ var nearestLine = getNearestLine(point, lines);
37
+
38
+ return getClosestPointOnLine(point, nearestLine);
39
+ }
40
+
41
+ /**
42
+ * Convert the given bounds to a lines array containing all edges.
43
+ *
44
+ * @param {Rect|Point} bounds
45
+ *
46
+ * @return {Line[]}
47
+ */
48
+ export function asEdges(bounds) {
49
+ return [
50
+ [ // top
51
+ {
52
+ x: bounds.x,
53
+ y: bounds.y
54
+ },
55
+ {
56
+ x: bounds.x + (bounds.width || 0),
57
+ y: bounds.y
58
+ }
59
+ ],
60
+ [ // right
61
+ {
62
+ x: bounds.x + (bounds.width || 0),
63
+ y: bounds.y
64
+ },
65
+ {
66
+ x: bounds.x + (bounds.width || 0),
67
+ y: bounds.y + (bounds.height || 0)
68
+ }
69
+ ],
70
+ [ // bottom
71
+ {
72
+ x: bounds.x,
73
+ y: bounds.y + (bounds.height || 0)
74
+ },
75
+ {
76
+ x: bounds.x + (bounds.width || 0),
77
+ y: bounds.y + (bounds.height || 0)
78
+ }
79
+ ],
80
+ [ // left
81
+ {
82
+ x: bounds.x,
83
+ y: bounds.y
84
+ },
85
+ {
86
+ x: bounds.x,
87
+ y: bounds.y + (bounds.height || 0)
88
+ }
89
+ ]
90
+ ];
91
+ }
92
+
93
+ /**
94
+ * Convert the given waypoints to a lines array containing all segments.
95
+ *
96
+ * @param {Point[]} waypoints
97
+ *
98
+ * @return {Line[]}
99
+ */
100
+ export function asLines(waypoints) {
101
+ var lines = [];
102
+
103
+ for (var i = 0; i < waypoints.length - 1; i++) {
104
+ lines.push([ waypoints[i], waypoints[i + 1] ]);
105
+ }
106
+
107
+ return lines;
108
+ }
109
+
110
+ /**
111
+ * Returns the nearest line for a given point by the distance to the
112
+ * closest point on each (clamped) line segment.
113
+ *
114
+ * @param {Point} point
115
+ * @param {Line[]} lines
116
+ *
117
+ * @return {Line}
118
+ */
119
+ function getNearestLine(point, lines) {
120
+
121
+ var distances = lines.map(function(line) {
122
+ return {
123
+ line: line,
124
+ distance: getDistancePointPoint(point, getClosestPointOnLine(point, line))
125
+ };
126
+ });
127
+
128
+ var sorted = sortBy(distances, 'distance');
129
+
130
+ return sorted[0].line;
131
+ }
132
+
133
+ /**
134
+ * Returns the point on a line segment that is closest to the given point,
135
+ * clamped to the segment's start and end.
136
+ *
137
+ * @param {Point} point
138
+ * @param {Line} line
139
+ *
140
+ * @return {Point}
141
+ */
142
+ function getClosestPointOnLine(point, line) {
143
+ var start = line[0],
144
+ end = line[1];
145
+
146
+ var dx = end.x - start.x,
147
+ dy = end.y - start.y;
148
+
149
+ var lengthSquared = dx * dx + dy * dy;
150
+
151
+ if (lengthSquared === 0) {
152
+ return { x: start.x, y: start.y };
153
+ }
154
+
155
+ var t = ((point.x - start.x) * dx + (point.y - start.y) * dy) / lengthSquared;
156
+
157
+ t = Math.max(0, Math.min(1, t));
158
+
159
+ return {
160
+ x: start.x + t * dx,
161
+ y: start.y + t * dy
162
+ };
163
+ }