diagram-js
Version:
A toolbox for displaying and modifying diagrams on the web
210 lines (157 loc) • 4.73 kB
JavaScript
import {
toPoint
} from '../../util/Event';
import {
getMidPoint,
pointsAligned
} from '../../util/Geometry';
import {
append as svgAppend,
attr as svgAttr,
classes as svgClasses,
create as svgCreate
} from 'tiny-svg';
import {
rotate,
translate
} from '../../util/SvgTransformUtil';
import {
getApproxIntersection
} from '../../util/LineIntersection';
import { getDistancePointLine, perpendicularFoot } from './GeometricUtil';
/**
* @typedef {import('../../core/Types').ConnectionLike} Connection
*
* @typedef {import('../../util/Types').Point} Point
*/
export var BENDPOINT_CLS = 'djs-bendpoint';
export var SEGMENT_DRAGGER_CLS = 'djs-segment-dragger';
export function toCanvasCoordinates(canvas, event) {
var position = toPoint(event),
clientRect = canvas._container.getBoundingClientRect(),
offset;
// canvas relative position
offset = {
x: clientRect.left,
y: clientRect.top
};
// update actual event payload with canvas relative measures
var viewbox = canvas.viewbox();
return {
x: viewbox.x + (position.x - offset.x) / viewbox.scale,
y: viewbox.y + (position.y - offset.y) / viewbox.scale
};
}
export function getConnectionIntersection(canvas, waypoints, event) {
var localPosition = toCanvasCoordinates(canvas, event),
intersection = getApproxIntersection(waypoints, localPosition);
return intersection;
}
export function addBendpoint(parentGfx, cls) {
var groupGfx = svgCreate('g');
svgClasses(groupGfx).add(BENDPOINT_CLS);
svgAppend(parentGfx, groupGfx);
var visual = svgCreate('circle');
svgAttr(visual, {
cx: 0,
cy: 0,
r: 4
});
svgClasses(visual).add('djs-visual');
svgAppend(groupGfx, visual);
var hit = svgCreate('circle');
svgAttr(hit, {
cx: 0,
cy: 0,
r: 10
});
svgClasses(hit).add('djs-hit');
svgAppend(groupGfx, hit);
if (cls) {
svgClasses(groupGfx).add(cls);
}
return groupGfx;
}
function createParallelDragger(parentGfx, segmentStart, segmentEnd, alignment) {
var draggerGfx = svgCreate('g');
svgAppend(parentGfx, draggerGfx);
var width = 18,
height = 6,
padding = 11,
hitWidth = calculateHitWidth(segmentStart, segmentEnd, alignment),
hitHeight = height + padding;
var visual = svgCreate('rect');
svgAttr(visual, {
x: -width / 2,
y: -height / 2,
width: width,
height: height
});
svgClasses(visual).add('djs-visual');
svgAppend(draggerGfx, visual);
var hit = svgCreate('rect');
svgAttr(hit, {
x: -hitWidth / 2,
y: -hitHeight / 2,
width: hitWidth,
height: hitHeight
});
svgClasses(hit).add('djs-hit');
svgAppend(draggerGfx, hit);
rotate(draggerGfx, alignment === 'v' ? 90 : 0, 0, 0);
return draggerGfx;
}
export function addSegmentDragger(parentGfx, segmentStart, segmentEnd) {
var groupGfx = svgCreate('g'),
mid = getMidPoint(segmentStart, segmentEnd),
alignment = pointsAligned(segmentStart, segmentEnd);
svgAppend(parentGfx, groupGfx);
createParallelDragger(groupGfx, segmentStart, segmentEnd, alignment);
svgClasses(groupGfx).add(SEGMENT_DRAGGER_CLS);
svgClasses(groupGfx).add(alignment === 'h' ? 'horizontal' : 'vertical');
translate(groupGfx, mid.x, mid.y);
return groupGfx;
}
/**
* Calculates region for segment move which is 2/3 of the full segment length
* @param {number} segmentLength
*
* @return {number}
*/
export function calculateSegmentMoveRegion(segmentLength) {
return Math.abs(Math.round(segmentLength * 2 / 3));
}
/**
* Returns the point with the closest distance that is on the connection path.
*
* @param {Point} position
* @param {Connection} connection
* @return {Point}
*/
export function getClosestPointOnConnection(position, connection) {
var segment = getClosestSegment(position, connection);
return perpendicularFoot(position, segment);
}
// helper //////////
function calculateHitWidth(segmentStart, segmentEnd, alignment) {
var segmentLengthXAxis = segmentEnd.x - segmentStart.x,
segmentLengthYAxis = segmentEnd.y - segmentStart.y;
return alignment === 'h' ?
calculateSegmentMoveRegion(segmentLengthXAxis) :
calculateSegmentMoveRegion(segmentLengthYAxis);
}
function getClosestSegment(position, connection) {
var waypoints = connection.waypoints;
var minDistance = Infinity,
segmentIndex;
for (var i = 0; i < waypoints.length - 1; i++) {
var start = waypoints[i],
end = waypoints[i + 1],
distance = getDistancePointLine(position, [ start, end ]);
if (distance < minDistance) {
minDistance = distance;
segmentIndex = i;
}
}
return [ waypoints[segmentIndex], waypoints[segmentIndex + 1] ];
}