import { each , isNil , isNumber } from '@antv/util' ; import { IEdge , INode } from '../interface/item' ; import { IPoint , IShapeBase , ModelConfig , NodeConfig , UpdateType } from '../types' ; import { getBBox } from '../util/graphic' ; import { distance , getCircleIntersectByPoint , getEllipseIntersectByPoint , getRectIntersectByPoint , } from '../util/math' ; import Item from './item' ; const CACHE_ANCHOR_POINTS = 'anchorPointsCache' ; const CACHE_BBOX = 'bboxCache' ; export default class Node extends Item implements INode { public getNearestPoint ( points , curPoint ) { let index = 0 ; let nearestPoint = points [ 0 ] ; let minDistance = distance ( points [ 0 ] , curPoint ) ; for ( let i = 0 ; i < points . length ; i ++ ) { const point = points [ i ] ; const dis = distance ( point , curPoint ) ; if ( dis < minDistance ) { nearestPoint = point ; minDistance = dis ; index = i ; } } nearestPoint . anchorIndex = index ; return nearestPoint ; } public getDefaultCfg ( ) { return { type : 'node' , edges : [ ] , } ; } /**
     * 获取从节点关联的所有边
     */ public getEdges ( ) { return this . get ( 'edges' ) ; } /**
     * 获取所有的入边
     */ public getInEdges ( ) { const self = this ; return this . get ( 'edges' ) . filter ( ( edge ) => edge . get ( 'target' ) === self ) ; } /**
     * 获取所有的出边
     */ public getOutEdges ( ) { const self = this ; return this . get ( 'edges' ) . filter ( ( edge ) => edge . get ( 'source' ) === self ) ; } returns { INode [ ] } memberof public getNeighbors ( type ? ) { const edges = this . get ( 'edges' ) as IEdge [ ] ; if ( type === 'target' ) { const neighhborsConverter = ( edge ) => { return edge . getSource ( ) === this ; } ; return edges . filter ( neighhborsConverter ) . map ( ( edge ) => edge . getTarget ( ) ) ; } if ( type === 'source' ) { const neighhborsConverter = ( edge ) => { return edge . getTarget ( ) === this ; } ; return edges . filter ( neighhborsConverter ) . map ( ( edge ) => edge . getSource ( ) ) ; } const neighhborsConverter = ( edge ) => { return edge . getSource ( ) === this ? edge . getTarget ( ) : edge . getSource ( ) ; } ; return edges . map ( neighhborsConverter ) ; } param { Number } index public getLinkPointByAnchor ( index ) { const anchorPoints = this . getAnchorPoints ( ) ; return anchorPoints [ index ] ; } param point public getLinkPoint ( point ) { const keyShape = this . get ( 'keyShape' ) ; const type = keyShape . get ( 'type' ) ; const itemType = this . get ( 'type' ) ; let centerX ; let centerY ; const bbox = this . getBBox ( ) ; if ( itemType === 'combo' ) { centerX = bbox . centerX || ( bbox . maxX + bbox . minX ) / 2 ; centerY = bbox . centerY || ( bbox . maxY + bbox . minY ) / 2 ; } else { centerX = bbox . centerX ; centerY = bbox . centerY ; } const anchorPoints = this . getAnchorPoints ( ) ; let intersectPoint ; switch ( type ) { case 'circle' : intersectPoint = getCircleIntersectByPoint ( { x : centerX ! , y : centerY ! , r : bbox . width / 2 , } , point ) ; break ; case 'ellipse' : intersectPoint = getEllipseIntersectByPoint ( { x : centerX ! , y : centerY ! , rx : bbox . width / 2 , ry : bbox . height / 2 , } , point ) ; break ; default : intersectPoint = getRectIntersectByPoint ( bbox , point ) ; } let linkPoint = intersectPoint ; if ( anchorPoints . length ) { if ( ! linkPoint ) { linkPoint = point ; } linkPoint = this . getNearestPoint ( anchorPoints , linkPoint ) ; } if ( ! linkPoint ) { linkPoint = { x : centerX , y : centerY } as IPoint ; } return linkPoint ; } return { array } public getAnchorPoints ( ) { let anchorPoints = this . get ( CACHE_ANCHOR_POINTS ) ; if ( ! anchorPoints ) { anchorPoints = [ ] ; const shapeFactory = this . get ( 'shapeFactory' ) ; const bbox = this . getBBox ( ) ; const model = this . get ( 'model' ) ; const shapeCfg = this . getShapeCfg ( model ) ; const type = model . type ; const points = shapeFactory . getAnchorPoints ( type , shapeCfg ) || [ ] ; each ( points , ( pointArr , index ) => { const point = { x : bbox . minX + pointArr [ 0 ] * bbox . width , y : bbox . minY + pointArr [ 1 ] * bbox . height , anchorIndex : index , } ; anchorPoints . push ( point ) ; } ) ; this . set ( CACHE_ANCHOR_POINTS , anchorPoints ) ; } return anchorPoints ; } param edge public addEdge ( edge ) { this . get ( 'edges' ) . push ( edge ) ; } /**
     * 锁定节点
     */ public lock ( ) { this . set ( 'locked' , true ) ; } /**
     * 解锁锁定的节点
     */ public unlock ( ) { this . set ( 'locked' , false ) ; } public hasLocked ( ) { return this . get ( 'locked' ) ; } param { Edge } edge public removeEdge ( edge ) { const edges = this . getEdges ( ) ; const index = edges . indexOf ( edge ) ; if ( index > - 1 ) edges . splice ( index , 1 ) ; } public clearCache ( ) { this . set ( CACHE_BBOX , null ) ; this . set ( CACHE_ANCHOR_POINTS , null ) ; } param cfg public getUpdateType ( cfg ? ) { if ( ! cfg ) return undefined ; const existX = ! isNil ( cfg . x ) ; const existY = ! isNil ( cfg . y ) ; const keys = Object . keys ( cfg ) ; if ( ( keys . length === 1 && ( existX || existY ) ) || ( keys . length === 2 && existX && existY ) ) return 'move' ; if ( isNumber ( cfg . x ) || isNumber ( cfg . y ) || cfg . type || cfg . anchorPoints || cfg . size || ( cfg ?. style && ( cfg ?. style ?. r || cfg ?. style ?. width || cfg ?. style ?. height || cfg ?. style ?. rx || cfg ?. style ?. ry ) ) ) return 'bbox|label' ; let updateLabel = keys . includes ( 'label' ) || keys . includes ( 'labelCfg' ) ; return updateLabel ? 'style|label' : 'style' ; } }