From 6eceb0926b49b43e4042a53f588f82b386b93c23 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Wed, 22 Apr 2015 09:08:38 +0200 Subject: [PATCH] feat(layout): add straight layouting for message flows This commit adds a straight layouting strategy for message flows. Other than that it makes sure connection attachments are being remembered during reconnection / shape move. Closes #249 Closes #179 --- lib/features/modeling/BpmnLayouter.js | 65 +++++++++++ lib/features/modeling/Layouter.js | 37 ------ lib/features/modeling/index.js | 2 +- .../bpmn/collaboration-message-flows.bpmn | 34 +++--- .../features/modeling/LayoutConnectionSpec.js | 3 +- .../modeling/LayoutMessageFlowSpec.js | 106 ++++++++++++++++++ test/spec/features/modeling/MoveShapeSpec.js | 4 +- 7 files changed, 193 insertions(+), 58 deletions(-) create mode 100644 lib/features/modeling/BpmnLayouter.js delete mode 100644 lib/features/modeling/Layouter.js create mode 100644 test/spec/features/modeling/LayoutMessageFlowSpec.js diff --git a/lib/features/modeling/BpmnLayouter.js b/lib/features/modeling/BpmnLayouter.js new file mode 100644 index 00000000..7f50fae7 --- /dev/null +++ b/lib/features/modeling/BpmnLayouter.js @@ -0,0 +1,65 @@ +'use strict'; + +var inherits = require('inherits'); + +var assign = require('lodash/object/assign'); + +var BaseLayouter = require('diagram-js/lib/layout/BaseLayouter'), + LayoutUtil = require('diagram-js/lib/layout/LayoutUtil'), + ManhattanLayout = require('diagram-js/lib/layout/ManhattanLayout'); + +var is = require('./ModelingUtil').is; + + +function BpmnLayouter() {} + +inherits(BpmnLayouter, BaseLayouter); + +module.exports = BpmnLayouter; + + +function getAttachment(waypoints, idx, shape) { + var point = waypoints && waypoints[idx]; + + return point ? (point.original || point) : LayoutUtil.getMidPoint(shape); +} + + +BpmnLayouter.prototype.layoutConnection = function(connection, hints) { + var source = connection.source, + target = connection.target, + waypoints = connection.waypoints, + start, + end; + + var layoutManhattan, + updatedWaypoints; + + start = getAttachment(waypoints, 0, source); + end = getAttachment(waypoints, waypoints && waypoints.length - 1, target); + + // manhattan layout sequence / message flows + if (is(connection, 'bpmn:MessageFlow')) { + layoutManhattan = { + preferStraight: true, + preferVertical: true + }; + } + + if (is(connection, 'bpmn:SequenceFlow')) { + layoutManhattan = {}; + } + + if (layoutManhattan) { + + layoutManhattan = assign(layoutManhattan, hints); + + updatedWaypoints = + ManhattanLayout.repairConnection( + source, target, start, end, + waypoints, + layoutManhattan); + } + + return updatedWaypoints || [ start, end ]; +}; \ No newline at end of file diff --git a/lib/features/modeling/Layouter.js b/lib/features/modeling/Layouter.js deleted file mode 100644 index 462de07c..00000000 --- a/lib/features/modeling/Layouter.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var inherits = require('inherits'); - -var BaseLayouter = require('diagram-js/lib/features/modeling/Layouter'), - LayoutUtil = require('diagram-js/lib/layout/Util'), - ManhattanLayout = require('diagram-js/lib/layout/ManhattanLayout'); - - -function Layouter() {} - -inherits(Layouter, BaseLayouter); - -module.exports = Layouter; - - -Layouter.prototype.getConnectionWaypoints = function(connection) { - var source = connection.source, - start = LayoutUtil.getMidPoint(source), - target = connection.target, - end = LayoutUtil.getMidPoint(target); - - var bo = connection.businessObject; - - // manhattan layout sequence / message flows - if (bo.$instanceOf('bpmn:SequenceFlow') || - bo.$instanceOf('bpmn:MessageFlow')) { - - var waypoints = ManhattanLayout.repairConnection(source, target, start, end, connection.waypoints); - - if (waypoints) { - return waypoints; - } - } - - return [ start, end ]; -}; \ No newline at end of file diff --git a/lib/features/modeling/index.js b/lib/features/modeling/index.js index 94db0b01..e5862088 100644 --- a/lib/features/modeling/index.js +++ b/lib/features/modeling/index.js @@ -12,6 +12,6 @@ module.exports = { elementFactory: [ 'type', require('./ElementFactory') ], modeling: [ 'type', require('./Modeling') ], labelSupport: [ 'type', require('./LabelSupport') ], - layouter: [ 'type', require('./Layouter') ], + layouter: [ 'type', require('./BpmnLayouter') ], connectionDocking: [ 'type', require('diagram-js/lib/layout/CroppingConnectionDocking') ] }; diff --git a/test/fixtures/bpmn/collaboration-message-flows.bpmn b/test/fixtures/bpmn/collaboration-message-flows.bpmn index a8cba171..206b73a6 100644 --- a/test/fixtures/bpmn/collaboration-message-flows.bpmn +++ b/test/fixtures/bpmn/collaboration-message-flows.bpmn @@ -1,9 +1,9 @@ - + - + @@ -13,6 +13,7 @@ + @@ -34,20 +35,18 @@ - + - - - - - + + + - + - - + + @@ -55,12 +54,12 @@ - - + + - + @@ -71,13 +70,14 @@ - - - + + + + \ No newline at end of file diff --git a/test/spec/features/modeling/LayoutConnectionSpec.js b/test/spec/features/modeling/LayoutConnectionSpec.js index 83c89708..27a3c61d 100644 --- a/test/spec/features/modeling/LayoutConnectionSpec.js +++ b/test/spec/features/modeling/LayoutConnectionSpec.js @@ -36,8 +36,9 @@ describe('features/modeling - layout connection', function() { // then // expect cropped, repaired connection + // that was not actually modified expect(sequenceFlowConnection.waypoints).toDeepEqual([ - { original: { x: 553, y: 341 }, x: 578, y: 341 }, + { original: { x: 578, y: 341 }, x: 578, y: 341 }, { x: 934, y: 341 }, { x: 934, y: 436 }, { original: { x: 832, y: 436 }, x: 832, y: 436 } diff --git a/test/spec/features/modeling/LayoutMessageFlowSpec.js b/test/spec/features/modeling/LayoutMessageFlowSpec.js new file mode 100644 index 00000000..3ad8bd96 --- /dev/null +++ b/test/spec/features/modeling/LayoutMessageFlowSpec.js @@ -0,0 +1,106 @@ +'use strict'; + +var Matchers = require('../../../Matchers'), + TestHelper = require('../../../TestHelper'); + +/* global bootstrapModeler, inject */ + + +var modelingModule = require('../../../../lib/features/modeling'), + coreModule = require('../../../../lib/core'); + + +describe('features/modeling - layout message flows', function() { + + beforeEach(Matchers.addDeepEquals); + + + var diagramXML = require('../../../fixtures/bpmn/collaboration-message-flows.bpmn'); + + var testModules = [ coreModule, modelingModule ]; + + beforeEach(bootstrapModeler(diagramXML, { modules: testModules })); + + + it('should layout manhattan after Task move', inject(function(elementRegistry, modeling) { + + // given + var taskShape = elementRegistry.get('Task_1'), + messageFlowConnection = elementRegistry.get('MessageFlow_4'); + + // when + modeling.moveShapes([ taskShape ], { x: 30, y: 20 }); + + // then + + // expect cropped, repaired manhattan connection + expect(messageFlowConnection.waypoints).toDeepEqual([ + { original: { x: 420, y: 234 }, x: 420, y: 234 }, + { x: 420, y: 387 }, + { x: 318, y: 387 }, + { original: { x: 318, y: 448 }, x: 318, y: 448 } + ]); + })); + + + it('should layout straight after Task move', inject(function(elementRegistry, modeling) { + + // given + var taskShape = elementRegistry.get('Task_2'), + messageFlowConnection = elementRegistry.get('MessageFlow_1'); + + // when + modeling.moveShapes([ taskShape ], { x: 20, y: -20 }); + + // then + + // expect cropped, repaired manhattan connection + expect(messageFlowConnection.waypoints).toDeepEqual([ + { original: { x: 610, y: 194 }, x: 610, y: 194 }, + { original: { x: 610, y: 415 }, x: 610, y: 415 } + ]); + })); + + + it('should layout straight after Participant move', inject(function(elementRegistry, modeling) { + + // given + var participantShape = elementRegistry.get('Participant_1'), + messageFlowConnection = elementRegistry.get('MessageFlow_5'); + + // when + modeling.moveShapes([ participantShape ], { x: 100, y: 50 }); + + // then + + // expect cropped, repaired manhattan connection + expect(messageFlowConnection.waypoints).toDeepEqual([ + { original: { x: 671, y: 214 }, x: 671, y: 214 }, + { original: { x: 671, y: 465 }, x: 671, y: 465 } + ]); + + })); + + + it('should layout manhattan after Participant move beyond EndEvent bounds', + inject(function(elementRegistry, modeling) { + + // given + var participantShape = elementRegistry.get('Participant_1'), + messageFlowConnection = elementRegistry.get('MessageFlow_5'); + + // when + modeling.moveShapes([ participantShape ], { x: -200, y: 0 }); + + // then + + // expect cropped, repaired manhattan connection + expect(messageFlowConnection.waypoints).toDeepEqual([ + { original: { x: 671, y: 214 }, x: 671, y: 214 }, + { x: 671, y: 315 }, + { x: 471, y: 315 }, + { original: { x: 471, y: 415 }, x: 471, y: 415 } + ]); + })); + +}); \ No newline at end of file diff --git a/test/spec/features/modeling/MoveShapeSpec.js b/test/spec/features/modeling/MoveShapeSpec.js index 945d8a2c..fff5d83d 100644 --- a/test/spec/features/modeling/MoveShapeSpec.js +++ b/test/spec/features/modeling/MoveShapeSpec.js @@ -47,10 +47,10 @@ describe('features/modeling - move shape', function() { // expect flow layout expect(sequenceFlowElement.waypoints).toDeepEqual([ - { original: { x: 370, y: 310 }, x: 388, y: 310 }, + { original: { x: 388, y: 310 }, x: 388, y: 310 }, { x: 404, y: 310 }, { x: 404, y: 260 }, - { original: { x: 470, y: 260 }, x: 420, y: 260 } + { original: { x: 420, y: 260 }, x: 420, y: 260 } ]); expect(sequenceFlow.di.waypoint).toDeepEqual([