bpmn-auto-layout 0.2.0 → 0.4.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.
package/lib/DiUtil.js DELETED
@@ -1,263 +0,0 @@
1
- 'use strict';
2
-
3
- var ORIENTATION_THRESHOLD = {
4
- 'h:h': 20,
5
- 'v:v': 20,
6
- 'h:v': -10,
7
- 'v:h': -10
8
- };
9
- var ALIGNED_THRESHOLD = 2;
10
-
11
- function getExpandedBounds(element) {
12
- var bounds = {
13
- x: Number.MAX_SAFE_INTEGER,
14
- y: Number.MAX_SAFE_INTEGER,
15
- width: 100,
16
- height: 80
17
- };
18
- var padding = 36 / 2;
19
- if (element.flowElements) {
20
-
21
- // determine x/y bounds
22
- element.flowElements.forEach(flowElement => {
23
- var flowBounds = flowElement.bounds;
24
- if (flowBounds) {
25
- bounds.x = flowBounds.x < bounds.x ? flowBounds.x : bounds.x;
26
- bounds.y = flowBounds.y < bounds.y ? flowBounds.y : bounds.y;
27
- }
28
- });
29
-
30
- // determine width/height bounds
31
- element.flowElements.forEach(flowElement => {
32
- var flowBounds = flowElement.bounds;
33
- if (flowBounds) {
34
- var newWidth = (flowBounds.x - bounds.x) + flowBounds.width;
35
- var newHeight = (flowBounds.y - bounds.y) + flowBounds.height;
36
- bounds.width = bounds.width < newWidth ? newWidth : bounds.width;
37
- bounds.height = bounds.height < newHeight ? newHeight : bounds.height;
38
- }
39
- });
40
- bounds.x -= padding;
41
- bounds.y -= padding;
42
- bounds.width += (padding * 2);
43
- bounds.height += padding * 2;
44
- }
45
- return bounds;
46
- }
47
-
48
- // see documentation bpmn.io/diagram-js/layout/ManhattanLayout
49
- function connectRectangles(source, target, preferredLayout = 'h:h') {
50
- var threshold = ORIENTATION_THRESHOLD[preferredLayout] || 0;
51
- var orientation = getOrientation(source, target, threshold);
52
- var directions = getDirections(orientation, preferredLayout);
53
- var start = getMid(source);
54
- var end = getMid(target);
55
-
56
- // overlapping elements
57
- if (!directions) {
58
- return;
59
- }
60
- if (directions === 'h:h') {
61
- switch (orientation) {
62
- case 'top-right':
63
- case 'right':
64
- case 'bottom-right':
65
- start = { original: start, x: source.x, y: start.y };
66
- end = { original: end, x: target.x + target.width, y: end.y };
67
- break;
68
- case 'top-left':
69
- case 'left':
70
- case 'bottom-left':
71
- start = { original: start, x: source.x + source.width, y: start.y };
72
- end = { original: end, x: target.x, y: end.y };
73
- break;
74
- }
75
- } else if (directions === 'v:v') {
76
- switch (orientation) {
77
- case 'top-left':
78
- case 'top':
79
- case 'top-right':
80
- start = { original: start, x: start.x, y: source.y + source.height };
81
- end = { original: end, x: end.x, y: target.y };
82
- break;
83
- case 'bottom-left':
84
- case 'bottom':
85
- case 'bottom-right':
86
- start = { original: start, x: start.x, y: source.y };
87
- end = { original: end, x: end.x, y: target.y + target.height };
88
- break;
89
- }
90
- } else if (directions === 'v:h') {
91
- switch (orientation) {
92
- case 'top-left':
93
- case 'top':
94
- case 'top-right':
95
- start = { original: start, x: start.x, y: source.y + source.height };
96
- end = { original: end, x: target.x + target.width, y: end.y };
97
- break;
98
- case 'bottom-left':
99
- case 'bottom':
100
- case 'bottom-right':
101
- start = { original: start, x: start.x, y: source.y };
102
- end = { original: end, x: target.x, y: end.y };
103
- break;
104
- }
105
- } else if (directions === 'h:v') {
106
- switch (orientation) {
107
- case 'top-left':
108
- case 'top':
109
- case 'top-right':
110
- start = { original: start, x: source.x, y: start.y };
111
- end = { original: end, x: end.x, y: target.y };
112
- break;
113
- case 'bottom-left':
114
- case 'bottom':
115
- case 'bottom-right':
116
- start = { original: start, x: start.x, y: source.y };
117
- start = { original: start, x: source.x + source.width, y: start.y };
118
- break;
119
- }
120
- }
121
-
122
- return connectPoints(start, end, directions);
123
- }
124
-
125
- function connectPoints(a, b, directions) {
126
- var points = [];
127
- if (!pointsAligned(a, b)) {
128
- points = getBendpoints(a, b, directions);
129
- }
130
- points.unshift(a);
131
- points.push(b);
132
- return points;
133
- }
134
-
135
- function getBendpoints(a, b, directions) {
136
- directions = directions || 'h:h';
137
- var xmid, ymid;
138
-
139
- // one point, next to a
140
- if (directions === 'h:v') {
141
- return [ { x: b.x, y: a.y } ];
142
- } else
143
-
144
- // one point, above a
145
- if (directions === 'v:h') {
146
- return [ { x: a.x, y: b.y } ];
147
- } else {
148
-
149
- // vertical edge xmid
150
- if (directions === 'h:h') {
151
- xmid = Math.round((b.x - a.x) / 2 + a.x);
152
- return [
153
- { x: xmid, y: a.y },
154
- { x: xmid, y: b.y }
155
- ];
156
- } else {
157
-
158
- // horizontal edge ymid
159
- if (directions === 'v:v') {
160
- ymid = Math.round((b.y - a.y) / 2 + a.y);
161
- return [
162
- { x: a.x, y: ymid },
163
- { x: b.x, y: ymid }
164
- ];
165
- } else {
166
- throw new Error(
167
- 'unknown directions: <' + directions + '>: ' +
168
- 'directions must be specified as {a direction}:{b direction} (direction in h|v)');
169
- }
170
- }
171
- }
172
- }
173
-
174
- // see documentation bpmn.io/diagram-js/layout/LayoutUtil &&
175
- // bpmn.io/diagram-js/util/geometry
176
- function getMid(bounds) {
177
- return roundPoint({
178
- x: bounds.x + (bounds.width || 0) / 2,
179
- y: bounds.y + (bounds.height || 0) / 2
180
- });
181
- }
182
-
183
- function pointsAligned(a, b) {
184
- if (Math.abs(a.x - b.x) <= ALIGNED_THRESHOLD) {
185
- return 'h';
186
- }
187
- if (Math.abs(a.y - b.y) <= ALIGNED_THRESHOLD) {
188
- return 'v';
189
- }
190
- return false;
191
- }
192
-
193
- function getDirections(orientation, defaultLayout) {
194
- switch (orientation) {
195
- case 'intersect':
196
- return null;
197
- case 'top':
198
- case 'bottom':
199
- return 'v:v';
200
- case 'left':
201
- case 'right':
202
- return 'h:h';
203
-
204
- // 'top-left'
205
- // 'top-right'
206
- // 'bottom-left'
207
- // 'bottom-right'
208
- default:
209
- return defaultLayout;
210
- }
211
- }
212
-
213
- function roundPoint(point) {
214
- return {
215
- x: Math.round(point.x),
216
- y: Math.round(point.y)
217
- };
218
- }
219
-
220
- function getOrientation(rect, reference, padding) {
221
- var rectOrientation = asTRBL(rect);
222
- var referenceOrientation = asTRBL(reference);
223
- var top = rectOrientation.bottom + padding <= referenceOrientation.top;
224
- var right = rectOrientation.left - padding >= referenceOrientation.right;
225
- var bottom = rectOrientation.top - padding >= referenceOrientation.bottom;
226
- var left = rectOrientation.right + padding <= referenceOrientation.left;
227
- var vertical = top ? 'top' : (bottom ? 'bottom' : null);
228
- var horizontal = left ? 'left' : (right ? 'right' : null);
229
- if (horizontal && vertical) {
230
- return vertical + '-' + horizontal;
231
- } else {
232
- return horizontal || vertical || 'intersect';
233
- }
234
- }
235
-
236
- function asTRBL(bounds) {
237
- return {
238
- top: bounds.y,
239
- right: bounds.x + (bounds.width || 0),
240
- bottom: bounds.y + (bounds.height || 0),
241
- left: bounds.x
242
- };
243
- }
244
-
245
- function is(type, expected) {
246
- var baseType = expected.split(':')[1];
247
- var findType = type.indexOf(baseType) !== -1;
248
- return findType;
249
- }
250
-
251
- export {
252
- getExpandedBounds,
253
- connectRectangles,
254
- connectPoints,
255
- getBendpoints,
256
- getMid,
257
- pointsAligned,
258
- getDirections,
259
- roundPoint,
260
- getOrientation,
261
- asTRBL,
262
- is
263
- };
package/lib/Tree.js DELETED
@@ -1,335 +0,0 @@
1
- /*
2
- This tree is a version of ECOTree.js (by Emilio Cortegoso Lobato), with modifications.
3
- These modifications include:
4
- - supporting multiple parents
5
- - removed all node styling code
6
- - removed tree orientation code in favour of left to right orientation
7
- References:
8
- - ECOTree.js, Emilio Cortegoso Lobato
9
- - "A Node-Positioning Algorithm for General Trees", Walker II, J. Q.
10
- */
11
-
12
- function TreeNode(id, dsc, w, h) {
13
- this.id = id;
14
- this.dsc = dsc;
15
- this.w = w;
16
- this.h = h;
17
- this.dbIndex = 0;
18
- this.XPosition = 0;
19
- this.YPosition = 0;
20
- this.prelim = 0;
21
- this.modifier = 0;
22
- this.leftNeighbor = null;
23
- this.rightNeighbor = null;
24
- this.parents = [];
25
- this.children = [];
26
- }
27
-
28
- TreeNode.prototype._getLevel = function() {
29
- if (this.id === -1 || this.parents === undefined || this.parents.length === 0) {
30
- return 0;
31
- } else {
32
- let maxLevel = 0;
33
- this.parents.forEach(parent => {
34
- var parentLevel = parent._getLevel();
35
- maxLevel = (maxLevel < parentLevel) ? parentLevel : maxLevel;
36
- });
37
- return maxLevel + 1;
38
- }
39
- };
40
-
41
- TreeNode.prototype._getChildrenCount = function() {
42
- return this.children ? this.children.length : 0;
43
- };
44
-
45
- TreeNode.prototype._getLeftSibling = function() {
46
- if (!this.leftNeighbor) {
47
- return null;
48
- }
49
- var haveSameParent = this.leftNeighbor.parents.find(lParent => {
50
- return this.parents.find(tParent => tParent.id === lParent.id);
51
- });
52
- return haveSameParent ? this.leftNeighbor : null;
53
- };
54
-
55
- TreeNode.prototype._getRightSibling = function() {
56
- if (!this.rightNeighbor) {
57
- return null;
58
- }
59
- var haveSameParent = this.rightNeighbor.parents.find(rParent => {
60
- return this.parents.find(tParent => tParent.id === rParent.id);
61
- });
62
- return haveSameParent ? this.rightNeighbor : null;
63
- };
64
-
65
- TreeNode.prototype._getChildAt = function(i) {
66
- return this.children[i];
67
- };
68
-
69
- TreeNode.prototype._getChildrenCenter = function(tree) {
70
- var first = this._getFirstChild();
71
- var last = this._getLastChild();
72
- return first.prelim + ((last.prelim - first.prelim) + tree._getNodeSize(last)) / 2;
73
- };
74
-
75
- TreeNode.prototype._getFirstChild = function() {
76
- return this._getChildAt(0);
77
- };
78
-
79
- TreeNode.prototype._getLastChild = function() {
80
- return this._getChildAt(this._getChildrenCount() - 1);
81
- };
82
-
83
- export default function Tree() {
84
- this.config = {
85
- iMaxDepth: 1000,
86
- iLevelSeparation: 40,
87
- iSiblingSeparation: 40,
88
- iSubtreeSeparation: 60,
89
- topXAdjustment: 0,
90
- topYAdjustment: 0
91
- };
92
- this.self = this;
93
- this.maxLevelHeight = [];
94
- this.maxLevelWidth = [];
95
- this.previousLevelNode = [];
96
- this.rootYOffset = 0;
97
- this.rootXOffset = 0;
98
- this.nDatabaseNodes = [];
99
- this.mapIDs = {};
100
- this.root = new TreeNode(-1, null, null, 2, 2);
101
- this.iLastSearch = 0;
102
- }
103
-
104
- // Layout algorithm
105
- Tree._firstWalk = function(tree, node, level, prevSiblings = []) {
106
-
107
- // console.log('_firstWalk()')
108
- var leftSibling = null;
109
- node.XPosition = 0;
110
- node.YPosition = 0;
111
- node.prelim = 0;
112
- node.modifier = 0;
113
- node.leftNeighbor = null;
114
- node.rightNeighbor = null;
115
- tree._setLevelHeight(node, level);
116
- tree._setLevelWidth(node, level);
117
- tree._setNeighbors(node, level);
118
- if (node._getChildrenCount() === 0 || level === tree.config.iMaxDepth) {
119
- leftSibling = node._getLeftSibling();
120
- if (leftSibling != null) {
121
- node.prelim = leftSibling.prelim + tree._getNodeSize(leftSibling) + tree.config.iSiblingSeparation;
122
- } else {
123
- node.prelim = 0;
124
- }
125
- } else {
126
- var n = node._getChildrenCount();
127
- for (var i = 0; i < n; i++) {
128
- var iChild = node._getChildAt(i);
129
- Tree._firstWalk(tree, iChild, level + 1, [ ...prevSiblings, iChild.id ]);
130
- }
131
- var midPoint = node._getChildrenCenter(tree);
132
- midPoint -= tree._getNodeSize(node) / 2;
133
- leftSibling = node._getLeftSibling();
134
- if (leftSibling != null && !prevSiblings.includes(leftSibling.id)) {
135
- node.prelim = leftSibling.prelim + tree._getNodeSize(leftSibling) + tree.config.iSiblingSeparation;
136
- node.modifier = node.prelim - midPoint;
137
- Tree._apportion(tree, node, level);
138
- } else {
139
- node.prelim = midPoint;
140
- }
141
- }
142
- };
143
-
144
- Tree._apportion = function(tree, node, level) {
145
-
146
- // console.log('_apportion()')
147
- var firstChild = node._getFirstChild();
148
- var firstChildLeftNeighbor = firstChild.leftNeighbor;
149
- var j = 1;
150
- for (var k = tree.config.iMaxDepth - level; firstChild != null && firstChildLeftNeighbor != null && j <= k;) {
151
- var modifierSumRight = 0;
152
- var modifierSumLeft = 0;
153
- var rightAncestor = firstChild;
154
- var leftAncestor = firstChildLeftNeighbor;
155
- for (var l = 0; l < j; l++) {
156
- rightAncestor = rightAncestor.parents[0];
157
- leftAncestor = leftAncestor.parents[0];
158
- modifierSumRight += rightAncestor.modifier;
159
- modifierSumLeft += leftAncestor.modifier;
160
- }
161
- var totalGap = (firstChildLeftNeighbor.prelim + modifierSumLeft + tree._getNodeSize(firstChildLeftNeighbor) + tree.config.iSubtreeSeparation) - (firstChild.prelim + modifierSumRight);
162
- if (totalGap > 0) {
163
- var subtreeAux = node;
164
- var numSubtrees = 0;
165
- for (; subtreeAux != null && subtreeAux !== leftAncestor; subtreeAux = subtreeAux._getLeftSibling()) {
166
- numSubtrees++;
167
- }
168
- if (subtreeAux != null) {
169
- var subtreeMoveAux = node;
170
- var singleGap = totalGap / numSubtrees;
171
- for (; subtreeMoveAux !== leftAncestor; subtreeMoveAux = subtreeMoveAux._getLeftSibling()) {
172
- subtreeMoveAux.prelim += totalGap;
173
- subtreeMoveAux.modifier += totalGap;
174
- totalGap -= singleGap;
175
- }
176
- }
177
- }
178
- j++;
179
- if (firstChild._getChildrenCount() === 0) {
180
- firstChild = tree._getLeftmost(node, 0, j);
181
- } else {
182
- firstChild = firstChild._getFirstChild();
183
- }
184
- if (firstChild != null) {
185
- firstChildLeftNeighbor = firstChild.leftNeighbor;
186
- }
187
- }
188
- };
189
-
190
- Tree._secondWalk = function(tree, node, level, X, Y, prevSiblings = []) {
191
-
192
- // console.log('_secondWalk()')
193
- if (level <= tree.config.iMaxDepth) {
194
- var xTmp = tree.rootXOffset + node.prelim + X;
195
- var yTmp = tree.rootYOffset + Y;
196
- var maxsizeTmp = 0;
197
- var nodesizeTmp = 0;
198
- var flag = false;
199
-
200
- maxsizeTmp = tree.maxLevelWidth[level];
201
- flag = true;
202
- nodesizeTmp = node.w;
203
-
204
- node.XPosition = xTmp;
205
- node.YPosition = yTmp + (maxsizeTmp - nodesizeTmp) / 2;
206
-
207
- if (flag) {
208
- var swapTmp = node.XPosition;
209
- node.XPosition = node.YPosition;
210
- node.YPosition = swapTmp;
211
- }
212
-
213
- if (node._getChildrenCount() !== 0) {
214
- Tree._secondWalk(tree, node._getFirstChild(), level + 1, X + node.modifier, Y + maxsizeTmp + tree.config.iLevelSeparation, [ ...prevSiblings, node.id ]);
215
- }
216
- var rightSibling = node._getRightSibling();
217
- if (rightSibling != null && !prevSiblings.includes(rightSibling.id)) { Tree._secondWalk(tree, rightSibling, level, X, Y, [ ...prevSiblings, node.id ]); }
218
- }
219
- };
220
-
221
- Tree.prototype._positionTree = function() {
222
-
223
- // console.log('_positionTree()')
224
- this.maxLevelHeight = [];
225
- this.maxLevelWidth = [];
226
- this.previousLevelNode = [];
227
- Tree._firstWalk(this.self, this.root, 0);
228
- this.rootXOffset = this.config.topXAdjustment + this.root.XPosition;
229
- this.rootYOffset = this.config.topYAdjustment + this.root.YPosition;
230
- Tree._secondWalk(this.self, this.root, 0, 0, 0);
231
- };
232
-
233
- Tree.prototype._setLevelHeight = function(node, level) {
234
- if (this.maxLevelHeight[level] == null) { this.maxLevelHeight[level] = 0; }
235
- if (this.maxLevelHeight[level] < node.h) { this.maxLevelHeight[level] = node.h; }
236
- };
237
-
238
- Tree.prototype._setLevelWidth = function(node, level) {
239
- if (this.maxLevelWidth[level] == null) { this.maxLevelWidth[level] = 0; }
240
- if (this.maxLevelWidth[level] < node.w) { this.maxLevelWidth[level] = node.w; }
241
- };
242
-
243
- Tree.prototype._setNeighbors = function(node, level) {
244
- var tempNeighbour = this.previousLevelNode[level];
245
- if (tempNeighbour && tempNeighbour.id !== node.id) {
246
- node.leftNeighbor = this.previousLevelNode[level];
247
- if (node.leftNeighbor != null) {
248
- node.leftNeighbor.rightNeighbor = node;
249
- }
250
- }
251
- this.previousLevelNode[level] = node;
252
- };
253
-
254
- Tree.prototype._getNodeSize = function(node) {
255
- return node.h;
256
- };
257
-
258
- Tree.prototype._getLeftmost = function(node, level, maxlevel) {
259
- if (level >= maxlevel) return node;
260
- if (node._getChildrenCount() === 0) return null;
261
-
262
- var n = node._getChildrenCount();
263
- for (var i = 0; i < n; i++) {
264
- var iChild = node._getChildAt(i);
265
- var leftmostDescendant = this._getLeftmost(iChild, level + 1, maxlevel);
266
- if (leftmostDescendant != null) { return leftmostDescendant; }
267
- }
268
-
269
- return null;
270
- };
271
-
272
- Tree.prototype.UpdateTree = function() {
273
- this._positionTree();
274
- };
275
-
276
- Tree.prototype.add = function(id, dsc, w, h) {
277
- var node = new TreeNode(id, dsc, w, h);
278
- var i = this.nDatabaseNodes.length;
279
- node.dbIndex = this.mapIDs[id] = i;
280
- this.nDatabaseNodes[i] = node;
281
- return node;
282
- };
283
-
284
- Tree.prototype.addParentToNode = function(nodeId, parentId) {
285
-
286
- // retrieve nodes from list
287
- var node = this.getNodeById(nodeId);
288
- var parent = (parentId === -1) ? this.root : this.getNodeById(parentId);
289
- if (node === undefined) {
290
- throw new Error('Node not found');
291
- }
292
- if (parent === undefined) {
293
- throw new Error('Parent node not found');
294
- }
295
-
296
- // confirm this parents hasn't already been added to this node
297
- var found = node.parents.find(parent => parent.id === parentId);
298
- if (found === undefined) {
299
- node.parents.push(parent);
300
-
301
- // confirm that the child hasnt been added to the parents children list
302
- found = parent.children.find(child => child.id === nodeId);
303
-
304
- if (found === undefined) {
305
- parent.children.push(node);
306
- }
307
- }
308
- };
309
-
310
- Tree.prototype.removeParentFromNode = function(nodeId, parentId) {
311
-
312
- // retrieve nodes from list
313
- var node = this.getNodeById(nodeId);
314
- var parent = (parentId === -1) ? this.root : this.getNodeById(parentId);
315
- if (node === undefined) {
316
- throw new Error('Node not found');
317
- }
318
- if (parent === undefined) {
319
- throw new Error('Parent node not found');
320
- }
321
-
322
- // remove the parent node from node parents
323
- node.parents = node.parents.filter(parent => parent.id !== parentId);
324
-
325
- // remove the ndoe form the parent children list
326
- parent.children = parent.children.filter(child => child.id !== nodeId);
327
- };
328
-
329
- Tree.prototype.getNodeById = function(id) {
330
- return this.nDatabaseNodes.find(node => node.id === id);
331
- };
332
-
333
- Tree.prototype.getNodeByName = function(name) {
334
- return this.nDatabaseNodes.find(node => node.dsc === name);
335
- };