chore(layouter): refactor
This commit is contained in:
parent
5c5656aceb
commit
0ed0492641
|
@ -22,195 +22,8 @@ import {
|
||||||
|
|
||||||
import { is } from '../../util/ModelUtil';
|
import { is } from '../../util/ModelUtil';
|
||||||
|
|
||||||
|
var ATTACH_ORIENTATION_PADDING = -10,
|
||||||
var BOUNDARY_TO_HOST_THRESHOLD = 40;
|
BOUNDARY_TO_HOST_THRESHOLD = 40;
|
||||||
|
|
||||||
export default function BpmnLayouter() {}
|
|
||||||
|
|
||||||
inherits(BpmnLayouter, BaseLayouter);
|
|
||||||
|
|
||||||
|
|
||||||
BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
|
|
||||||
if (!hints) {
|
|
||||||
hints = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var source = hints.source || connection.source,
|
|
||||||
target = hints.target || connection.target,
|
|
||||||
waypoints = hints.waypoints || connection.waypoints,
|
|
||||||
start = hints.connectionStart,
|
|
||||||
end = hints.connectionEnd;
|
|
||||||
|
|
||||||
var manhattanOptions,
|
|
||||||
updatedWaypoints;
|
|
||||||
|
|
||||||
if (!start) {
|
|
||||||
start = getConnectionDocking(waypoints && waypoints[0], source);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!end) {
|
|
||||||
end = getConnectionDocking(waypoints && waypoints[waypoints.length - 1], target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(nikku): support vertical modeling
|
|
||||||
// and invert preferredLayouts accordingly
|
|
||||||
|
|
||||||
if (is(connection, 'bpmn:Association') ||
|
|
||||||
is(connection, 'bpmn:DataAssociation')) {
|
|
||||||
|
|
||||||
if (waypoints && !isCompensationAssociation(source, target)) {
|
|
||||||
return [].concat([ start ], waypoints.slice(1, -1), [ end ]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// manhattan layout sequence / message flows
|
|
||||||
if (is(connection, 'bpmn:MessageFlow')) {
|
|
||||||
manhattanOptions = getMessageFlowManhattanOptions(source, target);
|
|
||||||
|
|
||||||
} else
|
|
||||||
|
|
||||||
|
|
||||||
// layout all connection between flow elements h:h,
|
|
||||||
//
|
|
||||||
// except for
|
|
||||||
//
|
|
||||||
// (1) outgoing of BoundaryEvents -> layout based on attach orientation and target orientation
|
|
||||||
// (2) incoming / outgoing of Gateway -> v:h (outgoing), h:v (incoming)
|
|
||||||
// (3) loops from / to the same element
|
|
||||||
//
|
|
||||||
if (is(connection, 'bpmn:SequenceFlow') ||
|
|
||||||
isCompensationAssociation(source, target)) {
|
|
||||||
|
|
||||||
if (source === target) {
|
|
||||||
manhattanOptions = {
|
|
||||||
preferredLayouts: getLoopPreferredLayout(source, connection)
|
|
||||||
};
|
|
||||||
} else
|
|
||||||
|
|
||||||
if (is(source, 'bpmn:BoundaryEvent')) {
|
|
||||||
|
|
||||||
manhattanOptions = {
|
|
||||||
preferredLayouts: getBoundaryEventPreferredLayouts(source, target, end)
|
|
||||||
};
|
|
||||||
|
|
||||||
} else
|
|
||||||
|
|
||||||
if (is(source, 'bpmn:Gateway')) {
|
|
||||||
|
|
||||||
manhattanOptions = {
|
|
||||||
preferredLayouts: [ 'v:h' ]
|
|
||||||
};
|
|
||||||
} else
|
|
||||||
|
|
||||||
if (is(target, 'bpmn:Gateway')) {
|
|
||||||
|
|
||||||
manhattanOptions = {
|
|
||||||
preferredLayouts: [ 'h:v' ]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
manhattanOptions = {
|
|
||||||
preferredLayouts: [ 'h:h' ]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (manhattanOptions) {
|
|
||||||
|
|
||||||
manhattanOptions = assign(manhattanOptions, hints);
|
|
||||||
|
|
||||||
updatedWaypoints =
|
|
||||||
withoutRedundantPoints(
|
|
||||||
repairConnection(
|
|
||||||
source, target,
|
|
||||||
start, end,
|
|
||||||
waypoints,
|
|
||||||
manhattanOptions
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedWaypoints || [ start, end ];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function getAttachOrientation(attachedElement) {
|
|
||||||
|
|
||||||
var hostElement = attachedElement.host,
|
|
||||||
padding = -10;
|
|
||||||
|
|
||||||
return getOrientation(getMid(attachedElement), hostElement, padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getMessageFlowManhattanOptions(source, target) {
|
|
||||||
return {
|
|
||||||
preferredLayouts: [ 'straight', 'v:v' ],
|
|
||||||
preserveDocking: getMessageFlowPreserveDocking(source, target)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getMessageFlowPreserveDocking(source, target) {
|
|
||||||
|
|
||||||
// (1) docking element connected to participant has precedence
|
|
||||||
|
|
||||||
if (is(target, 'bpmn:Participant')) {
|
|
||||||
return 'source';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is(source, 'bpmn:Participant')) {
|
|
||||||
return 'target';
|
|
||||||
}
|
|
||||||
|
|
||||||
// (2) docking element connected to expanded sub-process has precedence
|
|
||||||
|
|
||||||
if (isExpandedSubProcess(target)) {
|
|
||||||
return 'source';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isExpandedSubProcess(source)) {
|
|
||||||
return 'target';
|
|
||||||
}
|
|
||||||
|
|
||||||
// (3) docking event has precedence
|
|
||||||
|
|
||||||
if (is(target, 'bpmn:Event')) {
|
|
||||||
return 'target';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is(source, 'bpmn:Event')) {
|
|
||||||
return 'source';
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getConnectionDocking(point, shape) {
|
|
||||||
return point ? (point.original || point) : getMid(shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCompensationAssociation(source, target) {
|
|
||||||
return is(target, 'bpmn:Activity') &&
|
|
||||||
is(source, 'bpmn:BoundaryEvent') &&
|
|
||||||
target.businessObject.isForCompensation;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function isExpandedSubProcess(element) {
|
|
||||||
return is(element, 'bpmn:SubProcess') && isExpanded(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSame(a, b) {
|
|
||||||
return a === b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAnyOrientation(orientation, orientations) {
|
|
||||||
return orientations.indexOf(orientation) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oppositeOrientationMapping = {
|
var oppositeOrientationMapping = {
|
||||||
'top': 'bottom',
|
'top': 'bottom',
|
||||||
|
@ -230,6 +43,162 @@ var orientationDirectionMapping = {
|
||||||
left: 'l'
|
left: 'l'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default function BpmnLayouter() {}
|
||||||
|
|
||||||
|
inherits(BpmnLayouter, BaseLayouter);
|
||||||
|
|
||||||
|
|
||||||
|
BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
|
||||||
|
if (!hints) {
|
||||||
|
hints = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = hints.source || connection.source,
|
||||||
|
target = hints.target || connection.target,
|
||||||
|
waypoints = hints.waypoints || connection.waypoints,
|
||||||
|
connectionStart = hints.connectionStart,
|
||||||
|
connectionEnd = hints.connectionEnd;
|
||||||
|
|
||||||
|
var manhattanOptions,
|
||||||
|
updatedWaypoints;
|
||||||
|
|
||||||
|
if (!connectionStart) {
|
||||||
|
connectionStart = getConnectionDocking(waypoints && waypoints[ 0 ], source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connectionEnd) {
|
||||||
|
connectionEnd = getConnectionDocking(waypoints && waypoints[ waypoints.length - 1 ], target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(nikku): support vertical modeling
|
||||||
|
// and invert preferredLayouts accordingly
|
||||||
|
|
||||||
|
if (is(connection, 'bpmn:Association') ||
|
||||||
|
is(connection, 'bpmn:DataAssociation')) {
|
||||||
|
|
||||||
|
if (waypoints && !isCompensationAssociation(source, target)) {
|
||||||
|
return [].concat([ connectionStart ], waypoints.slice(1, -1), [ connectionEnd ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is(connection, 'bpmn:MessageFlow')) {
|
||||||
|
manhattanOptions = getMessageFlowManhattanOptions(source, target);
|
||||||
|
} else if (is(connection, 'bpmn:SequenceFlow') || isCompensationAssociation(source, target)) {
|
||||||
|
|
||||||
|
// layout all connection between flow elements h:h, except for
|
||||||
|
// (1) outgoing of boundary events -> layout based on attach orientation and target orientation
|
||||||
|
// (2) incoming/outgoing of gateways -> v:h for outgoing, h:v for incoming
|
||||||
|
// (3) loops
|
||||||
|
if (source === target) {
|
||||||
|
manhattanOptions = {
|
||||||
|
preferredLayouts: getLoopPreferredLayout(source, connection)
|
||||||
|
};
|
||||||
|
} else if (is(source, 'bpmn:BoundaryEvent')) {
|
||||||
|
manhattanOptions = {
|
||||||
|
preferredLayouts: getBoundaryEventPreferredLayouts(source, target, connectionEnd)
|
||||||
|
};
|
||||||
|
} else if (is(source, 'bpmn:Gateway')) {
|
||||||
|
manhattanOptions = {
|
||||||
|
preferredLayouts: [ 'v:h' ]
|
||||||
|
};
|
||||||
|
} else if (is(target, 'bpmn:Gateway')) {
|
||||||
|
manhattanOptions = {
|
||||||
|
preferredLayouts: [ 'h:v' ]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
manhattanOptions = {
|
||||||
|
preferredLayouts: [ 'h:h' ]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manhattanOptions) {
|
||||||
|
manhattanOptions = assign(manhattanOptions, hints);
|
||||||
|
|
||||||
|
updatedWaypoints = withoutRedundantPoints(repairConnection(
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
connectionStart,
|
||||||
|
connectionEnd,
|
||||||
|
waypoints,
|
||||||
|
manhattanOptions
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedWaypoints || [ connectionStart, connectionEnd ];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// helpers //////////
|
||||||
|
|
||||||
|
function getAttachOrientation(attachedElement) {
|
||||||
|
var hostElement = attachedElement.host;
|
||||||
|
|
||||||
|
return getOrientation(getMid(attachedElement), hostElement, ATTACH_ORIENTATION_PADDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMessageFlowManhattanOptions(source, target) {
|
||||||
|
return {
|
||||||
|
preferredLayouts: [ 'straight', 'v:v' ],
|
||||||
|
preserveDocking: getMessageFlowPreserveDocking(source, target)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMessageFlowPreserveDocking(source, target) {
|
||||||
|
|
||||||
|
// (1) docking element connected to participant has precedence
|
||||||
|
if (is(target, 'bpmn:Participant')) {
|
||||||
|
return 'source';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is(source, 'bpmn:Participant')) {
|
||||||
|
return 'target';
|
||||||
|
}
|
||||||
|
|
||||||
|
// (2) docking element connected to expanded sub-process has precedence
|
||||||
|
if (isExpandedSubProcess(target)) {
|
||||||
|
return 'source';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExpandedSubProcess(source)) {
|
||||||
|
return 'target';
|
||||||
|
}
|
||||||
|
|
||||||
|
// (3) docking event has precedence
|
||||||
|
if (is(target, 'bpmn:Event')) {
|
||||||
|
return 'target';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is(source, 'bpmn:Event')) {
|
||||||
|
return 'source';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConnectionDocking(point, shape) {
|
||||||
|
return point ? (point.original || point) : getMid(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCompensationAssociation(source, target) {
|
||||||
|
return is(target, 'bpmn:Activity') &&
|
||||||
|
is(source, 'bpmn:BoundaryEvent') &&
|
||||||
|
target.businessObject.isForCompensation;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExpandedSubProcess(element) {
|
||||||
|
return is(element, 'bpmn:SubProcess') && isExpanded(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSame(a, b) {
|
||||||
|
return a === b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnyOrientation(orientation, orientations) {
|
||||||
|
return orientations.indexOf(orientation) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
function getHorizontalOrientation(orientation) {
|
function getHorizontalOrientation(orientation) {
|
||||||
var matches = /right|left/.exec(orientation);
|
var matches = /right|left/.exec(orientation);
|
||||||
|
|
||||||
|
@ -312,8 +281,8 @@ function getBoundaryEventPreferredLayouts(source, target, end) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) {
|
function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) {
|
||||||
|
var orientation = attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation),
|
||||||
var sourceLayout = orientationDirectionMapping[attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation)],
|
sourceLayout = orientationDirectionMapping[ orientation ],
|
||||||
targetLayout;
|
targetLayout;
|
||||||
|
|
||||||
if (attachedToSide) {
|
if (attachedToSide) {
|
||||||
|
@ -334,20 +303,23 @@ function shouldConnectToSameSide(axis, source, target, end) {
|
||||||
|
|
||||||
return !(
|
return !(
|
||||||
areCloseOnAxis(axis, end, target, threshold) ||
|
areCloseOnAxis(axis, end, target, threshold) ||
|
||||||
areCloseOnAxis(axis, end, { x: target.x + target.width, y: target.y + target.height }, threshold) ||
|
areCloseOnAxis(axis, end, {
|
||||||
|
x: target.x + target.width,
|
||||||
|
y: target.y + target.height
|
||||||
|
}, threshold) ||
|
||||||
areCloseOnAxis(axis, end, getMid(source), threshold)
|
areCloseOnAxis(axis, end, getMid(source), threshold)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function areCloseOnAxis(axis, a, b, threshold) {
|
function areCloseOnAxis(axis, a, b, threshold) {
|
||||||
return Math.abs(a[axis] - b[axis]) < threshold;
|
return Math.abs(a[ axis ] - b[ axis ]) < threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) {
|
function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) {
|
||||||
|
|
||||||
// attached to either top, right, bottom or left side
|
// attached to either top, right, bottom or left side
|
||||||
if (attachedToSide) {
|
if (attachedToSide) {
|
||||||
return orientationDirectionMapping[attachOrientation];
|
return orientationDirectionMapping[ attachOrientation ];
|
||||||
}
|
}
|
||||||
|
|
||||||
// attached to either top-right, top-left, bottom-right or bottom-left corner
|
// attached to either top-right, top-left, bottom-right or bottom-left corner
|
||||||
|
@ -358,11 +330,11 @@ function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, atta
|
||||||
) || isOppositeOrientation(
|
) || isOppositeOrientation(
|
||||||
getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
|
getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
|
||||||
)) {
|
)) {
|
||||||
return orientationDirectionMapping[getVerticalOrientation(attachOrientation)];
|
return orientationDirectionMapping[ getVerticalOrientation(attachOrientation) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback
|
// fallback
|
||||||
return orientationDirectionMapping[getHorizontalOrientation(attachOrientation)];
|
return orientationDirectionMapping[ getHorizontalOrientation(attachOrientation) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) {
|
function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) {
|
||||||
|
@ -371,7 +343,7 @@ function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, atta
|
||||||
if (attachedToSide) {
|
if (attachedToSide) {
|
||||||
if (isHorizontalOrientation(attachOrientation)) {
|
if (isHorizontalOrientation(attachOrientation)) {
|
||||||
|
|
||||||
// orientation is 'right' or 'left'
|
// orientation is right or left
|
||||||
|
|
||||||
// opposite horizontal orientation or same orientation
|
// opposite horizontal orientation or same orientation
|
||||||
if (
|
if (
|
||||||
|
@ -385,7 +357,7 @@ function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, atta
|
||||||
return 'v';
|
return 'v';
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// orientation is 'top' or 'bottom'
|
// orientation is top or bottom
|
||||||
|
|
||||||
// opposite vertical orientation or same orientation
|
// opposite vertical orientation or same orientation
|
||||||
if (
|
if (
|
||||||
|
@ -402,8 +374,8 @@ function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, atta
|
||||||
|
|
||||||
// attached to either top-right, top-left, bottom-right or bottom-left corner
|
// attached to either top-right, top-left, bottom-right or bottom-left corner
|
||||||
|
|
||||||
// orientation is 'right', 'left'
|
// orientation is right, left
|
||||||
// or same vertical orientation but also 'right' or 'left'
|
// or same vertical orientation but also right or left
|
||||||
if (isHorizontalOrientation(targetOrientation) ||
|
if (isHorizontalOrientation(targetOrientation) ||
|
||||||
(isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) &&
|
(isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) &&
|
||||||
getHorizontalOrientation(targetOrientation))) {
|
getHorizontalOrientation(targetOrientation))) {
|
||||||
|
|
Loading…
Reference in New Issue