bpmn-auto-layout 0.1.0 → 0.2.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/README.md +22 -11
- package/index.js +1 -1
- package/lib/AutoLayout.js +48 -64
- package/lib/DiFactory.js +5 -16
- package/lib/DiUtil.js +3 -3
- package/lib/Tree.js +6 -8
- package/package.json +13 -5
- package/.travis.yml +0 -5
package/README.md
CHANGED
|
@@ -3,26 +3,27 @@
|
|
|
3
3
|
|
|
4
4
|
# bpmn-auto-layout
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
[](https://github.com/bpmn-io/bpmn-auto-layout/actions/workflows/CI.yml)
|
|
7
|
+
|
|
8
|
+
Get a layouted diagram of a BPMN process without graphical representation.
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
## Usage
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
This library works in [Node.js](https://nodejs.org/) and in the browser.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
var AutoLayout = require('bpmn-auto-layout');
|
|
15
|
+
To layout diagrams these must have __exactly one single start event__.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
```javascript
|
|
18
|
+
import AutoLayout from 'bpmn-auto-layout';
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
const diagramXML = '<bpmn:defintions ...></bpmn:defintions>';
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
var layoutedDiagramXML = await autoLayout.layoutProcess(diagramXML);
|
|
22
|
+
const autoLayout = new AutoLayout();
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
})();
|
|
24
|
+
const layoutedDiagramXML = await autoLayout.layoutProcess(diagramXML);
|
|
25
25
|
|
|
26
|
+
console.log(layoutedDiagramXML);
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
|
|
@@ -31,6 +32,16 @@ var autoLayout = new AutoLayout();
|
|
|
31
32
|
* [Issues](https://github.com/bpmn-io/bpmn-auto-layout/issues)
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
## Building
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm install
|
|
39
|
+
npm run all
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
As part of the test run, visual test cases are generated to `test/generated/test.html`.
|
|
43
|
+
|
|
44
|
+
|
|
34
45
|
## License
|
|
35
46
|
|
|
36
|
-
|
|
47
|
+
MIT
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export { default } from './lib/AutoLayout.js';
|
package/lib/AutoLayout.js
CHANGED
|
@@ -1,72 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
var Tree = require('./Tree');
|
|
3
|
-
var DiFactory = require('./DiFactory');
|
|
1
|
+
import BpmnModdle from 'bpmn-moddle';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var getExpandedBounds = DiUtil.getExpandedBounds;
|
|
9
|
-
var getBendpoints = DiUtil.getBendpoints;
|
|
3
|
+
import Tree from './Tree.js';
|
|
4
|
+
import DiFactory from './DiFactory.js';
|
|
5
|
+
import { is,getExpandedBounds, getBendpoints } from './DiUtil.js';
|
|
10
6
|
|
|
11
7
|
var PADDING_NODE = 'padding_node';
|
|
12
8
|
|
|
13
9
|
|
|
14
|
-
function AutoLayout() {
|
|
10
|
+
export default function AutoLayout() {
|
|
15
11
|
this.moddle = new BpmnModdle();
|
|
16
12
|
this.DiFactory = new DiFactory(this.moddle);
|
|
17
13
|
this.nodeCount = -1;
|
|
18
14
|
}
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
AutoLayout.prototype.layoutProcess = function(xmlStr, callback) {
|
|
16
|
+
AutoLayout.prototype.layoutProcess = async function(xmlStr) {
|
|
23
17
|
var self = this;
|
|
24
18
|
var moddle = this.moddle;
|
|
25
19
|
var createDiPlane = this.DiFactory.createDiPlane.bind(this.DiFactory);
|
|
26
20
|
var createDiDiagram = this.DiFactory.createDiDiagram.bind(this.DiFactory);
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
moddle.fromXML(xmlStr, function(error, moddleWithoutDi) {
|
|
31
|
-
if (error) {
|
|
32
|
-
return reject(error);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// create new di section
|
|
36
|
-
var root = moddleWithoutDi.get('rootElements').find(el => el.$type === 'bpmn:Process');
|
|
37
|
-
var rootDi = createDiPlane({
|
|
38
|
-
id: 'BPMNPlane_1',
|
|
39
|
-
bpmnElement: root
|
|
40
|
-
});
|
|
41
|
-
var newDiagram = createDiDiagram({
|
|
42
|
-
id: 'BPMNDiagram_1',
|
|
43
|
-
plane: rootDi
|
|
44
|
-
});
|
|
45
|
-
moddleWithoutDi.diagrams = [newDiagram];
|
|
46
|
-
|
|
47
|
-
// build the tree layout
|
|
48
|
-
self.tree = new Tree('Process');
|
|
49
|
-
self._addTreeNode(root, root.flowElements);
|
|
50
|
-
self._buildTreeBreadFirstSearch(root);
|
|
51
|
-
console.log('tree built');
|
|
52
|
-
self.tree.UpdateTree();
|
|
53
|
-
console.log('tree updated');
|
|
54
|
-
|
|
55
|
-
// create di
|
|
56
|
-
self._layoutTreeBreadFirstSearch(root, rootDi);
|
|
57
|
-
console.log('bpmn element layout complete');
|
|
22
|
+
try {
|
|
23
|
+
var { rootElement: moddleWithoutDi } = await moddle.fromXML(xmlStr);
|
|
58
24
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return resolve(result);
|
|
66
|
-
});
|
|
25
|
+
// create new di section
|
|
26
|
+
var root = moddleWithoutDi.get('rootElements').find(el => el.$type === 'bpmn:Process');
|
|
27
|
+
var rootDi = createDiPlane({
|
|
28
|
+
id: 'BPMNPlane_1',
|
|
29
|
+
bpmnElement: root
|
|
67
30
|
});
|
|
68
|
-
|
|
69
|
-
|
|
31
|
+
var newDiagram = createDiDiagram({
|
|
32
|
+
id: 'BPMNDiagram_1',
|
|
33
|
+
plane: rootDi
|
|
34
|
+
});
|
|
35
|
+
moddleWithoutDi.diagrams = [ newDiagram ];
|
|
36
|
+
|
|
37
|
+
// build the tree layout
|
|
38
|
+
self.tree = new Tree();
|
|
39
|
+
self._addTreeNode(root, root.flowElements);
|
|
40
|
+
self._buildTreeBreadFirstSearch(root);
|
|
41
|
+
console.log('tree built');
|
|
42
|
+
self.tree.UpdateTree();
|
|
43
|
+
console.log('tree updated');
|
|
44
|
+
|
|
45
|
+
// create di
|
|
46
|
+
self._layoutTreeBreadFirstSearch(root, rootDi);
|
|
47
|
+
console.log('bpmn element layout complete');
|
|
48
|
+
|
|
49
|
+
var { xml: result } = await moddle.toXML(moddleWithoutDi);
|
|
50
|
+
return Promise.resolve(result);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return Promise.reject(error);
|
|
53
|
+
}
|
|
70
54
|
};
|
|
71
55
|
|
|
72
56
|
AutoLayout.prototype._buildTreeBreadFirstSearch = function(rootFlowElement) {
|
|
@@ -74,7 +58,7 @@ AutoLayout.prototype._buildTreeBreadFirstSearch = function(rootFlowElement) {
|
|
|
74
58
|
var children = rootFlowElement.flowElements;
|
|
75
59
|
|
|
76
60
|
// queue holds visited elements
|
|
77
|
-
var queue = children ? [...children] : [];
|
|
61
|
+
var queue = children ? [ ...children ] : [];
|
|
78
62
|
var elementOrConnection;
|
|
79
63
|
var outgoings;
|
|
80
64
|
while (queue.length !== 0) {
|
|
@@ -159,7 +143,7 @@ AutoLayout.prototype._explodeSubprocess = function(flowElement, flowElements) {
|
|
|
159
143
|
self.tree.addParentToNode(child.id, subprocessNode.id);
|
|
160
144
|
});
|
|
161
145
|
}
|
|
162
|
-
var childNodes = [...subprocessNode.children].filter(child => {
|
|
146
|
+
var childNodes = [ ...subprocessNode.children ].filter(child => {
|
|
163
147
|
return !subprocessStartNodes.find(startNode => startNode.id === child.id);
|
|
164
148
|
});
|
|
165
149
|
childNodes.forEach(childNode => {
|
|
@@ -174,7 +158,7 @@ AutoLayout.prototype._explodeSubprocess = function(flowElement, flowElements) {
|
|
|
174
158
|
|
|
175
159
|
// remove parents from subprocessStartNodes
|
|
176
160
|
subprocessStartNodes.forEach(subprocessStartNode => {
|
|
177
|
-
var parents = [...subprocessStartNode.parents];
|
|
161
|
+
var parents = [ ...subprocessStartNode.parents ];
|
|
178
162
|
parents.forEach(parentNode => {
|
|
179
163
|
this.tree.removeParentFromNode(subprocessStartNode.id, parentNode.id);
|
|
180
164
|
});
|
|
@@ -250,7 +234,7 @@ AutoLayout.prototype._balanceTreeNodeParentLevels = function(node) {
|
|
|
250
234
|
var maxParentLevel = node._getLevel() - 1;
|
|
251
235
|
|
|
252
236
|
// pad out levels such that node parents are at same level
|
|
253
|
-
var oldParents = [...node.parents];
|
|
237
|
+
var oldParents = [ ...node.parents ];
|
|
254
238
|
oldParents.forEach(parent => {
|
|
255
239
|
let padParent = parent;
|
|
256
240
|
while (padParent._getLevel() < maxParentLevel) {
|
|
@@ -260,8 +244,8 @@ AutoLayout.prototype._balanceTreeNodeParentLevels = function(node) {
|
|
|
260
244
|
|
|
261
245
|
// console messaging
|
|
262
246
|
var message = 'Added ' + PADDING_NODE + padParent.id + ' between ' +
|
|
263
|
-
|
|
264
|
-
|
|
247
|
+
oldParent.dsc + (oldParent.dsc === PADDING_NODE ? oldParent.id : '') + ' and ' +
|
|
248
|
+
node.dsc + (node.dsc === PADDING_NODE ? node.id : '');
|
|
265
249
|
console.log(message);
|
|
266
250
|
|
|
267
251
|
// sort out parents
|
|
@@ -290,7 +274,7 @@ AutoLayout.prototype._layoutTreeBreadFirstSearch = function(parentFlowElement, r
|
|
|
290
274
|
}
|
|
291
275
|
|
|
292
276
|
// queue holds visited elements
|
|
293
|
-
var queue = startEvents ? [...startEvents] : [];
|
|
277
|
+
var queue = startEvents ? [ ...startEvents ] : [];
|
|
294
278
|
var elementOrConnection;
|
|
295
279
|
var outgoings;
|
|
296
280
|
while (queue.length !== 0) {
|
|
@@ -342,8 +326,8 @@ AutoLayout.prototype._groupElement = function(elementOrConnection, group, parent
|
|
|
342
326
|
} else {
|
|
343
327
|
this._layoutGroup(group, parentDi);
|
|
344
328
|
group = {
|
|
345
|
-
elements: elementOrConnection.$type === 'bpmn:SequenceFlow' ? [] : [elementOrConnection],
|
|
346
|
-
connections: elementOrConnection.$type === 'bpmn:SequenceFlow' ? [elementOrConnection] : [],
|
|
329
|
+
elements: elementOrConnection.$type === 'bpmn:SequenceFlow' ? [] : [ elementOrConnection ],
|
|
330
|
+
connections: elementOrConnection.$type === 'bpmn:SequenceFlow' ? [ elementOrConnection ] : [],
|
|
347
331
|
distance: elementOrConnection.dist
|
|
348
332
|
};
|
|
349
333
|
}
|
|
@@ -430,7 +414,7 @@ AutoLayout.prototype._layoutConnections = function(group, parentDi) {
|
|
|
430
414
|
// this connection needs to accommodate padding nodes
|
|
431
415
|
// check for any padding nodes and go through those coords
|
|
432
416
|
var oldPoints = connectionDi.waypoint;
|
|
433
|
-
var newPoints = [oldPoints[0]];
|
|
417
|
+
var newPoints = [ oldPoints[0] ];
|
|
434
418
|
|
|
435
419
|
// get node for sourceRef
|
|
436
420
|
var pathNodes = getPathToTarget(sourceNode, targetRef.id);
|
|
@@ -486,7 +470,7 @@ function getPathToTarget(sourceNode, targetId) {
|
|
|
486
470
|
return undefined;
|
|
487
471
|
}
|
|
488
472
|
if (sourceNode.dsc === targetId) {
|
|
489
|
-
return [sourceNode];
|
|
473
|
+
return [ sourceNode ];
|
|
490
474
|
}
|
|
491
475
|
if (sourceNode.children) {
|
|
492
476
|
let pathToTarget;
|
|
@@ -499,7 +483,7 @@ function getPathToTarget(sourceNode, targetId) {
|
|
|
499
483
|
}
|
|
500
484
|
});
|
|
501
485
|
if (pathToTarget) {
|
|
502
|
-
return [sourceNode, ...pathToTarget];
|
|
486
|
+
return [ sourceNode, ...pathToTarget ];
|
|
503
487
|
}
|
|
504
488
|
}
|
|
505
489
|
return undefined;
|
package/lib/DiFactory.js
CHANGED
|
@@ -1,23 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var pick = require('min-dash').pick;
|
|
3
|
+
import { map, assign, pick } from 'min-dash';
|
|
4
|
+
import { is, connectRectangles, getExpandedBounds } from './DiUtil.js';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var is = DiUtil.is;
|
|
10
|
-
var connectRectangles = DiUtil.connectRectangles;
|
|
11
|
-
var getExpandedBounds = DiUtil.getExpandedBounds;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function DiFactory(moddle) {
|
|
6
|
+
export default function DiFactory(moddle) {
|
|
15
7
|
this._model = moddle;
|
|
16
8
|
}
|
|
17
9
|
|
|
18
|
-
module.exports = DiFactory;
|
|
19
|
-
|
|
20
|
-
|
|
21
10
|
DiFactory.prototype._isExpanded = function(element) {
|
|
22
11
|
return element && element.flowElements ? element.flowElements.length > 0 : false;
|
|
23
12
|
};
|
|
@@ -52,7 +41,7 @@ DiFactory.prototype.createDiWaypoints = function(waypoints) {
|
|
|
52
41
|
};
|
|
53
42
|
|
|
54
43
|
DiFactory.prototype.createDiWaypoint = function(point) {
|
|
55
|
-
return this.create('dc:Point', pick(point, ['x', 'y']));
|
|
44
|
+
return this.create('dc:Point', pick(point, [ 'x', 'y' ]));
|
|
56
45
|
};
|
|
57
46
|
|
|
58
47
|
DiFactory.prototype.createDiEdge = function(semantic, waypoints, attrs) {
|
|
@@ -132,7 +121,7 @@ DiFactory.prototype._getDefaultSize = function(element) {
|
|
|
132
121
|
return { width: 50, height: 50 };
|
|
133
122
|
}
|
|
134
123
|
|
|
135
|
-
if (is(elementType, 'bpmn:
|
|
124
|
+
if (is(elementType, 'bpmn:Event')) {
|
|
136
125
|
return { width: 36, height: 36 };
|
|
137
126
|
}
|
|
138
127
|
|
package/lib/DiUtil.js
CHANGED
|
@@ -138,12 +138,12 @@ function getBendpoints(a, b, directions) {
|
|
|
138
138
|
|
|
139
139
|
// one point, next to a
|
|
140
140
|
if (directions === 'h:v') {
|
|
141
|
-
return [{ x: b.x, y: a.y }];
|
|
141
|
+
return [ { x: b.x, y: a.y } ];
|
|
142
142
|
} else
|
|
143
143
|
|
|
144
144
|
// one point, above a
|
|
145
145
|
if (directions === 'v:h') {
|
|
146
|
-
return [{ x: a.x, y: b.y }];
|
|
146
|
+
return [ { x: a.x, y: b.y } ];
|
|
147
147
|
} else {
|
|
148
148
|
|
|
149
149
|
// vertical edge xmid
|
|
@@ -248,7 +248,7 @@ function is(type, expected) {
|
|
|
248
248
|
return findType;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
export {
|
|
252
252
|
getExpandedBounds,
|
|
253
253
|
connectRectangles,
|
|
254
254
|
connectPoints,
|
package/lib/Tree.js
CHANGED
|
@@ -80,11 +80,11 @@ TreeNode.prototype._getLastChild = function() {
|
|
|
80
80
|
return this._getChildAt(this._getChildrenCount() - 1);
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
function Tree() {
|
|
83
|
+
export default function Tree() {
|
|
84
84
|
this.config = {
|
|
85
85
|
iMaxDepth: 1000,
|
|
86
|
-
iLevelSeparation:
|
|
87
|
-
iSiblingSeparation:
|
|
86
|
+
iLevelSeparation: 40,
|
|
87
|
+
iSiblingSeparation: 40,
|
|
88
88
|
iSubtreeSeparation: 60,
|
|
89
89
|
topXAdjustment: 0,
|
|
90
90
|
topYAdjustment: 0
|
|
@@ -101,8 +101,6 @@ function Tree() {
|
|
|
101
101
|
this.iLastSearch = 0;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
module.exports = Tree;
|
|
105
|
-
|
|
106
104
|
// Layout algorithm
|
|
107
105
|
Tree._firstWalk = function(tree, node, level, prevSiblings = []) {
|
|
108
106
|
|
|
@@ -128,7 +126,7 @@ Tree._firstWalk = function(tree, node, level, prevSiblings = []) {
|
|
|
128
126
|
var n = node._getChildrenCount();
|
|
129
127
|
for (var i = 0; i < n; i++) {
|
|
130
128
|
var iChild = node._getChildAt(i);
|
|
131
|
-
Tree._firstWalk(tree, iChild, level + 1, [...prevSiblings, iChild.id]);
|
|
129
|
+
Tree._firstWalk(tree, iChild, level + 1, [ ...prevSiblings, iChild.id ]);
|
|
132
130
|
}
|
|
133
131
|
var midPoint = node._getChildrenCenter(tree);
|
|
134
132
|
midPoint -= tree._getNodeSize(node) / 2;
|
|
@@ -213,10 +211,10 @@ Tree._secondWalk = function(tree, node, level, X, Y, prevSiblings = []) {
|
|
|
213
211
|
}
|
|
214
212
|
|
|
215
213
|
if (node._getChildrenCount() !== 0) {
|
|
216
|
-
Tree._secondWalk(tree, node._getFirstChild(), level + 1, X + node.modifier, Y + maxsizeTmp + tree.config.iLevelSeparation, [...prevSiblings, node.id]);
|
|
214
|
+
Tree._secondWalk(tree, node._getFirstChild(), level + 1, X + node.modifier, Y + maxsizeTmp + tree.config.iLevelSeparation, [ ...prevSiblings, node.id ]);
|
|
217
215
|
}
|
|
218
216
|
var rightSibling = node._getRightSibling();
|
|
219
|
-
if (rightSibling != null && !prevSiblings.includes(rightSibling.id)) { Tree._secondWalk(tree, rightSibling, level, X, Y, [...prevSiblings, node.id]); }
|
|
217
|
+
if (rightSibling != null && !prevSiblings.includes(rightSibling.id)) { Tree._secondWalk(tree, rightSibling, level, X, Y, [ ...prevSiblings, node.id ]); }
|
|
220
218
|
}
|
|
221
219
|
};
|
|
222
220
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bpmn-auto-layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Layout BPMN diagrams, generating missing DI information",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib"
|
|
9
|
+
],
|
|
6
10
|
"scripts": {
|
|
7
11
|
"all": "run-s lint test",
|
|
8
12
|
"lint": "eslint .",
|
|
@@ -23,13 +27,17 @@
|
|
|
23
27
|
},
|
|
24
28
|
"homepage": "https://github.com/bpmn-io/bpmn-auto-layout#readme",
|
|
25
29
|
"dependencies": {
|
|
26
|
-
"bpmn-moddle": "^
|
|
30
|
+
"bpmn-moddle": "^8.0.1",
|
|
27
31
|
"min-dash": "^3.5.2"
|
|
28
32
|
},
|
|
29
33
|
"devDependencies": {
|
|
30
|
-
"eslint": "^
|
|
31
|
-
"eslint-plugin-bpmn-io": "^0.
|
|
32
|
-
"mocha": "^
|
|
34
|
+
"eslint": "^8.41.0",
|
|
35
|
+
"eslint-plugin-bpmn-io": "^1.0.0",
|
|
36
|
+
"mocha": "^10.2.0",
|
|
33
37
|
"npm-run-all": "^4.1.5"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">= 10.0.0",
|
|
41
|
+
"npm": ">= 6.0.0"
|
|
34
42
|
}
|
|
35
43
|
}
|