feat(modeling): move labels with connections
* move on segment dragging * move on reconnect * move on bendpoint add / remove Closes #331
This commit is contained in:
parent
713021ecff
commit
fd198b6059
|
@ -4,12 +4,14 @@ var assign = require('lodash/object/assign'),
|
||||||
inherits = require('inherits');
|
inherits = require('inherits');
|
||||||
|
|
||||||
var LabelUtil = require('../../../util/LabelUtil'),
|
var LabelUtil = require('../../../util/LabelUtil'),
|
||||||
|
LabelLayoutUtil = require('./util/LabelLayoutUtil'),
|
||||||
ModelUtil = require('../../../util/ModelUtil'),
|
ModelUtil = require('../../../util/ModelUtil'),
|
||||||
is = ModelUtil.is,
|
is = ModelUtil.is,
|
||||||
getBusinessObject = ModelUtil.getBusinessObject;
|
getBusinessObject = ModelUtil.getBusinessObject;
|
||||||
|
|
||||||
var hasExternalLabel = LabelUtil.hasExternalLabel,
|
var hasExternalLabel = LabelUtil.hasExternalLabel,
|
||||||
getExternalLabelMid = LabelUtil.getExternalLabelMid;
|
getExternalLabelMid = LabelUtil.getExternalLabelMid,
|
||||||
|
getLabelAdjustment = LabelLayoutUtil.getLabelAdjustment;
|
||||||
|
|
||||||
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
|
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
|
||||||
|
|
||||||
|
@ -70,6 +72,55 @@ function LabelSupport(eventBus, modeling, bpmnFactory) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.postExecute([
|
||||||
|
'connection.layout',
|
||||||
|
'connection.reconnectEnd',
|
||||||
|
'connection.reconnectStart'
|
||||||
|
], function(e) {
|
||||||
|
|
||||||
|
var context = e.context,
|
||||||
|
oldWaypoints = context.oldWaypoints,
|
||||||
|
newWaypoints = context.newWaypoints || context.connection.waypoints,
|
||||||
|
label = context.connection.label,
|
||||||
|
hints = context.hints || {},
|
||||||
|
delta;
|
||||||
|
|
||||||
|
if (e.command != 'connection.layout') {
|
||||||
|
hints.startChanged = e.command == 'connection.reconnectStart' ? true : false;
|
||||||
|
hints.endChanged = e.command == 'connection.reconnectEnd' ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = getLabelAdjustment(label, newWaypoints, oldWaypoints, hints);
|
||||||
|
|
||||||
|
modeling.moveShape(label, delta);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.postExecute([
|
||||||
|
'connection.updateWaypoints'
|
||||||
|
], function(e) {
|
||||||
|
|
||||||
|
var context = e.context,
|
||||||
|
oldWaypoints = context.oldWaypoints,
|
||||||
|
newWaypoints = context.newWaypoints,
|
||||||
|
label = context.connection.label,
|
||||||
|
hints = context.hints || {},
|
||||||
|
delta;
|
||||||
|
|
||||||
|
if (!label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = getLabelAdjustment(label, newWaypoints, oldWaypoints, hints);
|
||||||
|
|
||||||
|
modeling.moveShape(label, delta);
|
||||||
|
});
|
||||||
|
|
||||||
this.postExecute([ 'shape.replace' ], function(e) {
|
this.postExecute([ 'shape.replace' ], function(e) {
|
||||||
var context = e.context,
|
var context = e.context,
|
||||||
newShape = context.newShape,
|
newShape = context.newShape,
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of a vector
|
||||||
|
*
|
||||||
|
* @param {Vector}
|
||||||
|
* @return {Float}
|
||||||
|
*/
|
||||||
|
function vectorLength(v) {
|
||||||
|
return Math.sqrt( Math.pow(v.x, 2) + Math.pow(v.y, 2) );
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.vectorLength = vectorLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the angle between a line a the yAxis
|
||||||
|
*
|
||||||
|
* @param {Array}
|
||||||
|
* @return {Float}
|
||||||
|
*/
|
||||||
|
function getAngle(line) {
|
||||||
|
// return value is between 0, 180 and -180, -0
|
||||||
|
// @janstuemmel: maybe replace return a/b with b/a
|
||||||
|
return Math.atan( (line[1].y - line[0].y) / (line[1].x - line[0].x) );
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getAngle = getAngle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates a vector by a given angle
|
||||||
|
*
|
||||||
|
* @param {Vector}
|
||||||
|
* @param {Float} Angle in radians
|
||||||
|
* @return {Vector}
|
||||||
|
*/
|
||||||
|
function rotateVector(vector, angle) {
|
||||||
|
return (!angle) ? vector : {
|
||||||
|
x: Math.cos(angle) * vector.x - Math.sin(angle) * vector.y,
|
||||||
|
y: Math.sin(angle) * vector.x + Math.cos(angle) * vector.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.rotateVector = rotateVector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solves a 2D equation system
|
||||||
|
* a + r*b = c, where a,b,c are 2D vectors
|
||||||
|
*
|
||||||
|
* @param {Vector}
|
||||||
|
* @param {Vector}
|
||||||
|
* @param {Vector}
|
||||||
|
* @return {Float}
|
||||||
|
*/
|
||||||
|
function solveLambaSystem(a, b, c) {
|
||||||
|
|
||||||
|
// the 2d system
|
||||||
|
var system = [
|
||||||
|
{ n: a[0] - c[0], lambda: b[0] },
|
||||||
|
{ n: a[1] - c[1], lambda: b[1] }
|
||||||
|
];
|
||||||
|
|
||||||
|
// solve
|
||||||
|
var n = system[0].n * b[0] + system[1].n * b[1],
|
||||||
|
l = system[0].lambda * b[0] + system[1].lambda * b[1];
|
||||||
|
|
||||||
|
return -n/l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Position of perpendicular foot
|
||||||
|
*
|
||||||
|
* @param {Point}
|
||||||
|
* @param [ {Point}, {Point} ] line defined throug two points
|
||||||
|
* @return {Point} the perpendicular foot position
|
||||||
|
*/
|
||||||
|
function perpendicularFoot(point, line) {
|
||||||
|
|
||||||
|
var a = line[0], b = line[1];
|
||||||
|
|
||||||
|
// relative position of b from a
|
||||||
|
var bd = { x: b.x - a.x, y: b.y - a.y };
|
||||||
|
|
||||||
|
// solve equation system to the parametrized vectors param real value
|
||||||
|
var r = solveLambaSystem( [ a.x, a.y ], [ bd.x, bd.y ], [ point.x, point.y ] );
|
||||||
|
|
||||||
|
return { x: a.x + r*bd.x, y: a.y + r*bd.y };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.perpendicularFoot = perpendicularFoot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the distance between a point and a line
|
||||||
|
*
|
||||||
|
* @param {Point}
|
||||||
|
* @param [ {Point}, {Point} ] line defined throug two points
|
||||||
|
* @return {Float} distance
|
||||||
|
*/
|
||||||
|
function getDistancePointLine(point, line) {
|
||||||
|
|
||||||
|
var pfPoint = perpendicularFoot(point, line);
|
||||||
|
|
||||||
|
// distance vector
|
||||||
|
var connectionVector = {
|
||||||
|
x: pfPoint.x - point.x,
|
||||||
|
y: pfPoint.y - point.y
|
||||||
|
};
|
||||||
|
|
||||||
|
return vectorLength(connectionVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getDistancePointLine = getDistancePointLine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the distance between two points
|
||||||
|
*
|
||||||
|
* @param {Point}
|
||||||
|
* @param {Point}
|
||||||
|
* @return {Float} distance
|
||||||
|
*/
|
||||||
|
function getDistancePointPoint(point1, point2) {
|
||||||
|
|
||||||
|
return vectorLength({
|
||||||
|
x: point1.x - point2.x,
|
||||||
|
y: point1.y - point2.y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getDistancePointPoint = getDistancePointPoint;
|
|
@ -0,0 +1,216 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var GeometricUtil = require('./GeometricUtil');
|
||||||
|
|
||||||
|
var getDistance = require('./GeometricUtil').getDistancePointPoint;
|
||||||
|
|
||||||
|
var getAttachment = require('./LineAttachmentUtil').getAttachment;
|
||||||
|
|
||||||
|
|
||||||
|
function findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) {
|
||||||
|
|
||||||
|
var index = attachment.segmentIndex;
|
||||||
|
|
||||||
|
var offset = newWaypoints.length - oldWaypoints.length;
|
||||||
|
|
||||||
|
// segmentMove happend
|
||||||
|
if (hints.segmentMove) {
|
||||||
|
|
||||||
|
var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex,
|
||||||
|
newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex;
|
||||||
|
|
||||||
|
// if label was on moved segment return new segment index
|
||||||
|
if (index === oldSegmentStartIndex) {
|
||||||
|
return newSegmentStartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// label is after new segment index
|
||||||
|
if (index >= newSegmentStartIndex) {
|
||||||
|
return (index+offset < newSegmentStartIndex) ? newSegmentStartIndex : index+offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if label is before new segment index
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bendpointMove happend
|
||||||
|
if (hints.bendpointMove) {
|
||||||
|
|
||||||
|
var insert = hints.bendpointMove.insert,
|
||||||
|
bendpointIndex = hints.bendpointMove.bendpointIndex,
|
||||||
|
newIndex;
|
||||||
|
|
||||||
|
// waypoints length didnt change
|
||||||
|
if (offset === 0) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// label behind new/removed bendpoint
|
||||||
|
if (index >= bendpointIndex) {
|
||||||
|
newIndex = insert ? index + 1 : index - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// label before new/removed bendpoint
|
||||||
|
if (index < bendpointIndex) {
|
||||||
|
|
||||||
|
newIndex = index;
|
||||||
|
|
||||||
|
// decide label should take right or left segment
|
||||||
|
if (insert && attachment.type !== 'bendpoint' && bendpointIndex-1 === index) {
|
||||||
|
|
||||||
|
var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex);
|
||||||
|
|
||||||
|
if (rel < attachment.relativeLocation) {
|
||||||
|
newIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start/end changed
|
||||||
|
if (offset === 0) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hints.connectionStart) {
|
||||||
|
return (index === 0) ? 0 : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hints.connectionEnd) {
|
||||||
|
return (index === oldWaypoints.length - 2) ? newWaypoints.length - 2 : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if nothing fits, return null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.findNewLabelLineStartIndex = findNewLabelLineStartIndex;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the required adjustment (move delta) for the given label
|
||||||
|
* after the connection waypoints got updated.
|
||||||
|
*
|
||||||
|
* @param {djs.model.Label} label
|
||||||
|
* @param {Array<Point>} newWaypoints
|
||||||
|
* @param {Array<Point>} oldWaypoints
|
||||||
|
* @param {Object} hints
|
||||||
|
*
|
||||||
|
* @return {Point} delta
|
||||||
|
*/
|
||||||
|
function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) {
|
||||||
|
|
||||||
|
var x = 0,
|
||||||
|
y = 0;
|
||||||
|
|
||||||
|
var labelPosition = getLabelMid(label);
|
||||||
|
|
||||||
|
// get closest attachment
|
||||||
|
var attachment = getAttachment(labelPosition, oldWaypoints),
|
||||||
|
oldLabelLineIndex = attachment.segmentIndex,
|
||||||
|
newLabelLineIndex = findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints);
|
||||||
|
|
||||||
|
if ( newLabelLineIndex === null ) {
|
||||||
|
return { x: x, y: y };
|
||||||
|
}
|
||||||
|
|
||||||
|
// should never happen
|
||||||
|
// TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored
|
||||||
|
if (newLabelLineIndex < 0 ||
|
||||||
|
newLabelLineIndex > newWaypoints.length - 2) {
|
||||||
|
return { x: x, y: y };
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex),
|
||||||
|
newLabelLine = getLine(newWaypoints, newLabelLineIndex),
|
||||||
|
oldFoot = attachment.position;
|
||||||
|
|
||||||
|
var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot),
|
||||||
|
angleDelta = getAngleDelta(oldLabelLine, newLabelLine);
|
||||||
|
|
||||||
|
// special rule if label on bendpoint
|
||||||
|
if (attachment.type === 'bendpoint') {
|
||||||
|
|
||||||
|
var offset = newWaypoints.length - oldWaypoints.length,
|
||||||
|
oldBendpointIndex = attachment.bendpointIndex,
|
||||||
|
oldBendpoint = oldWaypoints[oldBendpointIndex];
|
||||||
|
|
||||||
|
// bendpoint position hasnt changed, return same position
|
||||||
|
if (newWaypoints.indexOf(oldBendpoint) !== -1) {
|
||||||
|
return { x: x, y: y };
|
||||||
|
}
|
||||||
|
|
||||||
|
// new bendpoint and old bendpoint have same index, then just return the offset
|
||||||
|
if (offset === 0) {
|
||||||
|
var newBendpoint = newWaypoints[oldBendpointIndex];
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: newBendpoint.x - attachment.position.x,
|
||||||
|
y: newBendpoint.y - attachment.position.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if bendpoints get removed
|
||||||
|
if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) {
|
||||||
|
relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newFoot = {
|
||||||
|
x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x,
|
||||||
|
y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y
|
||||||
|
};
|
||||||
|
|
||||||
|
// the rotated vector to label
|
||||||
|
var newLabelVector = GeometricUtil.rotateVector({
|
||||||
|
x: labelPosition.x - oldFoot.x,
|
||||||
|
y: labelPosition.y - oldFoot.y
|
||||||
|
}, angleDelta);
|
||||||
|
|
||||||
|
// the new relative position
|
||||||
|
x = newFoot.x + newLabelVector.x - labelPosition.x;
|
||||||
|
y = newFoot.y + newLabelVector.y - labelPosition.y;
|
||||||
|
|
||||||
|
return { x: x, y: y };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getLabelAdjustment = getLabelAdjustment;
|
||||||
|
|
||||||
|
|
||||||
|
//// HELPERS ///////
|
||||||
|
|
||||||
|
function relativePositionMidWaypoint(waypoints, idx) {
|
||||||
|
|
||||||
|
var distanceSegment1 = getDistance(waypoints[idx-1], waypoints[idx]),
|
||||||
|
distanceSegment2 = getDistance(waypoints[idx], waypoints[idx+1]);
|
||||||
|
|
||||||
|
var relativePosition = distanceSegment1 / ( distanceSegment1 + distanceSegment2 );
|
||||||
|
|
||||||
|
return relativePosition;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLabelMid(label) {
|
||||||
|
return {
|
||||||
|
x: label.x + label.width / 2,
|
||||||
|
y: label.y + label.height / 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAngleDelta(l1, l2) {
|
||||||
|
var a1 = GeometricUtil.getAngle(l1),
|
||||||
|
a2 = GeometricUtil.getAngle(l2);
|
||||||
|
return a2 - a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLine(waypoints, idx) {
|
||||||
|
return [ waypoints[idx], waypoints[idx+1] ];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRelativeFootPosition(line, foot) {
|
||||||
|
var length = GeometricUtil.getDistancePointPoint(line[0], line[1]),
|
||||||
|
lengthToFoot = GeometricUtil.getDistancePointPoint(line[0], foot);
|
||||||
|
return lengthToFoot / length;
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var sqrt = Math.sqrt,
|
||||||
|
min = Math.min,
|
||||||
|
max = Math.max;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the square (power to two) of a number.
|
||||||
|
*
|
||||||
|
* @param {Number} n
|
||||||
|
*
|
||||||
|
* @return {Number}
|
||||||
|
*/
|
||||||
|
function sq(n) {
|
||||||
|
return Math.pow(n, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get distance between two points.
|
||||||
|
*
|
||||||
|
* @param {Point} p1
|
||||||
|
* @param {Point} p2
|
||||||
|
*
|
||||||
|
* @return {Number}
|
||||||
|
*/
|
||||||
|
function getDistance(p1, p2) {
|
||||||
|
return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the attachment of the given point on the specified line.
|
||||||
|
*
|
||||||
|
* The attachment is either a bendpoint (attached to the given point)
|
||||||
|
* or segment (attached to a location on a line segment) attachment:
|
||||||
|
*
|
||||||
|
* ```javascript
|
||||||
|
* var pointAttachment = {
|
||||||
|
* type: 'bendpoint',
|
||||||
|
* bendpointIndex: 3,
|
||||||
|
* position: { x: 10, y: 10 } // the attach point on the line
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* var segmentAttachment = {
|
||||||
|
* type: 'segment',
|
||||||
|
* segmentIndex: 2,
|
||||||
|
* relativeLocation: 0.31, // attach point location between 0 (at start) and 1 (at end)
|
||||||
|
* position: { x: 10, y: 10 } // the attach point on the line
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {Point} point
|
||||||
|
* @param {Array<Point>} line
|
||||||
|
*
|
||||||
|
* @return {Object} attachment
|
||||||
|
*/
|
||||||
|
function getAttachment(point, line) {
|
||||||
|
|
||||||
|
var idx = 0,
|
||||||
|
segmentStart,
|
||||||
|
segmentEnd,
|
||||||
|
segmentStartDistance,
|
||||||
|
segmentEndDistance,
|
||||||
|
attachmentPosition,
|
||||||
|
minDistance,
|
||||||
|
intersections,
|
||||||
|
attachment,
|
||||||
|
attachmentDistance,
|
||||||
|
closestAttachmentDistance,
|
||||||
|
closestAttachment;
|
||||||
|
|
||||||
|
for (idx = 0; idx < line.length - 1; idx++) {
|
||||||
|
|
||||||
|
segmentStart = line[idx];
|
||||||
|
segmentEnd = line[idx + 1];
|
||||||
|
|
||||||
|
if (pointsEqual(segmentStart, segmentEnd)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentStartDistance = getDistance(point, segmentStart);
|
||||||
|
segmentEndDistance = getDistance(point, segmentEnd);
|
||||||
|
|
||||||
|
minDistance = min(segmentStartDistance, segmentEndDistance);
|
||||||
|
|
||||||
|
intersections = getCircleSegmentIntersections(segmentStart, segmentEnd, point, minDistance);
|
||||||
|
|
||||||
|
if (intersections.length < 1) {
|
||||||
|
throw new Error('expected between [1, 2] circle -> line intersections');
|
||||||
|
}
|
||||||
|
|
||||||
|
// one intersection -> bendpoint attachment
|
||||||
|
if (intersections.length === 1) {
|
||||||
|
attachment = {
|
||||||
|
type: 'bendpoint',
|
||||||
|
position: intersections[0],
|
||||||
|
segmentIndex: idx,
|
||||||
|
bendpointIndex: pointsEqual(segmentStart, intersections[0]) ? idx : idx + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// two intersections -> segment attachment
|
||||||
|
if (intersections.length === 2) {
|
||||||
|
|
||||||
|
attachmentPosition = mid(intersections[0], intersections[1]);
|
||||||
|
|
||||||
|
attachment = {
|
||||||
|
type: 'segment',
|
||||||
|
position: attachmentPosition,
|
||||||
|
segmentIndex: idx,
|
||||||
|
relativeLocation: getDistance(segmentStart, attachmentPosition) / getDistance(segmentStart, segmentEnd)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentDistance = getDistance(attachment.position, point);
|
||||||
|
|
||||||
|
if (!closestAttachment || closestAttachmentDistance > attachmentDistance) {
|
||||||
|
closestAttachment = attachment;
|
||||||
|
closestAttachmentDistance = attachmentDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getAttachment = getAttachment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the intersection between a circle and a line segment.
|
||||||
|
*
|
||||||
|
* @param {Point} s1 segment start
|
||||||
|
* @param {Point} s2 segment end
|
||||||
|
* @param {Point} cc circle center
|
||||||
|
* @param {Number} cr circle radius
|
||||||
|
*
|
||||||
|
* @return {Array<Point>} intersections
|
||||||
|
*/
|
||||||
|
function getCircleSegmentIntersections(s1, s2, cc, cr) {
|
||||||
|
|
||||||
|
var baX = s2.x - s1.x;
|
||||||
|
var baY = s2.y - s1.y;
|
||||||
|
var caX = cc.x - s1.x;
|
||||||
|
var caY = cc.y - s1.y;
|
||||||
|
|
||||||
|
var a = baX * baX + baY * baY;
|
||||||
|
var bBy2 = baX * caX + baY * caY;
|
||||||
|
var c = caX * caX + caY * caY - cr * cr;
|
||||||
|
|
||||||
|
var pBy2 = bBy2 / a;
|
||||||
|
var q = c / a;
|
||||||
|
|
||||||
|
var disc = pBy2 * pBy2 - q;
|
||||||
|
if (disc < 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// if disc == 0 ... dealt with later
|
||||||
|
var tmpSqrt = sqrt(disc);
|
||||||
|
var abScalingFactor1 = -pBy2 + tmpSqrt;
|
||||||
|
var abScalingFactor2 = -pBy2 - tmpSqrt;
|
||||||
|
|
||||||
|
var i1 = {
|
||||||
|
x: round(s1.x - baX * abScalingFactor1),
|
||||||
|
y: round(s1.y - baY * abScalingFactor1)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (disc === 0) { // abScalingFactor1 == abScalingFactor2
|
||||||
|
return [ i1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
var i2 = {
|
||||||
|
x: round(s1.x - baX * abScalingFactor2),
|
||||||
|
y: round(s1.y - baY * abScalingFactor2)
|
||||||
|
};
|
||||||
|
|
||||||
|
return [ i1, i2 ].filter(function(p) {
|
||||||
|
return isPointInSegment(p, s1, s2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isPointInSegment(p, segmentStart, segmentEnd) {
|
||||||
|
return (
|
||||||
|
fenced(p.x, segmentStart.x, segmentEnd.x) &&
|
||||||
|
fenced(p.y, segmentStart.y, segmentEnd.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fenced(n, rangeStart, rangeEnd) {
|
||||||
|
return min(rangeStart, rangeEnd) <= n && n <= max(rangeStart, rangeEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate mid of two points.
|
||||||
|
*
|
||||||
|
* @param {Point} p1
|
||||||
|
* @param {Point} p2
|
||||||
|
*
|
||||||
|
* @return {Point}
|
||||||
|
*/
|
||||||
|
function mid(p1, p2) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: (p1.x + p2.x) / 2,
|
||||||
|
y: (p1.y + p2.y) / 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function round(n) {
|
||||||
|
return Math.round(n * 1000) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointsEqual(p1, p2) {
|
||||||
|
return p1.x === p2.x && p1.y === p2.y;
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
|
||||||
|
<bpmn:process id="Process_1" isExecutable="false">
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>SequenceFlow_A</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:task id="Task_1" name="1">
|
||||||
|
<bpmn:incoming>SequenceFlow_A</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>SequenceFlow_B</bpmn:outgoing>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_A" name="A" sourceRef="StartEvent_1" targetRef="Task_1" />
|
||||||
|
<bpmn:task id="Task_3" name="3">
|
||||||
|
<bpmn:incoming>SequenceFlow_B</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>SequenceFlow_C</bpmn:outgoing>
|
||||||
|
<bpmn:outgoing>SequenceFlow_D</bpmn:outgoing>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_B" name="B" sourceRef="Task_1" targetRef="Task_3" />
|
||||||
|
<bpmn:task id="Task_2" name="2">
|
||||||
|
<bpmn:incoming>SequenceFlow_C</bpmn:incoming>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_C" name="C" sourceRef="Task_3" targetRef="Task_2" />
|
||||||
|
<bpmn:task id="Task_4" name="4">
|
||||||
|
<bpmn:incoming>SequenceFlow_D</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>SequenceFlow_E</bpmn:outgoing>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_D" name="D" sourceRef="Task_3" targetRef="Task_4" />
|
||||||
|
<bpmn:endEvent id="EndEvent_1">
|
||||||
|
<bpmn:incoming>SequenceFlow_E</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_E" name="E" sourceRef="Task_4" targetRef="EndEvent_1" />
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
||||||
|
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||||
|
<dc:Bounds x="173" y="102" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
|
||||||
|
<dc:Bounds x="307" y="80" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_A_di" bpmnElement="SequenceFlow_A">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="209" y="120" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="307" y="120" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="213" y="95" width="90" height="20" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="Task_3_di" bpmnElement="Task_3">
|
||||||
|
<dc:Bounds x="504" y="235" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_B_di" bpmnElement="SequenceFlow_B">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="407" y="120" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="455" y="120" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="455" y="275" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="504" y="275" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="425" y="175" width="90" height="20" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="Task_2_di" bpmnElement="Task_2">
|
||||||
|
<dc:Bounds x="294" y="260" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_C_di" bpmnElement="SequenceFlow_C">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="554" y="315" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="554" y="385" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="344" y="385" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="344" y="340" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="486" y="392" width="90" height="20" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="Task_4_di" bpmnElement="Task_4">
|
||||||
|
<dc:Bounds x="717" y="353" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_D_di" bpmnElement="SequenceFlow_D">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="602" y="243" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="716" y="166" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="661" y="393" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="717" y="393" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="626.5" y="201.5" width="90" height="20" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="EndEvent_0za5rat_di" bpmnElement="EndEvent_1">
|
||||||
|
<dc:Bounds x="886" y="175" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="859" y="211" width="90" height="20" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_1ni843l_di" bpmnElement="SequenceFlow_E">
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="817" y="393" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="904" y="393" />
|
||||||
|
<di:waypoint xsi:type="dc:Point" x="904" y="211" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="794" y="372.5" width="90" height="20" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
|
@ -0,0 +1,559 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../../../TestHelper');
|
||||||
|
|
||||||
|
/* global bootstrapModeler, inject */
|
||||||
|
|
||||||
|
|
||||||
|
var coreModule = require('lib/core'),
|
||||||
|
bendpointsModule = require('diagram-js/lib/features/bendpoints'),
|
||||||
|
modelingModule = require('lib/features/modeling'),
|
||||||
|
labelEditingModule = require('lib/features/label-editing');
|
||||||
|
|
||||||
|
var canvasEvent = require('../../../util/MockEvents').createCanvasEvent;
|
||||||
|
|
||||||
|
var testModules = [
|
||||||
|
coreModule,
|
||||||
|
modelingModule,
|
||||||
|
labelEditingModule,
|
||||||
|
bendpointsModule
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
describe('modeling - label layouting', function() {
|
||||||
|
|
||||||
|
describe('should create label', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('./LabelLayouting.initial.bpmn');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, {
|
||||||
|
modules: testModules
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('horizontal', inject(function(modeling, elementRegistry) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var element1 = elementRegistry.get('StartEvent_1'),
|
||||||
|
element2 = elementRegistry.get('ExclusiveGateway_2');
|
||||||
|
|
||||||
|
// when
|
||||||
|
var connection = modeling.connect(element1, element2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(connection.label.x).to.be.equal(427);
|
||||||
|
expect(connection.label.y).to.be.equal(332);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('vertical', inject(function(modeling, elementRegistry) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var element1 = elementRegistry.get('StartEvent_1'),
|
||||||
|
element2 = elementRegistry.get('ExclusiveGateway_1');
|
||||||
|
|
||||||
|
// when
|
||||||
|
var connection = modeling.connect(element1, element2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(connection.label.x).to.be.equal(292);
|
||||||
|
expect(connection.label.y).to.be.equal(219.5);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('should move label', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('./LabelLayouting.move.bpmn');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, {
|
||||||
|
modules: testModules
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('on segment move', function() {
|
||||||
|
|
||||||
|
it('left - no relayout', inject(function(elementRegistry, connectionSegmentMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_B'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: -30, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: -30, y: 0 });
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('left - remove bendpoint', inject(function(elementRegistry, connectionSegmentMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_B'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: -70, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: -70, y: 23 });
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('right - no relayout', inject(function(elementRegistry, connectionSegmentMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_B'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 30, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: 30, y: 0 });
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('right - remove bendpoint', inject(function(elementRegistry, connectionSegmentMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_B'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 70, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: 70, y: -17 });
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('down', inject(function(elementRegistry, connectionSegmentMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 0, y: 70 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: 0, y: 70 });
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('up - remove two bendpoints', inject(function(elementRegistry, connectionSegmentMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 0, y: -90 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: -39, y: -85 });
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
// TODO(@janstuemmel): solve by connectionSegmentMove refactoring
|
||||||
|
it.skip('up - remove two bendpoints - redundant waypoints', inject(function(elementRegistry, connectionSegmentMove, dragging, bendpointMove) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C');
|
||||||
|
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1);
|
||||||
|
dragging.move(canvasEvent({ x: 620, y: 435 }));
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
dragging.move(canvasEvent({ x: 300, y: 435 }));
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
var labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
dragging.move(canvasEvent({ x: 0, y: -160 }));
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(getLabelPosition(connection)).to.not.eql(labelPosition);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on reconnect', function() {
|
||||||
|
|
||||||
|
it('start', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_D'),
|
||||||
|
shape = elementRegistry.get('Task_1');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.reconnectStart(connection, shape, { x: 0, y: 0 });
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(528);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(137);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('end', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_A'),
|
||||||
|
shape = elementRegistry.get('Task_2');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.reconnectEnd(connection, shape, { x: 294, y: 270 });
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(215);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(184);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on shape move', function() {
|
||||||
|
|
||||||
|
it('down', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_E'),
|
||||||
|
shape = elementRegistry.get('Task_4'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.moveShape(shape, { x: 0, y: 100 });
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: 0, y: 100 });
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on bendpoint add/delete/moving', function() {
|
||||||
|
|
||||||
|
|
||||||
|
it('move, label on segment', inject(function(elementRegistry, bendpointMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_B');
|
||||||
|
|
||||||
|
// when
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 455 + 50, y: 120 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(425);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(170);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('move, label on bendpoint', inject(function(elementRegistry, bendpointMove, dragging, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C');
|
||||||
|
|
||||||
|
// label out of segments, on a bendpoint (idx=1)
|
||||||
|
modeling.moveShape(connection.label, { x: 40, y: 0 });
|
||||||
|
|
||||||
|
// when
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 455 + 50, y: 500 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(477);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(507);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('remove bendpoint when label on segment', inject(function(elementRegistry, bendpointMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_B');
|
||||||
|
|
||||||
|
// when
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 455, y: 120 + 160 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(380);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(190);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('add bendpoint, label on right segment', inject(function(elementRegistry, bendpointMove, dragging, canvas) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_A');
|
||||||
|
|
||||||
|
// when
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1, true);
|
||||||
|
|
||||||
|
dragging.hover({
|
||||||
|
element: connection,
|
||||||
|
gfx: canvas.getGraphics(connection)
|
||||||
|
});
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 220, y: 200 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(211);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(152);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('add bendpoint, label on left segment', inject(function(elementRegistry, bendpointMove, dragging, canvas) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_A');
|
||||||
|
|
||||||
|
// when
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1, true);
|
||||||
|
|
||||||
|
dragging.hover({
|
||||||
|
element: connection,
|
||||||
|
gfx: canvas.getGraphics(connection)
|
||||||
|
});
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 260, y: 200 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(197);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(147);
|
||||||
|
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('remove bendpoint when label on bendpoint', inject(function(elementRegistry, bendpointMove, dragging, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C');
|
||||||
|
|
||||||
|
// label out of segments, on a bendpoint
|
||||||
|
modeling.moveShape(connection.label, { x: 40, y: 0 });
|
||||||
|
|
||||||
|
// when
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 455, y: 320 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(connection.label.x)).to.be.equal(426);
|
||||||
|
expect(Math.round(connection.label.y)).to.be.equal(287);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('add benpoint, label on segment, should not move', inject(function(elementRegistry, bendpointMove, canvas, dragging, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C');
|
||||||
|
|
||||||
|
// label out of segments, on a bendpoint
|
||||||
|
modeling.moveShape(connection.label, { x: 40, y: -60 });
|
||||||
|
var position = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
bendpointMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2, true);
|
||||||
|
|
||||||
|
dragging.hover({
|
||||||
|
element: connection,
|
||||||
|
gfx: canvas.getGraphics(connection)
|
||||||
|
});
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 400, y: 350 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, position, { x: 0, y: 0 });
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('special cases', function() {
|
||||||
|
|
||||||
|
it('should behave properly, right after importing', inject(function(elementRegistry, connectionSegmentMove, dragging, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C'),
|
||||||
|
labelPosition = getLabelPosition(connection),
|
||||||
|
label = connection.label;
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 0, y: 70 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// move label
|
||||||
|
modeling.moveShape(label, { x: 40, y: -30 });
|
||||||
|
|
||||||
|
// drag again
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 1);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: -20, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: 20, y: 40 });
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should reposition on right segment', inject(function(elementRegistry, connectionSegmentMove, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_E'),
|
||||||
|
labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: -100, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: -45, y: -70 });
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
describe.skip('label out of bounds', function() {
|
||||||
|
|
||||||
|
|
||||||
|
it('should not move label that is out of bounds', inject(function(elementRegistry, connectionSegmentMove, dragging, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C');
|
||||||
|
|
||||||
|
// move shape away
|
||||||
|
modeling.moveShape(connection.label, { x: 0, y: 140 });
|
||||||
|
|
||||||
|
var labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 0, y: 30 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: 0, y: 0 });
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should not move label that is out of bounds in corner', inject(function(elementRegistry, connectionSegmentMove, dragging, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var connection = elementRegistry.get('SequenceFlow_C');
|
||||||
|
|
||||||
|
// move shape away
|
||||||
|
modeling.moveShape(connection.label, { x: 50, y: 0 });
|
||||||
|
|
||||||
|
var labelPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
// when
|
||||||
|
connectionSegmentMove.start(canvasEvent({ x: 0, y: 0 }), connection, 2);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 0, y: 30 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expectLabelMoved(connection, labelPosition, { x: 0, y: 0 });
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function getLabelPosition(connection) {
|
||||||
|
|
||||||
|
var label = connection.label;
|
||||||
|
|
||||||
|
var mid = {
|
||||||
|
x: label.x + (label.width / 2),
|
||||||
|
y: label.y + (label.height / 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function expectLabelMoved(connection, oldPosition, expectedDelta) {
|
||||||
|
|
||||||
|
var newPosition = getLabelPosition(connection);
|
||||||
|
|
||||||
|
var delta = {
|
||||||
|
x: Math.round(newPosition.x - oldPosition.x),
|
||||||
|
y: Math.round(newPosition.y - oldPosition.y)
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(delta).to.eql(expectedDelta);
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../../../../../TestHelper');
|
||||||
|
|
||||||
|
var GeometricUtil = require('lib/features/modeling/behavior/util/GeometricUtil');
|
||||||
|
|
||||||
|
|
||||||
|
describe('modeling/behavior/util - GeometricUtil', function() {
|
||||||
|
|
||||||
|
it('should calculate right horizontal-line/point distance', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var testData = [
|
||||||
|
{ point: { x: 2, y: 4 }, line: [ { x: 1, y: 1 }, { x: 4, y: 1 } ], distance: 3 },
|
||||||
|
{ point: { x: 2, y: 2 }, line: [ { x: 1, y: 1 }, { x: 1, y: 4 } ], distance: 1 },
|
||||||
|
{ point: { x: 0, y: 0 }, line: [ { x: 0, y: 4 }, { x: 4, y: 0 } ], distance: 3 }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i=0; i<testData.length; i++) {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var d = GeometricUtil.getDistancePointLine(testData[i].point, testData[i].line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(d)).to.be.equal(testData[i].distance);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should calculate right perpendicular foot', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var testData = [
|
||||||
|
{ point: { x: 2, y: 2 }, line: [ { x: 1, y: 1 }, { x: 1, y: 3 } ], foot: { x: 1, y: 2 } },
|
||||||
|
{ point: { x: 2, y: 4 }, line: [ { x: 1, y: 1 }, { x: 4, y: 1 } ], foot: { x: 2, y: 1 } }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i=0; i<testData.length; i++) {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var foot = GeometricUtil.perpendicularFoot(testData[i].point, testData[i].line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(rounded(foot)).to.be.eql(testData[i].foot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should calculate right distance', function() {
|
||||||
|
|
||||||
|
var testData = [
|
||||||
|
{ p1: { x: 1, y: 1 }, p2: { x: 5, y: 1 }, distance: 4 },
|
||||||
|
{ p1: { x: 1, y: 1 }, p2: { x: 1, y: 5 }, distance: 4 },
|
||||||
|
{ p1: { x: 1, y: 1 }, p2: { x: 5, y: 5 }, distance: 6 },
|
||||||
|
{ p1: { x: 1, y: 1 }, p2: { x: -5, y: -5 }, distance: 8 }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i=0; i<testData.length; i++) {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var d = GeometricUtil.getDistancePointPoint(testData[i].p1, testData[i].p2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(Math.round(d)).to.be.eql(testData[i].distance);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should calculate right line angle', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var testLines = [
|
||||||
|
{ line: [ { x: 0, y: 0 }, { x: 10, y: 10 } ], angle: 45 },
|
||||||
|
{ line: [ { x: 0, y: 0 }, { x: 0, y: 10 } ], angle: 90 },
|
||||||
|
{ line: [ { x: 0, y: 0 }, { x: -10, y: 10 } ], angle: -45 },
|
||||||
|
{ line: [ { x: 0, y: 0 }, { x: 10, y: 0 } ], angle: 0 },
|
||||||
|
{ line: [ { x: 0, y: 0 }, { x: 0, y: -10 } ], angle: -90 },
|
||||||
|
{ line: [ { x: 0, y: 0 }, { x: -10, y: 0 } ], angle: 0 }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i=0; i<testLines.length; i++) {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var angle = GeometricUtil.getAngle(testLines[i].line);
|
||||||
|
|
||||||
|
// to degree
|
||||||
|
angle = angle * ( 180 / Math.PI );
|
||||||
|
|
||||||
|
//then
|
||||||
|
expect(angle).to.be.equal(testLines[i].angle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should rotate vector', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var testVectors = [
|
||||||
|
// x=-10 because we have system with flipped y axis
|
||||||
|
{ vector: { x: 0, y: 10 }, angle: 90, rotated: { x: -10, y: 0 } },
|
||||||
|
{ vector: { x: 10, y: 0 }, angle: 90, rotated: { x: 0, y: 10 } },
|
||||||
|
{ vector: { x: 10, y: 0 }, angle: 45, rotated: { x: 7, y: 7 } }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i=0; i<testVectors.length; i++) {
|
||||||
|
|
||||||
|
// degree to radian
|
||||||
|
var angle = testVectors[i].angle * ( Math.PI / 180 );
|
||||||
|
|
||||||
|
// when
|
||||||
|
var rotatedVector = GeometricUtil.rotateVector(testVectors[i].vector, angle);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(rounded(rotatedVector)).to.be.eql(testVectors[i].rotated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function rounded(v) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: Math.round(v.x),
|
||||||
|
y: Math.round(v.y)
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var getAttachment = require('lib/features/modeling/behavior/util/LineAttachmentUtil').getAttachment;
|
||||||
|
|
||||||
|
|
||||||
|
describe('modeling/behavior/util - LineAttachmentUtil#getAttachment', function() {
|
||||||
|
|
||||||
|
// test line
|
||||||
|
//
|
||||||
|
// *--*
|
||||||
|
// |
|
||||||
|
// *
|
||||||
|
// \
|
||||||
|
// *
|
||||||
|
//
|
||||||
|
var line = [
|
||||||
|
{ x: 10, y: 10 },
|
||||||
|
// -
|
||||||
|
{ x: 30, y: 10 },
|
||||||
|
// |
|
||||||
|
{ x: 30, y: 30 },
|
||||||
|
// \
|
||||||
|
{ x: 130, y: 130 }
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
describe('should recognize segment', function() {
|
||||||
|
|
||||||
|
it('horizontal', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 20, y: 5 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'segment',
|
||||||
|
position: { x: 20, y: 10 },
|
||||||
|
segmentIndex: 0,
|
||||||
|
relativeLocation: 0.5
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('horizontal (on line)', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 20, y: 10 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'segment',
|
||||||
|
position: { x: 20, y: 10 },
|
||||||
|
segmentIndex: 0,
|
||||||
|
relativeLocation: 0.5
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('vertical', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 40, y: 20 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'segment',
|
||||||
|
position: { x: 30, y: 20 },
|
||||||
|
segmentIndex: 1,
|
||||||
|
relativeLocation: 0.5
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('diagonal', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 30, y: 40 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'segment',
|
||||||
|
position: { x: 35, y: 35 },
|
||||||
|
segmentIndex: 2,
|
||||||
|
relativeLocation: 0.05
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('diagonal (on line)', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 50, y: 50 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'segment',
|
||||||
|
position: { x: 50, y: 50 },
|
||||||
|
segmentIndex: 2,
|
||||||
|
relativeLocation: 0.2
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('horizontal (conflicting with vertical)', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 25, y: 15 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'segment',
|
||||||
|
position: { x: 25, y: 10 },
|
||||||
|
segmentIndex: 0,
|
||||||
|
relativeLocation: 0.75
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('should recognize bendpoint', function() {
|
||||||
|
|
||||||
|
it('horizontal', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 35, y: 5 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'bendpoint',
|
||||||
|
position: { x: 30, y: 10 },
|
||||||
|
bendpointIndex: 1,
|
||||||
|
segmentIndex: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('horizontal (segment start)', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 5, y: 5 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'bendpoint',
|
||||||
|
position: { x: 10, y: 10 },
|
||||||
|
bendpointIndex: 0,
|
||||||
|
segmentIndex: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('vertical', function() {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 35, y: 10 }, line);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'bendpoint',
|
||||||
|
position: { x: 30, y: 10 },
|
||||||
|
bendpointIndex: 1,
|
||||||
|
segmentIndex: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('vertical (segment start)', function() {
|
||||||
|
|
||||||
|
var otherLine = [
|
||||||
|
{ x: 10, y: 10 },
|
||||||
|
{ x: 10, y: 50 }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var attachment = getAttachment({ x: 5, y: 5 }, otherLine);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(attachment).to.eql({
|
||||||
|
type: 'bendpoint',
|
||||||
|
position: { x: 10, y: 10 },
|
||||||
|
bendpointIndex: 0,
|
||||||
|
segmentIndex: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -1,47 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
require('../../TestHelper');
|
|
||||||
|
|
||||||
/* global bootstrapModeler, inject */
|
|
||||||
|
|
||||||
var coreModule = require('../../../lib/core'),
|
|
||||||
modelingModule = require('../../../lib/features/modeling');
|
|
||||||
|
|
||||||
|
|
||||||
describe('LabelUtil', function() {
|
|
||||||
|
|
||||||
var diagramXML = require('./LabelUtil.bpmn');
|
|
||||||
|
|
||||||
beforeEach(bootstrapModeler(diagramXML, { modules: [ coreModule, modelingModule ] }));
|
|
||||||
|
|
||||||
|
|
||||||
it('should correctly place horizontal label', inject(function(modeling, elementRegistry) {
|
|
||||||
|
|
||||||
// given
|
|
||||||
var element1 = elementRegistry.get('StartEvent_1'),
|
|
||||||
element2 = elementRegistry.get('ExclusiveGateway_2');
|
|
||||||
|
|
||||||
// when
|
|
||||||
var connection = modeling.connect(element1, element2);
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(connection.label.x).to.be.equal(427);
|
|
||||||
expect(connection.label.y).to.be.equal(332);
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
it('should correctly place vertical label', inject(function(modeling, elementRegistry) {
|
|
||||||
|
|
||||||
// given
|
|
||||||
var element1 = elementRegistry.get('StartEvent_1'),
|
|
||||||
element2 = elementRegistry.get('ExclusiveGateway_1');
|
|
||||||
|
|
||||||
// when
|
|
||||||
var connection = modeling.connect(element1, element2);
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(connection.label.x).to.be.equal(292);
|
|
||||||
expect(connection.label.y).to.be.equal(219.5);
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
Loading…
Reference in New Issue