chore(layouter): refactor

This commit is contained in:
Philipp Fromme 2019-11-05 12:47:36 +01:00 committed by Nico Rehwaldt
parent 5c5656aceb
commit 0ed0492641
1 changed files with 172 additions and 200 deletions

View File

@ -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))) {