import { watchEffect } from "vue" ; import { Reactive } from "./common" ; import { Config , Configs , EdgeKeepOrderType } from "./configs" ; import { Edge , Edges , LinePosition , Nodes , Position } from "./types" ; import { updateObjectDiff } from "./utility" ; interface EdgeLayoutPoint { edge : Edge ; pointInGroup : number ; groupWidth : number ; } export interface EdgeGroup { edges : Edges ; groupWidth : number ; summarize : boolean ; } export interface EdgeGroupStates { edgeLayoutPoints : Record < string , EdgeLayoutPoint > ; edgeGroups : Record < string , EdgeGroup > ; summarizedEdges : Record < string , true > ; } param nodes param edges param configs returns export function makeEdgeGroupStates ( nodes , edges , configs ) { const state = Reactive < EdgeGroupStates > ( { edgeLayoutPoints : { } , edgeGroups : { } , summarizedEdges : { } , } ) ; watchEffect ( ( ) => { const { edgeLayoutPoints , edgeGroups } = calculateEdgeGroupAndPositions ( configs , nodes , edges ) ; updateObjectDiff ( state . edgeLayoutPoints , edgeLayoutPoints ) ; updateObjectDiff ( state . edgeGroups , edgeGroups ) ; } ) ; watchEffect ( ( ) => { const summarizedEdges = { } ; for ( const [ id , { edges , groupWidth } ] of Object . entries ( state . edgeGroups ) ) { let summarize = false ; if ( groupWidth == 0 ) { summarize = false ; } else if ( configs . edge . summarize instanceof Function ) { const s = configs . edge . summarize ( edges , configs ) ; if ( s === null ) { summarize = defaultCheckSummarize ( nodes , edges , configs , groupWidth ) ; } else { summarize = s ; } } else if ( configs . edge . summarize ) { summarize = defaultCheckSummarize ( nodes , edges , configs , groupWidth ) ; } else { summarize = false ; } state . edgeGroups [ id ] . summarize = summarize ; if ( summarize ) { Object . keys ( edges ) . forEach ( id => ( summarizedEdges [ id ] = true ) ) ; } } updateObjectDiff ( state . summarizedEdges , summarizedEdges ) ; } ) ; return state ; } param p param isSummarized param source param target param scale param keepOrder returns export function calculateEdgeShiftedPosition ( p , isSummarized , source , target , scale , keepOrder ) { if ( ! p ) { return { x1 : 0 , y1 : 0 , x2 : 0 , y2 : 0 } ; } if ( isSummarized ) { return calculateEdgePositionInner ( p . edge , source , target , scale , 0 , 0 , keepOrder ) ; } else { return calculateEdgePositionInner ( p . edge , source , target , scale , p . groupWidth , p . pointInGroup , keepOrder ) ; } } function calculateEdgeGroupAndPositions ( configs , nodes , edges ) { const edgeLayoutPoints = { } ; const edgeGroups = { } ; const map = { } ; for ( const [ id , edge ] of Object . entries ( edges ) ) { if ( ! ( edge . source in nodes && edge . target in nodes ) ) { continue ; } const key = [ edge . source , edge . target ] . sort ( ) . join ( "<=>" ) ; const values = map [ key ] || { } ; values [ id ] = edge ; map [ key ] = values ; } const calcGap = configs . edge . gap instanceof Function ? configs . edge . gap : ( _e , _c ) => configs . edge . gap as number ; for ( const [ key , edges ] of Object . entries ( map ) ) { const edgeLen = Object . keys ( edges ) . length ; if ( edgeLen == 0 ) continue ; const gap = calcGap ( edges , configs ) ; const [ edgeId , edge ] = Object . entries ( edges ) [ 0 ] ; if ( edgeLen === 1 ) { edgeLayoutPoints [ edgeId ] = { edge , pointInGroup : 0 , groupWidth : 0 } ; edgeGroups [ key ] = { edges , groupWidth : 0 , summarize : false } ; } else { let pointInGroup = 0 ; const lineHalfWidths = Object . values ( edges ) . map ( edge => { return Config . value ( configs . edge . normal . width , edge ) / 2 ; } ) ; const points = Object . entries ( edges ) . map ( ( [ edgeId , edge ] , i ) => { if ( i > 0 ) { pointInGroup += lineHalfWidths [ i - 1 ] + gap + lineHalfWidths [ i ] ; } return [ edgeId , edge , pointInGroup ] as [ string , Edge , number ] ; } ) ; const groupWidth = pointInGroup ; points . forEach ( ( [ edgeId , edge , pointInGroup ] ) => { edgeLayoutPoints [ edgeId ] = { edge , pointInGroup , groupWidth } ; } ) ; edgeGroups [ key ] = { edges , groupWidth , summarize : false } ; } } return { edgeLayoutPoints , edgeGroups } ; } function defaultCheckSummarize ( nodes , edges , configs , width ) { const edgeCount = Object . entries ( edges ) . length ; if ( edgeCount === 1 ) return false ; const minWidth = Math . min ( ... Object . values ( edges ) . flatMap ( e => [ nodes [ e . source ] , nodes [ e . target ] ] ) . filter ( v => v ) . map ( node => { const shape = Config . values ( configs . node . normal , node ) ; if ( shape . type === "circle" ) { return shape . radius * 2 ; } else { return Math . min ( shape . width , shape . height ) ; } } ) ) ; return width > minWidth ; } function calculateEdgePositionInner ( edge , source , target , scale , groupWidth , pointInGroup , keepOrder ) { let x1 , y1 , x2 , y2 ; if ( edge . source < edge . target ) { [ x1 , y1 , x2 , y2 ] = calculateLinePosition ( source ?. x ?? 0 , source ?. y ?? 0 , target ?. x ?? 0 , target ?. y ?? 0 , scale , groupWidth , pointInGroup , keepOrder ) ; } else { [ x2 , y2 , x1 , y1 ] = calculateLinePosition ( target ?. x ?? 0 , target ?. y ?? 0 , source ?. x ?? 0 , source ?. y ?? 0 , scale , groupWidth , pointInGroup , keepOrder ) ; } return { x1 , y1 , x2 , y2 } ; } function calculateLinePosition ( x1 , y1 , x2 , y2 , scale , groupWidth , pointInGroup , keepOrder ) { const dx = x2 - x1 ; const dy = y2 - y1 ; let diff = ( groupWidth / 2 - pointInGroup ) * scale ; if ( diff !== 0 && keepOrder !== "clock" ) { const radian = Math . atan2 ( y2 - y1 , x2 - x1 ) ; if ( keepOrder === "vertical" ) { const perpendicular = Math . PI / 2 ; if ( radian < - perpendicular || radian >= perpendicular ) { diff *= - 1 ; } } else if ( keepOrder === "horizontal" ) { if ( radian < 0 ) { diff *= - 1 ; } } } if ( dx === 0 ) { const sign = dy < 0 ? - 1 : 1 ; return [ x1 + diff * sign , y1 , x2 + diff * sign , y2 ] ; } else if ( dy === 0 ) { const sign = dx < 0 ? 1 : - 1 ; return [ x1 , y1 + diff * sign , x2 , y2 + diff * sign ] ; } else { const slope = dy / dx ; const moveSlope = - 1 / slope ; if ( dy < 0 ) { diff = - diff ; } const diffX = diff / Math . sqrt ( 1 + Math . pow ( moveSlope , 2 ) ) ; return [ x1 + diffX , y1 + diffX * moveSlope , x2 + diffX , y2 + diffX * moveSlope ] ; } }