123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- import {
- pointsAligned,
- pointsOnLine
- } from '../../util/Geometry';
- import {
- addSegmentDragger
- } from './BendpointUtil';
- import {
- getMid,
- getOrientation
- } from '../../layout/LayoutUtil';
- var MARKER_CONNECT_HOVER = 'connect-hover',
- MARKER_CONNECT_UPDATING = 'djs-updating';
- import {
- classes as svgClasses,
- remove as svgRemove
- } from 'tiny-svg';
- import {
- translate
- } from '../../util/SvgTransformUtil';
- function axisAdd(point, axis, delta) {
- return axisSet(point, axis, point[axis] + delta);
- }
- function axisSet(point, axis, value) {
- return {
- x: (axis === 'x' ? value : point.x),
- y: (axis === 'y' ? value : point.y)
- };
- }
- function axisFenced(position, segmentStart, segmentEnd, axis) {
- var maxValue = Math.max(segmentStart[axis], segmentEnd[axis]),
- minValue = Math.min(segmentStart[axis], segmentEnd[axis]);
- var padding = 20;
- var fencedValue = Math.min(Math.max(minValue + padding, position[axis]), maxValue - padding);
- return axisSet(segmentStart, axis, fencedValue);
- }
- function flipAxis(axis) {
- return axis === 'x' ? 'y' : 'x';
- }
- /**
- * Get the docking point on the given element.
- *
- * Compute a reasonable docking, if non exists.
- *
- * @param {Point} point
- * @param {djs.model.Shape} referenceElement
- * @param {String} moveAxis (x|y)
- *
- * @return {Point}
- */
- function getDocking(point, referenceElement, moveAxis) {
- var referenceMid,
- inverseAxis;
- if (point.original) {
- return point.original;
- } else {
- referenceMid = getMid(referenceElement);
- inverseAxis = flipAxis(moveAxis);
- return axisSet(point, inverseAxis, referenceMid[inverseAxis]);
- }
- }
- /**
- * A component that implements moving of bendpoints
- */
- export default function ConnectionSegmentMove(
- injector, eventBus, canvas,
- dragging, graphicsFactory, rules,
- modeling) {
- // optional connection docking integration
- var connectionDocking = injector.get('connectionDocking', false);
- // API
- this.start = function(event, connection, idx) {
- var context,
- gfx = canvas.getGraphics(connection),
- segmentStartIndex = idx - 1,
- segmentEndIndex = idx,
- waypoints = connection.waypoints,
- segmentStart = waypoints[segmentStartIndex],
- segmentEnd = waypoints[segmentEndIndex],
- direction,
- axis;
- direction = pointsAligned(segmentStart, segmentEnd);
- // do not move diagonal connection
- if (!direction) {
- return;
- }
- // the axis where we are going to move things
- axis = direction === 'v' ? 'y' : 'x';
- if (segmentStartIndex === 0) {
- segmentStart = getDocking(segmentStart, connection.source, axis);
- }
- if (segmentEndIndex === waypoints.length - 1) {
- segmentEnd = getDocking(segmentEnd, connection.target, axis);
- }
- context = {
- connection: connection,
- segmentStartIndex: segmentStartIndex,
- segmentEndIndex: segmentEndIndex,
- segmentStart: segmentStart,
- segmentEnd: segmentEnd,
- axis: axis
- };
- dragging.init(event, {
- x: (segmentStart.x + segmentEnd.x)/2,
- y: (segmentStart.y + segmentEnd.y)/2
- }, 'connectionSegment.move', {
- cursor: axis === 'x' ? 'resize-ew' : 'resize-ns',
- data: {
- connection: connection,
- connectionGfx: gfx,
- context: context
- }
- });
- };
- /**
- * Crop connection if connection cropping is provided.
- *
- * @param {Connection} connection
- * @param {Array<Point>} newWaypoints
- *
- * @return {Array<Point>} cropped connection waypoints
- */
- function cropConnection(connection, newWaypoints) {
- // crop connection, if docking service is provided only
- if (!connectionDocking) {
- return newWaypoints;
- }
- var oldWaypoints = connection.waypoints,
- croppedWaypoints;
- // temporary set new waypoints
- connection.waypoints = newWaypoints;
- croppedWaypoints = connectionDocking.getCroppedWaypoints(connection);
- // restore old waypoints
- connection.waypoints = oldWaypoints;
- return croppedWaypoints;
- }
- // DRAGGING IMPLEMENTATION
- function redrawConnection(data) {
- graphicsFactory.update('connection', data.connection, data.connectionGfx);
- }
- function updateDragger(context, segmentOffset, event) {
- var newWaypoints = context.newWaypoints,
- segmentStartIndex = context.segmentStartIndex + segmentOffset,
- segmentStart = newWaypoints[segmentStartIndex],
- segmentEndIndex = context.segmentEndIndex + segmentOffset,
- segmentEnd = newWaypoints[segmentEndIndex],
- axis = flipAxis(context.axis);
- // make sure the dragger does not move
- // outside the connection
- var draggerPosition = axisFenced(event, segmentStart, segmentEnd, axis);
- // update dragger
- translate(context.draggerGfx, draggerPosition.x, draggerPosition.y);
- }
- /**
- * Filter waypoints for redundant ones (i.e. on the same axis).
- * Returns the filtered waypoints and the offset related to the segment move.
- *
- * @param {Array<Point>} waypoints
- * @param {Integer} segmentStartIndex of moved segment start
- *
- * @return {Object} { filteredWaypoints, segmentOffset }
- */
- function filterRedundantWaypoints(waypoints, segmentStartIndex) {
- var segmentOffset = 0;
- var filteredWaypoints = waypoints.filter(function(r, idx) {
- if (pointsOnLine(waypoints[idx - 1], waypoints[idx + 1], r)) {
- // remove point and increment offset
- segmentOffset = idx <= segmentStartIndex ? segmentOffset - 1 : segmentOffset;
- return false;
- }
- // dont remove point
- return true;
- });
- return {
- waypoints: filteredWaypoints,
- segmentOffset: segmentOffset
- };
- }
- eventBus.on('connectionSegment.move.start', function(e) {
- var context = e.context,
- connection = e.connection,
- layer = canvas.getLayer('overlays');
- context.originalWaypoints = connection.waypoints.slice();
- // add dragger gfx
- context.draggerGfx = addSegmentDragger(layer, context.segmentStart, context.segmentEnd);
- svgClasses(context.draggerGfx).add('djs-dragging');
- canvas.addMarker(connection, MARKER_CONNECT_UPDATING);
- });
- eventBus.on('connectionSegment.move.move', function(e) {
- var context = e.context,
- connection = context.connection,
- segmentStartIndex = context.segmentStartIndex,
- segmentEndIndex = context.segmentEndIndex,
- segmentStart = context.segmentStart,
- segmentEnd = context.segmentEnd,
- axis = context.axis;
- var newWaypoints = context.originalWaypoints.slice(),
- newSegmentStart = axisAdd(segmentStart, axis, e['d' + axis]),
- newSegmentEnd = axisAdd(segmentEnd, axis, e['d' + axis]);
- // original waypoint count and added / removed
- // from start waypoint delta. We use the later
- // to retrieve the updated segmentStartIndex / segmentEndIndex
- var waypointCount = newWaypoints.length,
- segmentOffset = 0;
- // move segment start / end by axis delta
- newWaypoints[segmentStartIndex] = newSegmentStart;
- newWaypoints[segmentEndIndex] = newSegmentEnd;
- var sourceToSegmentOrientation,
- targetToSegmentOrientation;
- // handle first segment
- if (segmentStartIndex < 2) {
- sourceToSegmentOrientation = getOrientation(connection.source, newSegmentStart);
- // first bendpoint, remove first segment if intersecting
- if (segmentStartIndex === 1) {
- if (sourceToSegmentOrientation === 'intersect') {
- newWaypoints.shift();
- newWaypoints[0] = newSegmentStart;
- segmentOffset--;
- }
- }
- // docking point, add segment if not intersecting anymore
- else {
- if (sourceToSegmentOrientation !== 'intersect') {
- newWaypoints.unshift(segmentStart);
- segmentOffset++;
- }
- }
- }
- // handle last segment
- if (segmentEndIndex > waypointCount - 3) {
- targetToSegmentOrientation = getOrientation(connection.target, newSegmentEnd);
- // last bendpoint, remove last segment if intersecting
- if (segmentEndIndex === waypointCount - 2) {
- if (targetToSegmentOrientation === 'intersect') {
- newWaypoints.pop();
- newWaypoints[newWaypoints.length - 1] = newSegmentEnd;
- }
- }
- // last bendpoint, remove last segment if intersecting
- else {
- if (targetToSegmentOrientation !== 'intersect') {
- newWaypoints.push(segmentEnd);
- }
- }
- }
- // update connection waypoints
- context.newWaypoints = connection.waypoints = cropConnection(connection, newWaypoints);
- // update dragger position
- updateDragger(context, segmentOffset, e);
- // save segmentOffset in context
- context.newSegmentStartIndex = segmentStartIndex + segmentOffset;
- // redraw connection
- redrawConnection(e);
- });
- eventBus.on('connectionSegment.move.hover', function(e) {
- e.context.hover = e.hover;
- canvas.addMarker(e.hover, MARKER_CONNECT_HOVER);
- });
- eventBus.on([
- 'connectionSegment.move.out',
- 'connectionSegment.move.cleanup'
- ], function(e) {
- // remove connect marker
- // if it was added
- var hover = e.context.hover;
- if (hover) {
- canvas.removeMarker(hover, MARKER_CONNECT_HOVER);
- }
- });
- eventBus.on('connectionSegment.move.cleanup', function(e) {
- var context = e.context,
- connection = context.connection;
- // remove dragger gfx
- if (context.draggerGfx) {
- svgRemove(context.draggerGfx);
- }
- canvas.removeMarker(connection, MARKER_CONNECT_UPDATING);
- });
- eventBus.on([
- 'connectionSegment.move.cancel',
- 'connectionSegment.move.end'
- ], function(e) {
- var context = e.context,
- connection = context.connection;
- connection.waypoints = context.originalWaypoints;
- redrawConnection(e);
- });
- eventBus.on('connectionSegment.move.end', function(e) {
- var context = e.context,
- connection = context.connection,
- newWaypoints = context.newWaypoints,
- newSegmentStartIndex = context.newSegmentStartIndex;
- // ensure we have actual pixel values bendpoint
- // coordinates (important when zoom level was > 1 during move)
- newWaypoints = newWaypoints.map(function(p) {
- return {
- original: p.original,
- x: Math.round(p.x),
- y: Math.round(p.y)
- };
- });
- // apply filter redunant waypoints
- var filtered = filterRedundantWaypoints(newWaypoints, newSegmentStartIndex);
- // get filtered waypoints
- var filteredWaypoints = filtered.waypoints,
- croppedWaypoints = cropConnection(connection, filteredWaypoints),
- segmentOffset = filtered.segmentOffset;
- var hints = {
- segmentMove: {
- segmentStartIndex: context.segmentStartIndex,
- newSegmentStartIndex: newSegmentStartIndex + segmentOffset
- }
- };
- modeling.updateWaypoints(connection, croppedWaypoints, hints);
- });
- }
- ConnectionSegmentMove.$inject = [
- 'injector',
- 'eventBus',
- 'canvas',
- 'dragging',
- 'graphicsFactory',
- 'rules',
- 'modeling'
- ];
|