| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- import inherits from 'inherits-browser';
- import {
- assign
- } from 'min-dash';
- import BaseLayouter from 'diagram-js/lib/layout/BaseLayouter';
- import {
- repairConnection,
- withoutRedundantPoints
- } from 'diagram-js/lib/layout/ManhattanLayout';
- import {
- getMid,
- getOrientation
- } from 'diagram-js/lib/layout/LayoutUtil';
- import {
- isExpanded
- } from '../../util/DiUtil';
- import { is } from '../../util/ModelUtil';
- /**
- * @typedef {import('diagram-js/lib/util/Types').Point} Point
- *
- * @typedef {import('../../model/Types').Connection} Connection
- * @typedef {import('../../model/Types').Element} Element
- *
- * @typedef {import('diagram-js/lib/layout/BaseLayouter').LayoutConnectionHints} LayoutConnectionHints
- *
- * @typedef { {
- * source?: Element;
- * target?: Element;
- * waypoints?: Point[];
- * connectionStart?: Point;
- * connectionEnd?: Point;
- * } & LayoutConnectionHints } BpmnLayoutConnectionHints
- */
- var ATTACH_ORIENTATION_PADDING = -10,
- BOUNDARY_TO_HOST_THRESHOLD = 40;
- var oppositeOrientationMapping = {
- 'top': 'bottom',
- 'top-right': 'bottom-left',
- 'top-left': 'bottom-right',
- 'right': 'left',
- 'bottom': 'top',
- 'bottom-right': 'top-left',
- 'bottom-left': 'top-right',
- 'left': 'right'
- };
- var orientationDirectionMapping = {
- top: 't',
- right: 'r',
- bottom: 'b',
- left: 'l'
- };
- export default function BpmnLayouter() {}
- inherits(BpmnLayouter, BaseLayouter);
- /**
- * Returns waypoints of laid out connection.
- *
- * @param {Connection} connection
- * @param {BpmnLayoutConnectionHints} [hints]
- *
- * @return {Point[]}
- */
- 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 (isExpandedSubProcess(source) || isExpandedSubProcess(target)) {
- manhattanOptions = getSubProcessManhattanOptions(source);
- } 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 getSubProcessManhattanOptions(source) {
- return {
- preferredLayouts: [ 'straight', 'h:h' ],
- preserveDocking: getSubProcessPreserveDocking(source)
- };
- }
- function getSubProcessPreserveDocking(source) {
- return isExpandedSubProcess(source) ? 'target' : 'source';
- }
- 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) {
- var matches = /right|left/.exec(orientation);
- return matches && matches[0];
- }
- function getVerticalOrientation(orientation) {
- var matches = /top|bottom/.exec(orientation);
- return matches && matches[0];
- }
- function isOppositeOrientation(a, b) {
- return oppositeOrientationMapping[a] === b;
- }
- function isOppositeHorizontalOrientation(a, b) {
- var horizontalOrientation = getHorizontalOrientation(a);
- var oppositeHorizontalOrientation = oppositeOrientationMapping[horizontalOrientation];
- return b.indexOf(oppositeHorizontalOrientation) !== -1;
- }
- function isOppositeVerticalOrientation(a, b) {
- var verticalOrientation = getVerticalOrientation(a);
- var oppositeVerticalOrientation = oppositeOrientationMapping[verticalOrientation];
- return b.indexOf(oppositeVerticalOrientation) !== -1;
- }
- function isHorizontalOrientation(orientation) {
- return orientation === 'right' || orientation === 'left';
- }
- function getLoopPreferredLayout(source, connection) {
- var waypoints = connection.waypoints;
- var orientation = waypoints && waypoints.length && getOrientation(waypoints[0], source);
- if (orientation === 'top') {
- return [ 't:r' ];
- } else if (orientation === 'right') {
- return [ 'r:b' ];
- } else if (orientation === 'left') {
- return [ 'l:t' ];
- }
- return [ 'b:l' ];
- }
- function getBoundaryEventPreferredLayouts(source, target, end) {
- var sourceMid = getMid(source),
- targetMid = getMid(target),
- attachOrientation = getAttachOrientation(source),
- sourceLayout,
- targetLayout;
- var isLoop = isSame(source.host, target);
- var attachedToSide = isAnyOrientation(attachOrientation, [ 'top', 'right', 'bottom', 'left' ]);
- var targetOrientation = getOrientation(targetMid, sourceMid, {
- x: source.width / 2 + target.width / 2,
- y: source.height / 2 + target.height / 2
- });
- if (isLoop) {
- return getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end);
- }
- // source layout
- sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide);
- // target layout
- targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide);
- return [ sourceLayout + ':' + targetLayout ];
- }
- function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) {
- var orientation = attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation),
- sourceLayout = orientationDirectionMapping[ orientation ],
- 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) {
- return orientationDirectionMapping[ attachOrientation ];
- }
- // attached to either top-right, top-left, bottom-right or bottom-left corner
- // same vertical or opposite horizontal orientation
- if (isSame(
- getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)
- ) || isOppositeOrientation(
- getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
- )) {
- return orientationDirectionMapping[ getVerticalOrientation(attachOrientation) ];
- }
- // fallback
- return orientationDirectionMapping[ getHorizontalOrientation(attachOrientation) ];
- }
- function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) {
- // attached to either top, right, bottom or left side
- if (attachedToSide) {
- if (isHorizontalOrientation(attachOrientation)) {
- // orientation is right or left
- // opposite horizontal orientation or same orientation
- if (
- isOppositeHorizontalOrientation(attachOrientation, targetOrientation) ||
- isSame(attachOrientation, targetOrientation)
- ) {
- return 'h';
- }
- // fallback
- return 'v';
- } else {
- // orientation is top or bottom
- // opposite vertical orientation or same orientation
- if (
- isOppositeVerticalOrientation(attachOrientation, targetOrientation) ||
- isSame(attachOrientation, targetOrientation)
- ) {
- return 'v';
- }
- // fallback
- return 'h';
- }
- }
- // attached to either top-right, top-left, bottom-right or bottom-left corner
- // orientation is right, left
- // or same vertical orientation but also right or left
- if (isHorizontalOrientation(targetOrientation) ||
- (isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) &&
- getHorizontalOrientation(targetOrientation))) {
- return 'h';
- } else {
- return 'v';
- }
- }
|