From 21b1ac0ab01287335953e6867451f9029f57e407 Mon Sep 17 00:00:00 2001
From: jdotzki
Date: Thu, 24 Apr 2014 12:25:04 +0200
Subject: [PATCH] feat(bpmnrenderer): gateway renndering added closes #17
---
lib/draw/BpmnRenderer.js | 146 +++++++++++++++++++++++-
lib/draw/PathMap.js | 143 +++++++++++++++++++++--
test/fixtures/bpmn/render/gateways.bpmn | 62 ++++++++++
3 files changed, 335 insertions(+), 16 deletions(-)
create mode 100644 test/fixtures/bpmn/render/gateways.bpmn
diff --git a/lib/draw/BpmnRenderer.js b/lib/draw/BpmnRenderer.js
index 6b80bdd6..21b4c1a5 100644
--- a/lib/draw/BpmnRenderer.js
+++ b/lib/draw/BpmnRenderer.js
@@ -494,12 +494,148 @@ function BpmnRenderer(events, styles, bpmnRegistry, pathMap) {
var rect = drawRect(p, data.width, data.height, 0);
return rect;
},
+ 'bpmn:InclusiveGateway': function(p, data) {
+ var diamond = drawDiamond(p, data.width, data.height);
- 'bpmn:InclusiveGateway': as('bpmn:Gateway'),
- 'bpmn:ExclusiveGateway': as('bpmn:Gateway'),
- 'bpmn:ComplexGateway': as('bpmn:Gateway'),
- 'bpmn:ParallelGateway': as('bpmn:Gateway'),
- 'bpmn:EventBasedGateway': as('bpmn:Gateway'),
+ var circle = drawCircle(p, data.width, data.height, data.height * 0.24);
+ circle.attr({
+ 'stroke-width': 2.5,
+ 'fill': 'None'
+ });
+
+ return diamond;
+ },
+ 'bpmn:ExclusiveGateway': function(p, data) {
+ var diamond = drawDiamond(p, data.width, data.height);
+
+ var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', {
+ xScaleFactor: 0.45,
+ yScaleFactor: 0.45,
+ containerWidth: data.width,
+ containerHeight: data.height,
+ position: {
+ mx: 0.3,
+ my: 0.28
+ }
+ });
+
+ var exclusivePath = drawPath(p, pathData);
+ exclusivePath.attr({
+ 'stroke-width': 1,
+ 'fill': 'Black'
+ });
+
+ return diamond;
+ },
+ 'bpmn:ComplexGateway': function(p, data) {
+ var diamond = drawDiamond(p, data.width, data.height);
+
+ var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', {
+ xScaleFactor: 0.5,
+ yScaleFactor:0.5,
+ containerWidth: data.width,
+ containerHeight: data.height,
+ position: {
+ mx: 0.46,
+ my: 0.26
+ }
+ });
+
+ var complexPath = drawPath(p, pathData);
+ complexPath.attr({
+ 'stroke-width': 1,
+ 'fill': 'Black'
+ });
+
+ return diamond;
+ },
+ 'bpmn:ParallelGateway': function(p, data) {
+ var diamond = drawDiamond(p, data.width, data.height);
+
+ var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
+ xScaleFactor: 0.6,
+ yScaleFactor:0.6,
+ containerWidth: data.width,
+ containerHeight: data.height,
+ position: {
+ mx: 0.46,
+ my: 0.2
+ }
+ });
+
+ var parallelPath = drawPath(p, pathData);
+ parallelPath.attr({
+ 'stroke-width': 1,
+ 'fill': 'Black'
+ });
+
+ return diamond;
+ },
+ 'bpmn:EventBasedGateway': function(p, data) {
+ var diamond = drawDiamond(p, data.width, data.height);
+
+ var outerCircle = drawCircle(p, data.width, data.height, data.height * 0.21);
+ outerCircle.attr({
+ 'stroke-width': 1,
+ 'fill': 'None'
+ });
+
+ var type = bpmnRegistry.getSemantic(data.id).eventGatewayType;
+
+ function drawEvent() {
+
+ var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', {
+ xScaleFactor: 0.20,
+ yScaleFactor: 0.20,
+ containerWidth: data.width,
+ containerHeight: data.height,
+ position: {
+ mx: 0.34,
+ my: 0.43
+ }
+ });
+
+ var eventPath = drawPath(p, pathData);
+ eventPath.attr({
+ 'stroke-width': 2,
+ 'fill': 'None'
+ });
+ }
+
+ if(type === 'Parallel') {
+
+ var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
+ xScaleFactor: 0.4,
+ yScaleFactor:0.4,
+ containerWidth: data.width,
+ containerHeight: data.height,
+ position: {
+ mx: 0.46,
+ my: 0.29
+ }
+ });
+
+ var parallelPath = drawPath(p, pathData);
+ parallelPath.attr({
+ 'stroke-width': 1,
+ 'fill': 'None'
+ });
+ } else if(type === 'Exclusive') {
+
+ drawEvent();
+ } else {
+
+ var innerCircle = drawCircle(p, data.width, data.height, data.height * 0.24);
+ innerCircle.attr({
+ 'stroke-width': 1,
+ 'fill': 'None'
+ });
+ drawEvent();
+ }
+
+
+ return diamond;
+ },
'bpmn:Gateway': function(p, data) {
var diamond = drawDiamond(p, data.width, data.height);
diff --git a/lib/draw/PathMap.js b/lib/draw/PathMap.js
index 590df30a..9dc440e2 100644
--- a/lib/draw/PathMap.js
+++ b/lib/draw/PathMap.js
@@ -8,6 +8,34 @@ function PathMap(Snap) {
/**
* Contains a map of path elements
+ *
+ * Path definition
+ * A parameterized path is defined like this:
+ *
+ * 'GATEWAY_PARALLEL': {
+ * d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
+ '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
+ * height: 17.5,
+ * width: 17.5,
+ * heightElements: [2.5, 7.5],
+ * widthElements: [2.5, 7.5]
+ * }
+ *
+ * It's important to specify a correct height and width for the path as the scaling
+ * is based on the ratio between the specified height and width in this object and the
+ * height and width that is set as scale target (Note x,y coordinates will be scaled with
+ * individual ratios).
+ * The 'heightElements' and 'widthElements' array must contain the values that will be scaled.
+ * The scaling is based on the computed ratios.
+ * Coordinates on the y axis should be in the heightElement's array, they will be scaled using
+ * the computed ratio coefficient.
+ * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets.
+ *
+ * - The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....
+ * - The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....
+ *
+ * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index.
+ *
*/
this.pathMap = {
'shape': {
@@ -173,6 +201,46 @@ function PathMap(Snap) {
width: 17,
heightElements: [],
widthElements: []
+ },
+ 'GATEWAY_EXCLUSIVE': {
+ //m 0,0 6.5,8.5 -6.5,8.5 3,0 5,-6.5312 5,6.5312 3,0 -6.5,-8.5 6.5,-8.5 -3,0 -5,6.5313 -5,-6.5313 -3,0 z
+ d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' +
+ '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' +
+ '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z',
+ height: 17.5,
+ width: 17.5,
+ heightElements: [8.5, 6.5312, -6.5312, -8.5],
+ widthElements: [6.5, -6.5, 3, -3, 5, -5]
+ },
+ 'GATEWAY_PARALLEL': {
+ d:'m {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
+ '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
+ height: 17.125,
+ width: 16.125,
+ heightElements: [2.5, 7.5],
+ widthElements: [2.5, 7.5]
+ },
+ 'GATEWAY_EVENT_BASED': {
+ //d:'m {mx},{my} 9.42149,-6.28099 9.42149,6.28099 -3.1405,12.56199 -12.56198,0 z',
+ d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z',
+ height: 11,
+ width: 11,
+ heightElements: [-6, 6, 12, -12],
+ widthElements: [9, -3, -12]
+ },
+ 'GATEWAY_COMPLEX': {
+ //d:'m 7.0625,0.0625 0,4.8750001 -3.4375,-3.4375 -2.125,2.125 3.4375,3.4375 -4.875,0
+ // 0,2.9999999 4.875,0 L 1.5,13.5 l 2.125,2.125 3.4375,-3.4375 0,4.875 3,0 0,-4.875
+ // 3.4375,3.4375 2.125,-2.125 -3.4375,-3.4375 4.875,0 0,-2.9999999 -4.875,0 3.4375,-3.4375
+ // -2.125,-2.125 -3.4375,3.4375 0,-4.8750001 z',
+ d:'m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} ' +
+ '{e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} ' +
+ '{e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} ' +
+ '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z',
+ height: 17.125,
+ width: 17.125,
+ heightElements: [4.875, 3.4375, 2.125, 3],
+ widthElements: [3.4375, 2.125, 4.875, 3]
}
};
@@ -182,32 +250,85 @@ function PathMap(Snap) {
/**
* Scales the path to the given height and width.
+ * Use case
+ * Use case is to scale the content of elements (event, gateways) based
+ * on the element bounding box's size.
+ *
+ * Why not transform
+ * Scaling a path with transform() will also scale the stroke and IE does not support
+ * the option 'non-scaling-stroke' to prevent this.
+ * Also there are use cases where only some parts of a path should be
+ * scaled.
+ *
+ * @param {String} pathId The ID of the path.
+ * @param {Object} param
+ * Example param object scales the path to 60% size of the container (data.width, data.height).
+ *
+ * {
+ * xScaleFactor: 0.6,
+ * yScaleFactor:0.6,
+ * containerWidth: data.width,
+ * containerHeight: data.height,
+ * position: {
+ * mx: 0.46,
+ * my: 0.2,
+ * }
+ * }
+ *
+ *
+ * - targetpathwidth = xScaleFactor * containerWidth
+ * - targetpathheight = yScaleFactor * containerHeight
+ * - Position is used to set the starting coordinate of the path. M is computed:
+ *
+ * - position.x * containerWidth
+ * - position.y * containerHeight
+ *
+ * Center of the container position: {
+ * mx: 0.5,
+ * my: 0.5,
+ * }
+ * Upper left corner of the container
+ * position: {
+ * mx: 0.0,
+ * my: 0.0,
+ * }
+ *
+ *
+ *
+ *
*/
- this.getScaledPath = function getScaledPath(pathId, height, width) {
- var rawPath = this.getRawPath(pathId);
- var heightRatio = rawPath.height / height;
- var widthRatio = rawPath.width / width;
- var heightElements = [];
- var widthElements = [];
+ this.getScaledPath = function getScaledPath(pathId, param) {
+ var rawPath = this.pathMap[pathId];
+
+ // positioning
+ // compute the start point of the path
+ var mx = param.containerWidth * param.position.mx;
+ var my = param.containerHeight * param.position.my;
+
+ // path
+ var heightRatio = (param.containerHeight * param.yScaleFactor) / rawPath.height;
+ var widthRatio = (param.containerWidth * param.xScaleFactor) / rawPath.width;
+ var coordinates = {}; //map for the scaled coordinates
//Apply height ratio
for(var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) {
- heightElements[heightIndex] = rawPath.heightElements[heightIndex] * heightRatio;
+ coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio;
}
//Apply width ratio
for(var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) {
- widthElements[widthIndex] = rawPath.widthElements[widthIndex] * widthRatio;
+ coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio;
}
+
//Apply value to raw path
var path = Snap.format(
rawPath.d, {
- heightElements: heightElements,
- widthElements: widthElements
+ mx: mx,
+ my: my,
+ e: coordinates
}
);
-
return path;
};
}
diff --git a/test/fixtures/bpmn/render/gateways.bpmn b/test/fixtures/bpmn/render/gateways.bpmn
new file mode 100644
index 00000000..49c959ca
--- /dev/null
+++ b/test/fixtures/bpmn/render/gateways.bpmn
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file