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:
parent
d80076a034
commit
085cedfda1
|
@ -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)
|
||||
) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 }
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue