import inherits from 'inherits-browser'; import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; import { pointsAligned } from 'diagram-js/lib/util/Geometry'; import { assign } from 'min-dash'; import { DEFAULT_CONNECT } from '../../config/constants'; import { NodeUtils } from '../../utils/nodeUtil'; /** * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus * @typedef {import('diagram-js/lib/features/grid-snapping/GridSnapping').default} GridSnapping * @typedef {import('../../modeling/Modeling').default} Modeling * * @typedef {import('diagram-js/lib/util/Types').Point} Point */ const HIGH_PRIORITY = 3000; export default function GridSnappingLayoutConnectionBehavior(this: any, eventBus: any, gridSnapping: any, modeling: any, jnpfData: any, injector: any) { CommandInterceptor.call(this, eventBus); this._gridSnapping = gridSnapping; this._injector = injector; const self = this; this.postExecuted(['connection.create', 'connection.layout'], HIGH_PRIORITY, function (event: any) { let context = event.context, connection = context.connection, waypoints = connection.waypoints; let target = connection.target, source = connection.source; // 横向美化 if (jnpfData.data?.layout?.value === 'horizontal') { let x = source.x; let y = source.y + source.height / 2; let targetX = target.x + target.width / 2; if (source.x + source.width <= target.x) { // 右侧 x = source.x + source.width; if (y === target.y + target.height / 2) { waypoints = [ { x: x, y: y }, { x: target.x, y: target.y + target.height / 2 }, ]; } else if (y >= target.y + target.height && x + 30 > target.x) { waypoints = [ { x: x, y: y }, { x: targetX, y: y }, { x: targetX, y: target.y + target.height }, ]; } else if (y - 10 < target.y && x + 30 > target.x) { waypoints = [ { x: x, y: y }, { x: targetX, y: y }, { x: targetX, y: target.y }, ]; } else { if (target.x - source.x - source.width - DEFAULT_CONNECT / 2 > 0) { waypoints = [ { x: x, y: y }, { x: target.x - DEFAULT_CONNECT / 2, y: y }, { x: target.x - DEFAULT_CONNECT / 2, y: target.y + target.height / 2 }, { x: target.x, y: target.y + target.height / 2 }, ]; } else { waypoints = [ { x: x, y: y }, { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: y }, { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: target.y + target.height / 2 }, { x: target.x, y: target.y + target.height / 2 }, ]; } } } else if (source.x > target.x + target.width) { // 左侧 x = target.x + target.width; if (x + 30 > source.x) { if (y - 10 > target.y + target.height) { waypoints = [ { x: source.x, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } else if (y - 10 < target.y + target.height) { waypoints = [ { x: source.x, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: target.y }, ]; } } else { waypoints = [ { x: source.x, y: source.y + source.height / 2 }, { x: (source.x - x) / 2 + x, y: source.y + source.height / 2 }, { x: (source.x - x) / 2 + x, y: target.y + target.height / 2 }, { x: x, y: target.y + target.height / 2 }, ]; } } else { // 上侧 if (target.y < source.y) { if (source.x + source.width / 2 === target.x + target.width / 2) { waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } else if (target.y + target.height > source.y - 20) { if (source.x + source.width / 2 >= target.x + target.width) { // 左上 waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x + target.width, y: target.y + target.height / 2 }, ]; } else if (source.x + source.width / 2 <= target.x) { // 右上 waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x, y: target.y + target.height / 2 }, ]; } else { waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: source.x + source.width / 2, y: source.y - (source.y - target.y - target.height) / 2 }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } } else { waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: source.x + source.width / 2, y: source.y - (source.y - target.y - target.height) / 2 }, { x: target.x + target.width / 2, y: source.y - (source.y - target.y - target.height) / 2 }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } } else if (target.y + target.height > source.y + source.height) { // 下侧 if (source.x + source.width / 2 === target.x + target.width / 2) { waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: target.x + target.width / 2, y: target.y }, ]; } else if (target.y - 20 < source.y + source.height) { if (source.x + source.width / 2 >= target.x + target.width) { // 左下 waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x + target.width, y: target.y + target.height / 2 }, ]; } else if (source.x + source.width / 2 <= target.x) { // 右下 waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x, y: target.y + target.height / 2 }, ]; } else { waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: source.x + source.width / 2, y: target.y - (target.y - source.y - source.height) / 2 }, { x: target.x + target.width / 2, y: target.y - (target.y - source.y - source.height) / 2 }, { x: target.x + target.width / 2, y: target.y }, ]; } } else { waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: source.x + source.width / 2, y: target.y - (target.y - source.y - source.height) / 2 }, { x: target.x + target.width / 2, y: target.y - (target.y - source.y - source.height) / 2 }, { x: target.x + target.width / 2, y: target.y }, ]; } } } } if (jnpfData.data?.layout?.value === 'vertical') { if (source.y + source.height < target.y) { if (source.x + source.width / 2 === target.x + target.width / 2) { waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: target.x + target.width / 2, y: target.y }, ]; } else { if (source.y + source.height + 20 > target.y && source.x + source.width / 2 < target.x) { waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x, y: target.y + target.height / 2 }, ]; } else if (source.y + source.height + 20 > target.y && source.x + source.width / 2 > target.x + target.width + 10) { waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x + target.width, y: target.y + target.height / 2 }, ]; } else waypoints = [ { x: source.x + source.width / 2, y: source.y + source.height }, { x: source.x + source.width / 2, y: source.y + source.height + (target.y - source.y - source.height) / 2 }, { x: target.x + target.width / 2, y: source.y + source.height + (target.y - source.y - source.height) / 2 }, { x: target.x + target.width / 2, y: target.y }, ]; } } else if (source.y > target.y + target.height) { if (source.x + source.width / 2 === target.x + target.width / 2) { waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } else { if (source.y - 20 < target.y + target.height && source.x + source.width / 2 < target.x) { waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x, y: target.y + target.height / 2 }, ]; } else if (source.y - 20 < target.y + target.height && source.x + source.width / 2 > target.x + target.width + 10) { waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: source.x + source.width / 2, y: target.y + target.height / 2 }, { x: target.x + target.width, y: target.y + target.height / 2 }, ]; } else waypoints = [ { x: source.x + source.width / 2, y: source.y }, { x: source.x + source.width / 2, y: source.y - (source.y - target.y - target.height) / 2 }, { x: target.x + target.width / 2, y: source.y - (source.y - target.y - target.height) / 2 }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } } else if (source.x + source.width / 2 < target.x - 10) { // 右侧 if (source.x + source.width >= target.x - 10 && source.y + source.height / 2 >= target.y + target.height - 10) { waypoints = [ { x: source.x + source.width, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } else if (source.x + source.width >= target.x - 10 && source.y + source.height / 2 + 10 <= target.y) { waypoints = [ { x: source.x + source.width, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: target.y }, ]; } else { waypoints = [ { x: source.x + source.width, y: source.y + source.height / 2 }, { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: source.y + source.height / 2 }, { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: target.y + target.height / 2 }, { x: target.x, y: target.y + target.height / 2 }, ]; } } else if (source.x + source.width / 2 > target.x + target.width - 10) { // 左侧 if (source.x < target.x + target.width + 20 && source.y + source.height / 2 >= target.y + target.height - 10) { waypoints = [ { x: source.x, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: target.y + target.height }, ]; } else if (source.x < target.x + target.width + 20 && source.y + source.height / 2 + 10 <= target.y) { waypoints = [ { x: source.x, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: source.y + source.height / 2 }, { x: target.x + target.width / 2, y: target.y }, ]; } else waypoints = [ { x: source.x, y: source.y + source.height / 2 }, { x: target.x + target.width + (source.x - target.x - target.width) / 2, y: source.y + source.height / 2 }, { x: target.x + target.width + (source.x - target.x - target.width) / 2, y: target.y + target.height / 2 }, { x: target.x + target.width, y: target.y + target.height / 2 }, ]; } } // 判断是横向还是纵向 如果是纵向 需要重新修改坐标 modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints)); let elementRegistry = injector.get('elementRegistry'); let labelCenter = NodeUtils.updateLabelWaypoints(connection, elementRegistry, jnpfData); let connectLabel = connection.label; if (connectLabel) { let label = elementRegistry.get(connectLabel.id); label.x = labelCenter.x; label.y = labelCenter.y; modeling.updateProperties(label, {}); } }); } GridSnappingLayoutConnectionBehavior.$inject = ['eventBus', 'gridSnapping', 'modeling', 'jnpfData', 'injector']; inherits(GridSnappingLayoutConnectionBehavior, CommandInterceptor); /** * Snap middle segments of a given connection. * * @param {Point[]} waypoints * * @return {Point[]} */ GridSnappingLayoutConnectionBehavior.prototype.snapMiddleSegments = function (this: any, waypoints: any[]) { const gridSnapping = this._gridSnapping; waypoints = waypoints.slice(); for (let i = 1; i < waypoints.length - 2; i++) { const snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]); waypoints[i] = snapped[0]; waypoints[i + 1] = snapped[1]; } return waypoints; }; /** * Check whether a connection has middle segments. * * @param {Point[]} waypoints * * @return {boolean} */ function hasMiddleSegments(waypoints: any[]) { return waypoints.length > 3; } /** * Check whether an alignment is horizontal. * * @param {string} aligned * * @return {boolean} */ function horizontallyAligned(aligned: string) { return aligned === 'h'; } /** * Check whether an alignment is vertical. * * @param {string} aligned * * @return {boolean} */ function verticallyAligned(aligned: string) { return aligned === 'v'; } /** * Get middle segments from a given connection. * * @param {Point[]} waypoints * * @return {Point[]} */ function snapSegment(gridSnapping: any, segmentStart: any, segmentEnd: any) { const aligned: any = pointsAligned(segmentStart, segmentEnd); const snapped: any = {}; if (horizontallyAligned(aligned)) { snapped.y = gridSnapping.snapValue(segmentStart.y); } if (verticallyAligned(aligned)) { snapped.x = gridSnapping.snapValue(segmentStart.x); } if ('x' in snapped || 'y' in snapped) { segmentStart = assign({}, segmentStart, snapped); segmentEnd = assign({}, segmentEnd, snapped); } return [segmentStart, segmentEnd]; }