From db53608b3d2e104a9e6f384c84ebbc9209a1c794 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Wed, 22 Jun 2016 14:38:44 +0200 Subject: [PATCH] feat(connect): support point to point connection Closes #578 --- lib/features/modeling/BpmnLayouter.js | 19 +- lib/features/modeling/Modeling.js | 4 +- .../features/modeling/CreateConnectionSpec.js | 218 ++++++++++-------- 3 files changed, 133 insertions(+), 108 deletions(-) diff --git a/lib/features/modeling/BpmnLayouter.js b/lib/features/modeling/BpmnLayouter.js index e9c3c571..028fcfe2 100644 --- a/lib/features/modeling/BpmnLayouter.js +++ b/lib/features/modeling/BpmnLayouter.js @@ -24,18 +24,21 @@ inherits(BpmnLayouter, BaseLayouter); module.exports = BpmnLayouter; -BpmnLayouter.prototype.layoutConnection = function(connection, layoutHints) { +BpmnLayouter.prototype.layoutConnection = function(connection, hints) { + + hints = hints || {}; + var source = connection.source, target = connection.target, waypoints = connection.waypoints, - start, - end; + start = hints.connectionStart, + end = hints.connectionEnd; var manhattanOptions, updatedWaypoints; - start = getConnectionDocking(waypoints, 0, source); - end = getConnectionDocking(waypoints, waypoints && waypoints.length - 1, target); + start = getConnectionDocking(waypoints, 0, source, start); + end = getConnectionDocking(waypoints, waypoints && waypoints.length - 1, target, end); // TODO(nikku): support vertical modeling // and invert preferredLayouts accordingly @@ -141,7 +144,7 @@ BpmnLayouter.prototype.layoutConnection = function(connection, layoutHints) { if (manhattanOptions) { - manhattanOptions = assign(manhattanOptions, layoutHints); + manhattanOptions = assign(manhattanOptions, hints); updatedWaypoints = ManhattanLayout.repairConnection( @@ -164,10 +167,10 @@ function getAttachOrientation(attachedElement) { } -function getConnectionDocking(waypoints, idx, shape) { +function getConnectionDocking(waypoints, idx, shape, defaultPoint) { var point = waypoints && waypoints[idx]; - return point ? (point.original || point) : getMid(shape); + return point ? (point.original || point) : (defaultPoint || getMid(shape)); } function isCompensationAssociation(connection) { diff --git a/lib/features/modeling/Modeling.js b/lib/features/modeling/Modeling.js index 93d43fc0..3cad95db 100644 --- a/lib/features/modeling/Modeling.js +++ b/lib/features/modeling/Modeling.js @@ -57,7 +57,7 @@ Modeling.prototype.updateLabel = function(element, newLabel) { }; -Modeling.prototype.connect = function(source, target, attrs) { +Modeling.prototype.connect = function(source, target, attrs, hints) { var bpmnRules = this._bpmnRules; @@ -65,7 +65,7 @@ Modeling.prototype.connect = function(source, target, attrs) { attrs = bpmnRules.canConnect(source, target) || { type: 'bpmn:Association' }; } - return this.createConnection(source, target, attrs, source.parent); + return this.createConnection(source, target, attrs, source.parent, hints); }; diff --git a/test/spec/features/modeling/CreateConnectionSpec.js b/test/spec/features/modeling/CreateConnectionSpec.js index b64a6f78..97ddc4eb 100644 --- a/test/spec/features/modeling/CreateConnectionSpec.js +++ b/test/spec/features/modeling/CreateConnectionSpec.js @@ -18,132 +18,154 @@ describe('features/modeling - create connection', function() { beforeEach(bootstrapModeler(diagramXML, { modules: testModules })); - describe('connection handling', function() { + it('should connect', inject(function(elementRegistry, modeling, bpmnFactory) { - it('should execute', inject(function(elementRegistry, modeling, bpmnFactory) { + // given + var taskShape = elementRegistry.get('Task_1'), + task = taskShape.businessObject, + gatewayShape = elementRegistry.get('Gateway_1'), + gateway = gatewayShape.businessObject; - // given - var taskShape = elementRegistry.get('Task_1'), - task = taskShape.businessObject, - gatewayShape = elementRegistry.get('Gateway_1'), - gateway = gatewayShape.businessObject; + // when + var sequenceFlowConnection = modeling.createConnection(taskShape, gatewayShape, { + type: 'bpmn:SequenceFlow' + }, taskShape.parent); + + var sequenceFlow = sequenceFlowConnection.businessObject; + + // then + expect(sequenceFlowConnection).to.exist; + expect(sequenceFlow).to.exist; + + expect(sequenceFlow.sourceRef).to.eql(task); + expect(sequenceFlow.targetRef).to.eql(gateway); + + expect(task.outgoing).to.include(sequenceFlow); + expect(gateway.incoming).to.include(sequenceFlow); + + expect(sequenceFlow.di.$parent).to.eql(task.di.$parent); + expect(sequenceFlow.di.$parent.planeElement).to.include(sequenceFlow.di); + + // expect cropped connection + expect(sequenceFlowConnection.waypoints).eql([ + { original: { x: 242, y: 376 }, x: 292, y: 376 }, + { x: 410, y: 376 }, + { x: 410, y: 341 }, + { original: { x: 553, y: 341 }, x: 528, y: 341 } + ]); + + var diWaypoints = bpmnFactory.createDiWaypoints([ + { x: 292, y: 376 }, + { x: 410, y: 376 }, + { x: 410, y: 341 }, + { x: 528, y: 341 } + ]); + + // expect cropped waypoints in di + expect(sequenceFlow.di.waypoint).eql(diWaypoints); + })); - // when - var sequenceFlowConnection = modeling.createConnection(taskShape, gatewayShape, { - type: 'bpmn:SequenceFlow' - }, taskShape.parent); + it('should connect with custom start / end', inject(function(elementRegistry, modeling) { - var sequenceFlow = sequenceFlowConnection.businessObject; + // given + var sourceShape = elementRegistry.get('Task_2'), + sourcePosition = { + x: 740, + y: 400 + }, + targetShape = elementRegistry.get('Task_3'), + targetPosition = { + x: 420, + y: 130 + }; - // then - expect(sequenceFlowConnection).to.exist; - expect(sequenceFlow).to.exist; + // when + var newConnection = modeling.connect( + sourceShape, targetShape, + null, + { + connectionStart: sourcePosition, + connectionEnd: targetPosition + } + ); - expect(sequenceFlow.sourceRef).to.eql(task); - expect(sequenceFlow.targetRef).to.eql(gateway); - - expect(task.outgoing).to.include(sequenceFlow); - expect(gateway.incoming).to.include(sequenceFlow); - - expect(sequenceFlow.di.$parent).to.eql(task.di.$parent); - expect(sequenceFlow.di.$parent.planeElement).to.include(sequenceFlow.di); - - // expect cropped connection - expect(sequenceFlowConnection.waypoints).eql([ - { original: { x: 242, y: 376 }, x: 292, y: 376 }, - { x: 410, y: 376 }, - { x: 410, y: 341 }, - { original: { x: 553, y: 341 }, x: 528, y: 341 } - ]); - - var diWaypoints = bpmnFactory.createDiWaypoints([ - { x: 292, y: 376 }, - { x: 410, y: 376 }, - { x: 410, y: 341 }, - { x: 528, y: 341 } - ]); - - // expect cropped waypoints in di - expect(sequenceFlow.di.waypoint).eql(diWaypoints); - })); - - }); + // then + // expect cropped connection with custom start/end + expect(newConnection).to.have.waypoints([ + { x: 738, y: 400 }, + { x: 590, y: 400 }, + { x: 590, y: 130 }, + { x: 446, y: 130 } + ]); + })); - describe('undo support', function() { + it('should undo', inject(function(elementRegistry, commandStack, modeling) { - it('should undo', inject(function(elementRegistry, commandStack, modeling) { - - // given - var taskShape = elementRegistry.get('Task_1'), - task = taskShape.businessObject, - gatewayShape = elementRegistry.get('Gateway_1'), - gateway = gatewayShape.businessObject; + // given + var taskShape = elementRegistry.get('Task_1'), + task = taskShape.businessObject, + gatewayShape = elementRegistry.get('Gateway_1'), + gateway = gatewayShape.businessObject; - var sequenceFlowConnection = modeling.createConnection(taskShape, gatewayShape, { - type: 'bpmn:SequenceFlow' - }, taskShape.parent); + var sequenceFlowConnection = modeling.createConnection(taskShape, gatewayShape, { + type: 'bpmn:SequenceFlow' + }, taskShape.parent); - var sequenceFlow = sequenceFlowConnection.businessObject; + var sequenceFlow = sequenceFlowConnection.businessObject; - // when - commandStack.undo(); + // when + commandStack.undo(); - // then - expect(sequenceFlow.$parent).to.be.null; - expect(sequenceFlow.sourceRef).to.be.null; - expect(sequenceFlow.targetRef).to.be.null; + // then + expect(sequenceFlow.$parent).to.be.null; + expect(sequenceFlow.sourceRef).to.be.null; + expect(sequenceFlow.targetRef).to.be.null; - expect(task.outgoing).not.to.include(sequenceFlow); - expect(gateway.incoming).not.to.include(sequenceFlow); - })); - - }); + expect(task.outgoing).not.to.include(sequenceFlow); + expect(gateway.incoming).not.to.include(sequenceFlow); + })); - describe('redo support', function() { + it('should redo', inject(function(elementRegistry, commandStack, modeling) { - it('should redo', inject(function(elementRegistry, commandStack, modeling) { - - // given - var taskShape = elementRegistry.get('Task_1'), - task = taskShape.businessObject, - gatewayShape = elementRegistry.get('Gateway_1'), - gateway = gatewayShape.businessObject; + // given + var taskShape = elementRegistry.get('Task_1'), + task = taskShape.businessObject, + gatewayShape = elementRegistry.get('Gateway_1'), + gateway = gatewayShape.businessObject; - var sequenceFlowConnection = modeling.createConnection(taskShape, gatewayShape, { - type: 'bpmn:SequenceFlow' - }, taskShape.parent); + var sequenceFlowConnection = modeling.createConnection(taskShape, gatewayShape, { + type: 'bpmn:SequenceFlow' + }, taskShape.parent); - var sequenceFlow = sequenceFlowConnection.businessObject; + var sequenceFlow = sequenceFlowConnection.businessObject; - var newWaypoints = sequenceFlowConnection.waypoints, - newDiWaypoints = sequenceFlow.di.waypoint; + var newWaypoints = sequenceFlowConnection.waypoints, + newDiWaypoints = sequenceFlow.di.waypoint; - // when - commandStack.undo(); - commandStack.redo(); + // when + commandStack.undo(); + commandStack.redo(); - // then - expect(sequenceFlow.sourceRef).to.eql(task); - expect(sequenceFlow.targetRef).to.eql(gateway); + // then + expect(sequenceFlow.sourceRef).to.eql(task); + expect(sequenceFlow.targetRef).to.eql(gateway); - expect(task.outgoing).to.include(sequenceFlow); - expect(gateway.incoming).to.include(sequenceFlow); + expect(task.outgoing).to.include(sequenceFlow); + expect(gateway.incoming).to.include(sequenceFlow); - expect(sequenceFlow.di.$parent).to.eql(task.di.$parent); - expect(sequenceFlow.di.$parent.planeElement).to.include(sequenceFlow.di); + expect(sequenceFlow.di.$parent).to.eql(task.di.$parent); + expect(sequenceFlow.di.$parent.planeElement).to.include(sequenceFlow.di); - // expect cropped connection - expect(sequenceFlowConnection.waypoints).eql(newWaypoints); + // expect cropped connection + expect(sequenceFlowConnection.waypoints).eql(newWaypoints); - // expect cropped waypoints in di - expect(sequenceFlow.di.waypoint).eql(newDiWaypoints); - })); - - }); + // expect cropped waypoints in di + expect(sequenceFlow.di.waypoint).eql(newDiWaypoints); + })); });