chore(connect-snapping): snap correctly

This commit is contained in:
Philipp Fromme 2019-11-05 12:47:11 +01:00 committed by Nico Rehwaldt
parent ef5a72d722
commit bdc3b70b2a
1 changed files with 70 additions and 61 deletions

View File

@ -11,22 +11,19 @@ import {
import { is } from '../../util/ModelUtil'; import { is } from '../../util/ModelUtil';
import {
some
} from 'min-dash';
import { isAny } from '../modeling/util/ModelingUtil'; import { isAny } from '../modeling/util/ModelingUtil';
import { some } from 'min-dash';
var HIGHER_PRIORITY = 1250; var HIGHER_PRIORITY = 1250;
var BOUNDARY_TO_HOST_THRESHOLD = 40; var BOUNDARY_TO_HOST_THRESHOLD = 40;
var TARGET_BOUNDS_PADDING = 20; var TARGET_BOUNDS_PADDING = 20,
TASK_BOUNDS_PADDING = 10;
var TARGET_CENTER_PADDING = 20; var TARGET_CENTER_PADDING = 20;
var TASK_BOUNDS_PADDING = 10;
var AXES = [ 'x', 'y' ]; var AXES = [ 'x', 'y' ];
var abs = Math.abs; var abs = Math.abs;
@ -44,66 +41,67 @@ export default function BpmnConnectSnapping(eventBus, rules) {
'connect.end', 'connect.end',
], HIGHER_PRIORITY, function(event) { ], HIGHER_PRIORITY, function(event) {
var context = event.context, var context = event.context,
canExecute = context.canExecute,
start = context.start,
hover = context.hover,
source = context.source, source = context.source,
target = context.target; target = context.target;
// do NOT snap on CMD
if (event.originalEvent && isCmd(event.originalEvent)) { if (event.originalEvent && isCmd(event.originalEvent)) {
return; return;
} }
if (!context.initialSourcePosition) { if (!context.initialConnectionStart) {
context.initialSourcePosition = context.sourcePosition; context.initialConnectionStart = context.connectionStart;
} }
var connectionAttrs = rules.allowed('connection.create', { // snap hover
source: source, if (canExecute && hover) {
target: target snapToShape(event, hover, getTargetBoundsPadding(hover));
});
if (target && connectionAttrs) {
snapInsideTarget(event, target, getTargetBoundsPadding(target));
} }
if (target && isAnyType(connectionAttrs, [ if (hover && isAnyType(canExecute, [
'bpmn:Association', 'bpmn:Association',
'bpmn:DataInputAssociation', 'bpmn:DataInputAssociation',
'bpmn:DataOutputAssociation', 'bpmn:DataOutputAssociation',
'bpmn:SequenceFlow' 'bpmn:SequenceFlow'
])) { ])) {
context.connectionStart = mid(start);
// snap source // snap hover
context.sourcePosition = mid(source); if (isAny(hover, [ 'bpmn:Event', 'bpmn:Gateway' ])) {
snapToPosition(event, mid(hover));
if (isAny(target, [ 'bpmn:Event', 'bpmn:Gateway' ])) {
snapToPosition(event, mid(target));
} }
if (isAny(target, [ 'bpmn:Task', 'bpmn:SubProcess' ])) { // snap hover
snapToTaskMid(event, target); if (isAny(hover, [ 'bpmn:Task', 'bpmn:SubProcess' ])) {
snapToTargetMid(event, hover);
} }
// snap source and target
if (is(source, 'bpmn:BoundaryEvent') && target === source.host) { if (is(source, 'bpmn:BoundaryEvent') && target === source.host) {
snapBoundaryEventLoop(event, source, target); snapBoundaryEventLoop(event);
} }
} else if (isType(connectionAttrs, 'bpmn:MessageFlow')) { } else if (isType(canExecute, 'bpmn:MessageFlow')) {
if (is(source, 'bpmn:Event')) { if (is(start, 'bpmn:Event')) {
// snap source // snap start
context.sourcePosition = mid(source); context.connectionStart = mid(start);
} }
if (is(target, 'bpmn:Event')) { if (is(hover, 'bpmn:Event')) {
// snap target // snap hover
snapToPosition(event, mid(target)); snapToPosition(event, mid(hover));
} }
} else { } else {
// un-snap source // un-snap source
context.sourcePosition = context.initialSourcePosition; context.connectionStart = context.initialConnectionStart;
} }
}); });
} }
@ -113,58 +111,64 @@ BpmnConnectSnapping.$inject = [
'rules' 'rules'
]; ];
function snapInsideTarget(event, target, padding) {
// helpers //////////
// snap to target if event in target
function snapToShape(event, target, padding) {
AXES.forEach(function(axis) { AXES.forEach(function(axis) {
var matchingTargetDimension = getDimensionForAxis(axis, target), var dimensionForAxis = getDimensionForAxis(axis, target);
newCoordinate;
if (event[axis] < target[axis] + padding) { if (event[ axis ] < target[ axis ] + padding) {
newCoordinate = target[axis] + padding; setSnapped(event, axis, target[ axis ] + padding);
} else if (event[axis] > target[axis] + matchingTargetDimension - padding) { } else if (event[ axis ] > target[ axis ] + dimensionForAxis - padding) {
newCoordinate = target[axis] + matchingTargetDimension - padding; setSnapped(event, axis, target[ axis ] + dimensionForAxis - padding);
}
if (newCoordinate) {
setSnapped(event, axis, newCoordinate);
} }
}); });
} }
// snap to target mid if event in center // snap to target mid if event in target mid
function snapToTaskMid(event, target) { function snapToTargetMid(event, target) {
var targetMid = mid(target); var targetMid = mid(target);
AXES.forEach(function(axis) { AXES.forEach(function(axis) {
if (isCenter(event, target, axis)) { if (isMid(event, target, axis)) {
setSnapped(event, axis, targetMid[ axis ]); setSnapped(event, axis, targetMid[ axis ]);
} }
}); });
} }
// snap outside of Boundary Event surroundings // snap to prevent loop overlapping boundary event
function snapBoundaryEventLoop(event, source, target) { function snapBoundaryEventLoop(event) {
var context = event.context,
source = context.source,
target = context.target;
if (isReverse(context)) {
return;
}
var sourceMid = mid(source), var sourceMid = mid(source),
orientation = getOrientation(sourceMid, target, -10), orientation = getOrientation(sourceMid, target, -10),
snappingAxes = []; axes = [];
if (/top|bottom/.test(orientation)) { if (/top|bottom/.test(orientation)) {
snappingAxes.push('x'); axes.push('x');
} }
if (/left|right/.test(orientation)) { if (/left|right/.test(orientation)) {
snappingAxes.push('y'); axes.push('y');
} }
snappingAxes.forEach(function(axis) { axes.forEach(function(axis) {
var coordinate = event[axis], newCoordinate; var coordinate = event[ axis ], newCoordinate;
if (abs(coordinate - sourceMid[axis]) < BOUNDARY_TO_HOST_THRESHOLD) { if (abs(coordinate - sourceMid[ axis ]) < BOUNDARY_TO_HOST_THRESHOLD) {
if (coordinate > sourceMid[axis]) { if (coordinate > sourceMid[ axis ]) {
newCoordinate = sourceMid[axis] + BOUNDARY_TO_HOST_THRESHOLD; newCoordinate = sourceMid[ axis ] + BOUNDARY_TO_HOST_THRESHOLD;
} }
else { else {
newCoordinate = sourceMid[axis] - BOUNDARY_TO_HOST_THRESHOLD; newCoordinate = sourceMid[ axis ] - BOUNDARY_TO_HOST_THRESHOLD;
} }
setSnapped(event, axis, newCoordinate); setSnapped(event, axis, newCoordinate);
@ -172,8 +176,6 @@ function snapBoundaryEventLoop(event, source, target) {
}); });
} }
// helpers //////////
function snapToPosition(event, position) { function snapToPosition(event, position) {
setSnapped(event, 'x', position.x); setSnapped(event, 'x', position.x);
setSnapped(event, 'y', position.y); setSnapped(event, 'y', position.y);
@ -201,7 +203,14 @@ function getTargetBoundsPadding(target) {
} }
} }
function isCenter(event, target, axis) { function isMid(event, target, axis) {
return event[ axis ] > target[ axis ] + TARGET_CENTER_PADDING return event[ axis ] > target[ axis ] + TARGET_CENTER_PADDING
&& event[ axis ] < target[ axis ] + getDimensionForAxis(axis, target) - TARGET_CENTER_PADDING; && event[ axis ] < target[ axis ] + getDimensionForAxis(axis, target) - TARGET_CENTER_PADDING;
} }
function isReverse(context) {
var hover = context.hover,
source = context.source;
return hover && source && hover === source;
}