diff --git a/lib/draw/BpmnRenderer.js b/lib/draw/BpmnRenderer.js index 98316998..f96875fa 100644 --- a/lib/draw/BpmnRenderer.js +++ b/lib/draw/BpmnRenderer.js @@ -144,10 +144,6 @@ function BpmnRenderer(events, styles, bpmnRegistry, pathMap) { function drawRect(p, width, height, r, offset) { offset = offset || 0; - var x, y; - - x = y = offset; - return p.rect(offset, offset, width - offset * 2, height - offset * 2, r).attr({ 'stroke': 'Black', 'stroke-width': 2, @@ -183,7 +179,7 @@ function BpmnRenderer(events, styles, bpmnRegistry, pathMap) { var fillColor = fill ? 'Black' : 'None'; var path = p.path(d).attr(styles.style([ 'no-fill' ],{ - 'stroke-width': 2, + 'stroke-width': 1, 'stroke': 'Black', 'fill': fillColor })); @@ -600,6 +596,7 @@ function BpmnRenderer(events, styles, bpmnRegistry, pathMap) { 'bpmn:Task': function(p, data) { var rect = renderer('bpmn:Activity')(p, data); renderEmbeddedLabel(p, data, 'center-middle'); + attachTaskMarkers(p, data); return rect; }, 'bpmn:ServiceTask': as('bpmn:Task'), @@ -612,12 +609,24 @@ function BpmnRenderer(events, styles, bpmnRegistry, pathMap) { 'bpmn:SubProcess': function(p, data) { var rect = renderer('bpmn:Activity')(p, data); + var di = bpmnRegistry.getDi(data); renderEmbeddedLabel(p, data, di.isExpanded ? 'center-top' : 'center-middle'); + + if(di.isExpanded) { + attachTaskMarkers(p, data); + } else { + attachTaskMarkers(p, data, ['SubProcessMarker']); + } + return rect; }, - 'bpmn:AdHocSubProcess': as('bpmn:SubProcess'), + 'bpmn:AdHocSubProcess': function(p, data) { + var process = renderer('bpmn:SubProcess')(p, data); + + return process; + }, 'bpmn:Transaction': function(p, data) { var outer = renderer('bpmn:SubProcess')(p, data); var inner = drawRect(p, data.width, data.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST) @@ -974,15 +983,159 @@ function BpmnRenderer(events, styles, bpmnRegistry, pathMap) { my: 0.0 } }); - var textPath = drawPath(p, textPathData); + drawPath(p, textPathData); var text = bpmnRegistry.getSemantic(data.id).text || ''; var label = renderLabel(p, text, { box: data, align: 'left-middle' }); return label; + }, + 'SubProcessMarker': function(p, data) { + var markerRect = drawRect(p, 14, 14, 0); + // Process marker is placed in the middle of the box + // therefore fixed values can be used here + markerRect.transform('translate(' + (data.width / 2 - 7.5) + ',' + (data.height - 20) + ')'); + markerRect.attr({ + 'stroke-width': 1 + }); + + var subProcessPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', { + xScaleFactor: 1.5, + yScaleFactor: 1.5, + containerWidth: data.width, + containerHeight: data.height, + position: { + mx: (data.width / 2 - 7.5) / data.width, + my: (data.height - 20) / data.height + } + }); + drawPath(p, subProcessPath); + }, + 'ParallelMarker': function(p, data, position) { + var subProcessPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: data.width, + containerHeight: data.height, + position: { + mx: ((data.width / 2 + position.parallel) / data.width), + my: (data.height - 20) / data.height + } + }); + drawPath(p, subProcessPath); + }, + 'SequentialMarker': function(p, data, position) { + var sequentialPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: data.width, + containerHeight: data.height, + position: { + mx: ((data.width / 2 + position.seq) / data.width), + my: (data.height - 19) / data.height + } + }); + drawPath(p, sequentialPath); + }, + 'CompensationMarker': function(p, data, position) { + var compensationPath = pathMap.getScaledPath('MARKER_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: data.width, + containerHeight: data.height, + position: { + mx: ((data.width / 2 + position.compensation) / data.width), + my: (data.height - 13) / data.height + } + }); + drawPath(p, compensationPath); + }, + 'LoopMarker': function(p, data, position) { + var loopPath = pathMap.getScaledPath('MARKER_LOOP', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: data.width, + containerHeight: data.height, + position: { + mx: ((data.width / 2 + position.loop) / data.width), + my: (data.height - 7) / data.height + } + }); + var marker = drawPath(p, loopPath); + marker.attr({ + 'stroke-width': 1, + 'fill': 'None', + 'stroke-linecap':'round', + 'stroke-miterlimit':0.5 + }); + }, + 'AdhocMarker': function(p, data, position) { + console.log(data); + var loopPath = pathMap.getScaledPath('MARKER_ADHOC', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: data.width, + containerHeight: data.height, + position: { + mx: ((data.width / 2 + position.adhoc) / data.width), + my: (data.height - 15) / data.height + } + }); + var marker = drawPath(p, loopPath); + marker.attr({ + 'stroke-width': 1, + 'fill': 'Black' + }); + } + }; + + function attachTaskMarkers(p, data, taskMarkers) { + var obj = bpmnRegistry.getSemantic(data.id); + + var subprocess = _.contains(taskMarkers, 'SubProcessMarker'); + var position; + + if(subprocess) { + position = { + seq: -21, + parallel: -22, + compensation: -42, + loop: -18, + adhoc: 10 + }; + } else { + position = { + seq: -3, + parallel: -6, + compensation: -27, + loop: 0, + adhoc: 10 + }; } - }; + _.forEach(taskMarkers, function(marker) { + renderer(marker)(p, data, position); + }); + + if(obj.$type === 'bpmn:AdHocSubProcess') { + renderer('AdhocMarker')(p, data, position); + } + if(obj.loopCharacteristics && obj.loopCharacteristics.isSequential === undefined) { + renderer('LoopMarker')(p, data, position); + return; + } + if(obj.loopCharacteristics && + obj.loopCharacteristics.isSequential !== undefined && + !obj.loopCharacteristics.isSequential) { + renderer('ParallelMarker')(p, data, position); + } + if(obj.loopCharacteristics && !!obj.loopCharacteristics.isSequential) { + renderer('SequentialMarker')(p, data, position); + } + if(!!obj.isForCompensation) { + renderer('CompensationMarker')(p, data, position); + } + } function drawShape(parent, data) { var type = data.type; diff --git a/lib/draw/PathMap.js b/lib/draw/PathMap.js index 243bda69..3bbfe8bd 100644 --- a/lib/draw/PathMap.js +++ b/lib/draw/PathMap.js @@ -204,6 +204,55 @@ function PathMap(Snap) { width: 10, heightElements: [30], widthElements: [10] + }, + 'MARKER_SUB_PROCESS': { + d: 'm{mx} {my}, m 7,2 l 0,10 m -5,-5 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_PARALLEL': { + d: 'm{mx} {my}, m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_SEQUENTIAL': { + d: 'm{mx} {my}, m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_COMPENSATION': { + d: 'm {mx},{my} 8,-5 0,10 z m 9,0 8,-5 0,10 z', + height: 10, + width: 21, + heightElements: [], + widthElements: [] + }, + 'MARKER_LOOP': { + d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' + + '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' + + '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' + + 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902', + height: 13.9, + width: 13.7, + heightElements: [], + widthElements: [] + }, + 'MARKER_ADHOC': { + d: 'm {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 ' + + '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' + + '1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 ' + + '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' + + '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z', + height: 4, + width: 15, + heightElements: [], + widthElements: [] } }; diff --git a/test/fixtures/bpmn/render/activity-marker-combination.bpmn b/test/fixtures/bpmn/render/activity-marker-combination.bpmn new file mode 100644 index 00000000..02e2ea9b --- /dev/null +++ b/test/fixtures/bpmn/render/activity-marker-combination.bpmn @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/fixtures/bpmn/render/activity-marker.bpmn b/test/fixtures/bpmn/render/activity-marker.bpmn new file mode 100644 index 00000000..974b3438 --- /dev/null +++ b/test/fixtures/bpmn/render/activity-marker.bpmn @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/browser/draw/BpmnRendererSpec.js b/test/spec/browser/draw/BpmnRendererSpec.js index b4b4b3fe..2a44c3f5 100644 --- a/test/spec/browser/draw/BpmnRendererSpec.js +++ b/test/spec/browser/draw/BpmnRendererSpec.js @@ -28,6 +28,27 @@ describe('draw/BpmnRenderer', function() { }); }); + it('should render activity-marker', function(done) { + + var xml = fs.readFileSync(__dirname + '/../../../fixtures/bpmn/render/activity-marker-combination.bpmn', 'utf8'); + + var renderer = new Viewer(container); + + renderer.importXML(xml, function(err) { + done(err); + }); + }); + + it('should render activity-marker', function(done) { + + var xml = fs.readFileSync(__dirname + '/../../../fixtures/bpmn/render/activity-marker.bpmn', 'utf8'); + + var renderer = new Viewer(container); + + renderer.importXML(xml, function(err) { + done(err); + }); + }); it('should render data objects', function(done) {