feat(layout): correctly lay out Boundary Event loops

Loops will now be laid out with respect to minimum
second segment width.
This commit is contained in:
Maciej Barelkowski 2019-06-06 15:05:35 +02:00 committed by Nico Rehwaldt
parent d80076a034
commit 085cedfda1
3 changed files with 201 additions and 86 deletions

View File

@ -23,6 +23,8 @@ import {
import { is } from '../../util/ModelUtil';
var BOUNDARY_TO_HOST_THRESHOLD = 40;
export default function BpmnLayouter() {}
inherits(BpmnLayouter, BaseLayouter);
@ -86,7 +88,7 @@ BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
if (is(source, 'bpmn:BoundaryEvent')) {
manhattanOptions = {
preferredLayouts: getBoundaryEventPreferredLayouts(source, target)
preferredLayouts: getBoundaryEventPreferredLayouts(source, target, end)
};
} else
@ -262,7 +264,7 @@ function isHorizontalOrientation(orientation) {
return orientation === 'right' || orientation === 'left';
}
function getBoundaryEventPreferredLayouts(source, target) {
function getBoundaryEventPreferredLayouts(source, target, end) {
var sourceMid = getMid(source),
targetMid = getMid(target),
attachOrientation = getAttachOrientation(source),
@ -278,16 +280,52 @@ function getBoundaryEventPreferredLayouts(source, target) {
y: source.height / 2 + target.height / 2
});
if (isLoop) {
return getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end);
}
// source layout
sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide, isLoop);
sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide);
// target layout
targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide, isLoop);
targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide);
return [ sourceLayout + ':' + targetLayout ];
}
function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide, isLoop) {
function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) {
var sourceLayout = orientationDirectionMapping[attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation)],
targetLayout;
if (attachedToSide) {
if (isHorizontalOrientation(attachOrientation)) {
targetLayout = shouldConnectToSameSide('y', source, target, end) ? 'h' : 'b';
} else {
targetLayout = shouldConnectToSameSide('x', source, target, end) ? 'v' : 'l';
}
} else {
targetLayout = 'v';
}
return [ sourceLayout + ':' + targetLayout ];
}
function shouldConnectToSameSide(axis, source, target, end) {
var threshold = BOUNDARY_TO_HOST_THRESHOLD;
return !(
areCloseOnAxis(axis, end, target, threshold) ||
areCloseOnAxis(axis, end, { x: target.x + target.width, y: target.y + target.height }, threshold) ||
areCloseOnAxis(axis, end, getMid(source), threshold)
);
}
function areCloseOnAxis(axis, a, b, threshold) {
return Math.abs(a[axis] - b[axis]) < threshold;
}
function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) {
// attached to either top, right, bottom or left side
if (attachedToSide) {
@ -296,14 +334,12 @@ function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, atta
// attached to either top-right, top-left, bottom-right or bottom-left corner
// loop, same vertical or opposite horizontal orientation
if (isLoop ||
isSame(
getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)
) ||
isOppositeOrientation(
getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
)) {
// same vertical or opposite horizontal orientation
if (isSame(
getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)
) || isOppositeOrientation(
getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
)) {
return orientationDirectionMapping[getVerticalOrientation(attachOrientation)];
}
@ -311,16 +347,15 @@ function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, atta
return orientationDirectionMapping[getHorizontalOrientation(attachOrientation)];
}
function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide, isLoop) {
function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) {
// attached to either top, right, bottom or left side
if (attachedToSide) {
if (isHorizontalOrientation(attachOrientation)) {
// orientation is 'right' or 'left'
// loop or opposite horizontal orientation or same orientation
// opposite horizontal orientation or same orientation
if (
isLoop ||
isOppositeHorizontalOrientation(attachOrientation, targetOrientation) ||
isSame(attachOrientation, targetOrientation)
) {
@ -332,9 +367,8 @@ function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, atta
} else {
// orientation is 'top' or 'bottom'
// loop or opposite vertical orientation or same orientation
// opposite vertical orientation or same orientation
if (
isLoop ||
isOppositeVerticalOrientation(attachOrientation, targetOrientation) ||
isSame(attachOrientation, targetOrientation)
) {

View File

@ -1,40 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.16.0">
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.1.1">
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:subProcess id="SubProcess" />
<bpmn:boundaryEvent id="BoundaryEvent_TopLeft" attachedToRef="SubProcess" />
<bpmn:boundaryEvent id="BoundaryEvent_TopCenter" attachedToRef="SubProcess" />
<bpmn:boundaryEvent id="BoundaryEvent_BottomRight" attachedToRef="SubProcess" />
<bpmn:boundaryEvent id="BoundaryEvent_BottomCenter" attachedToRef="SubProcess" />
<bpmn:boundaryEvent id="BoundaryEvent_BottomLeft" attachedToRef="SubProcess" />
<bpmn:boundaryEvent id="BoundaryEvent_TopRight" attachedToRef="SubProcess" />
<bpmn:boundaryEvent id="BoundaryEvent_RightCenter" attachedToRef="SubProcess" />
<bpmn:subProcess id="SubProcess_2" />
<bpmn:boundaryEvent id="BoundaryEvent_CenterRight" attachedToRef="SubProcess_2" />
<bpmn:boundaryEvent id="BoundaryEvent_BottomCenter" attachedToRef="SubProcess_2" />
<bpmn:boundaryEvent id="BoundaryEvent_TopCenter" attachedToRef="SubProcess_2" />
<bpmn:boundaryEvent id="BoundaryEvent_CenterLeft" attachedToRef="SubProcess_2" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="SubProcess_12qmapm_di" bpmnElement="SubProcess" isExpanded="true">
<dc:Bounds x="300" y="300" width="350" height="200" />
<dc:Bounds x="200" y="200" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_0s0nl1k_di" bpmnElement="BoundaryEvent_TopLeft">
<dc:Bounds x="282" y="320" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_TopCenter_di" bpmnElement="BoundaryEvent_TopCenter">
<dc:Bounds x="432" y="282" width="36" height="36" />
<dc:Bounds x="182" y="220" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_0nomac7_di" bpmnElement="BoundaryEvent_BottomRight">
<dc:Bounds x="632" y="450" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_BottomCenter_di" bpmnElement="BoundaryEvent_BottomCenter">
<dc:Bounds x="432" y="482" width="36" height="36" />
<dc:Bounds x="532" y="350" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_1spolhy_di" bpmnElement="BoundaryEvent_BottomLeft">
<dc:Bounds x="282" y="482" width="36" height="36" />
<dc:Bounds x="182" y="382" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_13iwzlu_di" bpmnElement="BoundaryEvent_TopRight">
<dc:Bounds x="632" y="282" width="36" height="36" />
<dc:Bounds x="532" y="182" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_RightCenter_di" bpmnElement="BoundaryEvent_RightCenter">
<dc:Bounds x="632" y="372" width="36" height="36" />
<bpmndi:BPMNShape id="SubProcess_2_di" bpmnElement="SubProcess_2" isExpanded="true">
<dc:Bounds x="200" y="460" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_CenterRight_di" bpmnElement="BoundaryEvent_CenterRight">
<dc:Bounds x="532" y="542" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_BottomCenter_di" bpmnElement="BoundaryEvent_BottomCenter">
<dc:Bounds x="357" y="642" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_TopCenter_di" bpmnElement="BoundaryEvent_TopCenter">
<dc:Bounds x="357" y="442" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_CenterLeft_di" bpmnElement="BoundaryEvent_CenterLeft">
<dc:Bounds x="182" y="542" width="36" height="36" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>

View File

@ -26,65 +26,138 @@ describe('features/modeling - layout', function() {
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('attached top right', function() {
// when
var connection = connect('BoundaryEvent_TopRight', 'SubProcess');
describe('in the corner', function() {
// then
expect(connection).to.have.waypoints([
{ original: { x: 650, y: 300 }, x: 650, y: 282 },
{ x: 650, y: 262 },
{ x: 475, y: 262 },
{ original: { x: 475, y: 400 }, x: 475, y: 300 }
]);
it('attached top right', function() {
// when
var connection = connect('BoundaryEvent_TopRight', 'SubProcess');
// then
expect(connection).to.have.waypoints([
{ original: { x: 550, y: 200 }, x: 550, y: 182 },
{ x: 550, y: 162 },
{ x: 375, y: 162 },
{ original: { x: 375, y: 300 }, x: 375, y: 200 }
]);
});
it('attached bottom right', function() {
// when
var connection = connect('BoundaryEvent_BottomRight', 'SubProcess');
// then
expect(connection).to.have.waypoints([
{ original: { x: 550, y: 368 } , x: 568, y: 368 },
{ x: 588, y: 368 },
{ x: 588, y: 300 },
{ original: { x: 375, y: 300 } , x: 550, y: 300 }
]);
});
it('attached bottom left', function() {
// when
var connection = connect('BoundaryEvent_BottomLeft', 'SubProcess');
// then
expect(connection).to.have.waypoints([
{ original: { x: 200, y: 500 }, x: 200, y: 418 },
{ x: 200, y: 438 },
{ x: 375, y: 438 },
{ original: { x: 375, y: 300 }, x: 375, y: 400 }
]);
});
it('attached top left', function() {
// when
var connection = connect('BoundaryEvent_TopLeft', 'SubProcess');
// then
expect(connection).to.have.waypoints([
{ original: { x: 200, y: 238 }, x: 182, y: 238 },
{ x: 162, y: 238 },
{ x: 162, y: 300 },
{ original: { x: 375, y: 300 }, x: 200, y: 300 }
]);
});
});
it('attached bottom right', function() {
describe('on the side center', function() {
// when
var connection = connect('BoundaryEvent_BottomRight', 'SubProcess');
var host = 'SubProcess_2';
// then
expect(connection).to.have.waypoints([
{ original: { x: 650, y: 468 }, x: 668, y: 468 },
{ x: 688, y: 468 },
{ x: 688, y: 400 },
{ original: { x: 475, y: 400 }, x: 650, y: 400 }
]);
it('attached top center', function() {
// when
var connection = connect('BoundaryEvent_TopCenter', host);
// then
expect(connection).to.have.waypoints([
{ original: { x: 375, y: 460 }, x: 375, y: 442 },
{ x:375, y: 422 },
{ x:180, y: 422 },
{ x:180, y: 560 },
{ original:{ x: 375, y: 560 }, x: 200, y: 560 }
]);
});
it('attached center right', function() {
// when
var connection = connect('BoundaryEvent_CenterRight', host);
// then
expect(connection).to.have.waypoints([
{ original: { x: 550, y: 560 }, x: 568, y: 560 },
{ x: 588, y: 560 },
{ x: 588, y: 680 },
{ x: 375, y: 680 },
{ original: { x: 375, y: 560 }, x: 375, y: 660 }
]);
});
it('attached bottom center', function() {
// when
var connection = connect('BoundaryEvent_BottomCenter', host);
// then
expect(connection).to.have.waypoints([
{ original: { x: 375, y: 660 }, x: 375, y: 678 },
{ x: 375, y: 698 },
{ x: 180, y: 698 },
{ x: 180, y: 560 },
{ original: { x: 375, y: 560 }, x: 200, y: 560 }
]);
});
it('attached center left', function() {
// when
var connection = connect('BoundaryEvent_CenterLeft', host);
// then
expect(connection).to.have.waypoints([
{ original: { x: 200, y: 560 }, x: 182, y: 560 },
{ x: 162, y: 560 },
{ x: 162, y: 680 },
{ x: 375, y: 680 },
{ original: { x: 375, y: 560 }, x: 375, y: 660 }
]);
});
});
it('attached bottom left', function() {
// when
var connection = connect('BoundaryEvent_BottomLeft', 'SubProcess');
// then
expect(connection).to.have.waypoints([
{ original: { x: 300, y: 500 }, x: 300, y: 518 },
{ x: 300, y: 538 },
{ x: 475, y: 538 },
{ original: { x: 475, y: 400 }, x: 475, y: 500 }
]);
});
it('attached top left', function() {
// when
var connection = connect('BoundaryEvent_TopLeft', 'SubProcess');
// then
expect(connection).to.have.waypoints([
{ original: { x: 300, y: 338 }, x: 282, y: 338 },
{ x: 262, y: 338 },
{ x: 262, y: 400 },
{ original: { x: 475, y: 400 }, x: 300, y: 400 }
]);
});
});