bpmn-js/lib/features/modeling/BpmnLayouter.js

213 lines
4.7 KiB
JavaScript

import inherits from 'inherits';
import {
assign
} from 'min-dash';
import BaseLayouter from 'diagram-js/lib/layout/BaseLayouter';
import {
repairConnection
} from 'diagram-js/lib/layout/ManhattanLayout';
import {
getMid,
getOrientation
} from 'diagram-js/lib/layout/LayoutUtil';
import {
pointsOnLine
} from 'diagram-js/lib/util/Geometry';
import {
isExpanded
} from '../../util/DiUtil';
import { is } from '../../util/ModelUtil';
export default function BpmnLayouter() {}
inherits(BpmnLayouter, BaseLayouter);
BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
hints = hints || {};
var source = connection.source,
target = connection.target,
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(connection)) {
return [].concat([ start ], waypoints.slice(1, -1), [ end ]);
}
}
// manhattan layout sequence / message flows
if (is(connection, 'bpmn:MessageFlow')) {
manhattanOptions = {
preferredLayouts: [ 'v:v' ]
};
if (is(target, 'bpmn:Participant')) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ]
};
}
if (isExpandedSubProcess(target)) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ]
};
}
if (isExpandedSubProcess(source) && is(target, 'bpmn:FlowNode')) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ],
preserveDocking: isExpandedSubProcess(target) ? 'source' : 'target'
};
}
if (is(source, 'bpmn:Participant') && is(target, 'bpmn:FlowNode')) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ],
preserveDocking: 'target'
};
}
if (is(target, 'bpmn:Event')) {
manhattanOptions = {
preferredLayouts: [ 'v:v' ]
};
}
} else
// layout all connection between flow elements h:h,
//
// except for
//
// (1) outgoing of BoundaryEvents -> layout h:v or v:h based on attach orientation
// (2) incoming / outgoing of Gateway -> v:h (outgoing), h:v (incoming)
//
if (is(connection, 'bpmn:SequenceFlow') ||
isCompensationAssociation(connection)) {
// make sure boundary event connections do
// not look ugly =:>
if (is(source, 'bpmn:BoundaryEvent')) {
var orientation = getAttachOrientation(source);
if (/left|right/.test(orientation)) {
manhattanOptions = {
preferredLayouts: [ 'h:v' ]
};
} else
if (/top|bottom/.test(orientation)) {
manhattanOptions = {
preferredLayouts: [ 'v:h' ]
};
}
} else
if (is(source, 'bpmn:Gateway')) {
manhattanOptions = {
preferredLayouts: [ 'v:h' ]
};
} else
if (is(target, 'bpmn:Gateway')) {
manhattanOptions = {
preferredLayouts: [ 'h:v' ]
};
}
// apply horizontal love <3
else {
manhattanOptions = {
preferredLayouts: [ 'h:h' ]
};
}
}
if (manhattanOptions) {
manhattanOptions = assign(manhattanOptions, hints);
updatedWaypoints =
repairConnection(
source, target,
start, end,
waypoints,
manhattanOptions);
// filter un-needed waypoints that may be the result of
// bundle collapsing
updatedWaypoints = updatedWaypoints && updatedWaypoints.reduce(function(points, p, idx) {
var previous = points[points.length - 1],
next = updatedWaypoints[idx + 1];
if (!pointsOnLine(previous, next, p, 0)) {
points.push(p);
}
return points;
}, []);
}
return updatedWaypoints || [ start, end ];
};
function getAttachOrientation(attachedElement) {
var hostElement = attachedElement.host,
padding = -10;
return getOrientation(getMid(attachedElement), hostElement, padding);
}
function getConnectionDocking(point, shape) {
return point ? (point.original || point) : getMid(shape);
}
function isCompensationAssociation(connection) {
var source = connection.source,
target = connection.target;
return is(target, 'bpmn:Activity') &&
is(source, 'bpmn:BoundaryEvent') &&
target.businessObject.isForCompensation;
}
function isExpandedSubProcess(element) {
return is(element, 'bpmn:SubProcess') && isExpanded(element);
}