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 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,
+   *     }
+   *   }
+   *   
+ * + *

+ * */ - 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