UNPKG

@antv/x6

Version:

JavaScript diagramming library that uses SVG and HTML for rendering

130 lines (116 loc) 3.64 kB
import { Dom, ObjectExt, Util } from '../../common' import { Ellipse, Path, Rectangle, type Segment } from '../../geometry' import type { ConnectionPointDefinition, ConnectionPointStrokedOptions, } from './index' import { findShapeNode, getStrokeWidth, offset } from './util' export interface BoundaryOptions extends ConnectionPointStrokedOptions { selector?: string | string[] insideout?: boolean precision?: number extrapolate?: boolean sticky?: boolean } export interface BoundaryCache { shapeBBox?: Rectangle | null segmentSubdivisions?: Segment[][] } /** * Places the connection point at the intersection between the * edge path end segment and the actual shape of the target magnet. */ export const boundary: ConnectionPointDefinition<BoundaryOptions> = ( line, view, magnet, options, ) => { let node let intersection const anchor = line.end const selector = options.selector if (typeof selector === 'string') { node = view.findOne(selector) } else if (Array.isArray(selector)) { node = ObjectExt.getByPath(magnet, selector) } else { node = findShapeNode(magnet) } if (!Dom.isSVGGraphicsElement(node)) { if (node === magnet || !Dom.isSVGGraphicsElement(magnet)) { return anchor } node = magnet } const localShape = view.getShapeOfElement(node) const magnetMatrix = view.getMatrixOfElement(node) const translateMatrix = view.getRootTranslatedMatrix() const rotateMatrix = view.getRootRotatedMatrix() const targetMatrix = translateMatrix .multiply(rotateMatrix) .multiply(magnetMatrix) const localMatrix = targetMatrix.inverse() const localLine = Util.transformLine(line, localMatrix) const localRef = localLine.start.clone() const data = view.getDataOfElement(node) as BoundaryCache if (options.insideout === false) { if (data.shapeBBox == null) { data.shapeBBox = localShape.bbox() } const localBBox = data.shapeBBox if (localBBox != null && localBBox.containsPoint(localRef)) { return anchor } } if (options.extrapolate === true) { localLine.setLength(1e6) } // Caching segment subdivisions for paths let pathOptions if (Path.isPath(localShape)) { const precision = options.precision || 2 if (data.segmentSubdivisions == null) { data.segmentSubdivisions = localShape.getSegmentSubdivisions({ precision, }) } pathOptions = { precision, segmentSubdivisions: data.segmentSubdivisions, } intersection = localLine.intersect(localShape, pathOptions) } else { intersection = localLine.intersect(localShape) } if (intersection) { if (Array.isArray(intersection)) { intersection = localRef.closest(intersection) } } else if (options.sticky === true) { // No intersection, find the closest point instead if (Rectangle.isRectangle(localShape)) { intersection = localShape.getNearestPointToPoint(localRef) } else if (Ellipse.isEllipse(localShape)) { intersection = localShape.intersectsWithLineFromCenterToPoint(localRef) } else { intersection = localShape.closestPoint(localRef, pathOptions) } } const cp = intersection ? Util.transformPoint(intersection, targetMatrix) : anchor let cpOffset = options.offset || 0 if (options.stroked !== false) { if (typeof cpOffset === 'object') { cpOffset = { ...cpOffset } if (cpOffset.x == null) { cpOffset.x = 0 } cpOffset.x += getStrokeWidth(node) / 2 } else { cpOffset += getStrokeWidth(node) / 2 } } return offset(cp, line.start, cpOffset) }