123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- "use strict";
- /* eslint-disable no-underscore-dangle */
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.jumpover = void 0;
- const x6_geometry_1 = require("@antv/x6-geometry");
- // takes care of math. error for case when jump is too close to end of line
- const CLOSE_PROXIMITY_PADDING = 1;
- const F13 = 1 / 3;
- const F23 = 2 / 3;
- function setupUpdating(view) {
- let updateList = view.graph._jumpOverUpdateList;
- // first time setup for this paper
- if (updateList == null) {
- updateList = view.graph._jumpOverUpdateList = [];
- view.graph.on('cell:mouseup', () => {
- const list = view.graph._jumpOverUpdateList;
- // add timeout to wait for the target node to be connected
- // fix https://github.com/antvis/X6/issues/3387
- setTimeout(() => {
- for (let i = 0; i < list.length; i += 1) {
- list[i].update();
- }
- });
- });
- view.graph.on('model:reseted', () => {
- updateList = view.graph._jumpOverUpdateList = [];
- });
- }
- // add this link to a list so it can be updated when some other link is updated
- if (updateList.indexOf(view) < 0) {
- updateList.push(view);
- // watch for change of connector type or removal of link itself
- // to remove the link from a list of jump over connectors
- const clean = () => updateList.splice(updateList.indexOf(view), 1);
- view.cell.once('change:connector', clean);
- view.cell.once('removed', clean);
- }
- }
- function createLines(sourcePoint, targetPoint, route = []) {
- const points = [sourcePoint, ...route, targetPoint];
- const lines = [];
- points.forEach((point, idx) => {
- const next = points[idx + 1];
- if (next != null) {
- lines.push(new x6_geometry_1.Line(point, next));
- }
- });
- return lines;
- }
- function findLineIntersections(line, crossCheckLines) {
- const intersections = [];
- crossCheckLines.forEach((crossCheckLine) => {
- const intersection = line.intersectsWithLine(crossCheckLine);
- if (intersection) {
- intersections.push(intersection);
- }
- });
- return intersections;
- }
- function getDistence(p1, p2) {
- return new x6_geometry_1.Line(p1, p2).squaredLength();
- }
- /**
- * Split input line into multiple based on intersection points.
- */
- function createJumps(line, intersections, jumpSize) {
- return intersections.reduce((memo, point, idx) => {
- // skipping points that were merged with the previous line
- // to make bigger arc over multiple lines that are close to each other
- if (skippedPoints.includes(point)) {
- return memo;
- }
- // always grab the last line from buffer and modify it
- const lastLine = memo.pop() || line;
- // calculate start and end of jump by moving by a given size of jump
- const jumpStart = x6_geometry_1.Point.create(point).move(lastLine.start, -jumpSize);
- let jumpEnd = x6_geometry_1.Point.create(point).move(lastLine.start, +jumpSize);
- // now try to look at the next intersection point
- const nextPoint = intersections[idx + 1];
- if (nextPoint != null) {
- const distance = jumpEnd.distance(nextPoint);
- if (distance <= jumpSize) {
- // next point is close enough, move the jump end by this
- // difference and mark the next point to be skipped
- jumpEnd = nextPoint.move(lastLine.start, distance);
- skippedPoints.push(nextPoint);
- }
- }
- else {
- // this block is inside of `else` as an optimization so the distance is
- // not calculated when we know there are no other intersection points
- const endDistance = jumpStart.distance(lastLine.end);
- // if the end is too close to possible jump, draw remaining line instead of a jump
- if (endDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) {
- memo.push(lastLine);
- return memo;
- }
- }
- const startDistance = jumpEnd.distance(lastLine.start);
- if (startDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) {
- // if the start of line is too close to jump, draw that line instead of a jump
- memo.push(lastLine);
- return memo;
- }
- // finally create a jump line
- const jumpLine = new x6_geometry_1.Line(jumpStart, jumpEnd);
- // it's just simple line but with a `isJump` property
- jumppedLines.push(jumpLine);
- memo.push(new x6_geometry_1.Line(lastLine.start, jumpStart), jumpLine, new x6_geometry_1.Line(jumpEnd, lastLine.end));
- return memo;
- }, []);
- }
- function buildPath(lines, jumpSize, jumpType, radius) {
- const path = new x6_geometry_1.Path();
- let segment;
- // first move to the start of a first line
- segment = x6_geometry_1.Path.createSegment('M', lines[0].start);
- path.appendSegment(segment);
- lines.forEach((line, index) => {
- if (jumppedLines.includes(line)) {
- let angle;
- let diff;
- let control1;
- let control2;
- if (jumpType === 'arc') {
- // approximates semicircle with 2 curves
- angle = -90;
- // determine rotation of arc based on difference between points
- diff = line.start.diff(line.end);
- // make sure the arc always points up (or right)
- const xAxisRotate = diff.x < 0 || (diff.x === 0 && diff.y < 0);
- if (xAxisRotate) {
- angle += 180;
- }
- const center = line.getCenter();
- const centerLine = new x6_geometry_1.Line(center, line.end).rotate(angle, center);
- let halfLine;
- // first half
- halfLine = new x6_geometry_1.Line(line.start, center);
- control1 = halfLine.pointAt(2 / 3).rotate(angle, line.start);
- control2 = centerLine.pointAt(1 / 3).rotate(-angle, centerLine.end);
- segment = x6_geometry_1.Path.createSegment('C', control1, control2, centerLine.end);
- path.appendSegment(segment);
- // second half
- halfLine = new x6_geometry_1.Line(center, line.end);
- control1 = centerLine.pointAt(1 / 3).rotate(angle, centerLine.end);
- control2 = halfLine.pointAt(1 / 3).rotate(-angle, line.end);
- segment = x6_geometry_1.Path.createSegment('C', control1, control2, line.end);
- path.appendSegment(segment);
- }
- else if (jumpType === 'gap') {
- segment = x6_geometry_1.Path.createSegment('M', line.end);
- path.appendSegment(segment);
- }
- else if (jumpType === 'cubic') {
- // approximates semicircle with 1 curve
- angle = line.start.theta(line.end);
- const xOffset = jumpSize * 0.6;
- let yOffset = jumpSize * 1.35;
- // determine rotation of arc based on difference between points
- diff = line.start.diff(line.end);
- // make sure the arc always points up (or right)
- const xAxisRotate = diff.x < 0 || (diff.x === 0 && diff.y < 0);
- if (xAxisRotate) {
- yOffset *= -1;
- }
- control1 = new x6_geometry_1.Point(line.start.x + xOffset, line.start.y + yOffset).rotate(angle, line.start);
- control2 = new x6_geometry_1.Point(line.end.x - xOffset, line.end.y + yOffset).rotate(angle, line.end);
- segment = x6_geometry_1.Path.createSegment('C', control1, control2, line.end);
- path.appendSegment(segment);
- }
- }
- else {
- const nextLine = lines[index + 1];
- if (radius === 0 || !nextLine || jumppedLines.includes(nextLine)) {
- segment = x6_geometry_1.Path.createSegment('L', line.end);
- path.appendSegment(segment);
- }
- else {
- buildRoundedSegment(radius, path, line.end, line.start, nextLine.end);
- }
- }
- });
- return path;
- }
- function buildRoundedSegment(offset, path, curr, prev, next) {
- const prevDistance = curr.distance(prev) / 2;
- const nextDistance = curr.distance(next) / 2;
- const startMove = -Math.min(offset, prevDistance);
- const endMove = -Math.min(offset, nextDistance);
- const roundedStart = curr.clone().move(prev, startMove).round();
- const roundedEnd = curr.clone().move(next, endMove).round();
- const control1 = new x6_geometry_1.Point(F13 * roundedStart.x + F23 * curr.x, F23 * curr.y + F13 * roundedStart.y);
- const control2 = new x6_geometry_1.Point(F13 * roundedEnd.x + F23 * curr.x, F23 * curr.y + F13 * roundedEnd.y);
- let segment;
- segment = x6_geometry_1.Path.createSegment('L', roundedStart);
- path.appendSegment(segment);
- segment = x6_geometry_1.Path.createSegment('C', control1, control2, roundedEnd);
- path.appendSegment(segment);
- }
- let jumppedLines;
- let skippedPoints;
- const jumpover = function (sourcePoint, targetPoint, routePoints, options = {}) {
- jumppedLines = [];
- skippedPoints = [];
- setupUpdating(this);
- const jumpSize = options.size || 5;
- const jumpType = options.type || 'arc';
- const radius = options.radius || 0;
- // list of connector types not to jump over.
- const ignoreConnectors = options.ignoreConnectors || ['smooth'];
- const graph = this.graph;
- const model = graph.model;
- const allLinks = model.getEdges();
- // there is just one link, draw it directly
- if (allLinks.length === 1) {
- return buildPath(createLines(sourcePoint, targetPoint, routePoints), jumpSize, jumpType, radius);
- }
- const edge = this.cell;
- const thisIndex = allLinks.indexOf(edge);
- const defaultConnector = graph.options.connecting.connector || {};
- // not all links are meant to be jumped over.
- const edges = allLinks.filter((link, idx) => {
- const connector = link.getConnector() || defaultConnector;
- // avoid jumping over links with connector type listed in `ignored connectors`.
- if (ignoreConnectors.includes(connector.name)) {
- return false;
- }
- // filter out links that are above this one and have the same connector type
- // otherwise there would double hoops for each intersection
- if (idx > thisIndex) {
- return connector.name !== 'jumpover';
- }
- return true;
- });
- // find views for all links
- const linkViews = edges.map((edge) => {
- return graph.findViewByCell(edge);
- });
- // create lines for this link
- const thisLines = createLines(sourcePoint, targetPoint, routePoints);
- // create lines for all other links
- const linkLines = linkViews.map((linkView) => {
- if (linkView == null) {
- return [];
- }
- if (linkView === this) {
- return thisLines;
- }
- return createLines(linkView.sourcePoint, linkView.targetPoint, linkView.routePoints);
- });
- // transform lines for this link by splitting with jump lines at
- // points of intersection with other links
- const jumpingLines = [];
- thisLines.forEach((line) => {
- // iterate all links and grab the intersections with this line
- // these are then sorted by distance so the line can be split more easily
- const intersections = edges
- .reduce((memo, link, i) => {
- // don't intersection with itself
- if (link !== edge) {
- const lineIntersections = findLineIntersections(line, linkLines[i]);
- memo.push(...lineIntersections);
- }
- return memo;
- }, [])
- .sort((a, b) => getDistence(line.start, a) - getDistence(line.start, b));
- if (intersections.length > 0) {
- // split the line based on found intersection points
- jumpingLines.push(...createJumps(line, intersections, jumpSize));
- }
- else {
- // without any intersection the line goes uninterrupted
- jumpingLines.push(line);
- }
- });
- const path = buildPath(jumpingLines, jumpSize, jumpType, radius);
- jumppedLines = [];
- skippedPoints = [];
- return options.raw ? path : path.serialize();
- };
- exports.jumpover = jumpover;
- //# sourceMappingURL=jumpover.js.map
|