import { AbstractCanvas } from '@antv/g-base' ; import { Point , IGroup } from '@antv/g-base' ; import { isNumber , isString } from '@antv/util' ; import { modifyCSS } from '@antv/dom-util' ; import { Item , Matrix , Padding , GraphAnimateConfig , IEdge , FitViewRules } from '../../types' ; import { formatPadding } from '../../util/base' ; import { applyMatrix , invertMatrix } from '../../util/math' ; import { IAbstractGraph } from '../../interface/graph' ; export default class ViewController { private graph : IAbstractGraph ; public destroyed : boolean = false ; constructor ( graph ) { this . graph = graph ; this . destroyed = false ; } private getViewCenter ( ) { const padding = this . getFormatPadding ( ) ; const { graph } = this ; const width = this . graph . get ( 'width' ) ; const height = graph . get ( 'height' ) ; return { x : ( width - padding [ 1 ] - padding [ 3 ] ) / 2 + padding [ 3 ] , y : ( height - padding [ 0 ] - padding [ 2 ] ) / 2 + padding [ 0 ] , } ; } public fitCenter ( ) { const { graph } = this ; const group = graph . get ( 'group' ) ; group . resetMatrix ( ) ; const bbox = group . getCanvasBBox ( ) ; if ( bbox . width === 0 || bbox . height === 0 ) return ; const viewCenter = this . getViewCenter ( ) ; const groupCenter = { x : bbox . x + bbox . width / 2 , y : bbox . y + bbox . height / 2 , } ; graph . translate ( viewCenter . x - groupCenter . x , viewCenter . y - groupCenter . y ) ; } public fitView ( ) { const { graph } = this ; const padding = this . getFormatPadding ( ) ; const width = graph . get ( 'width' ) ; const height = graph . get ( 'height' ) ; const group = graph . get ( 'group' ) ; group . resetMatrix ( ) ; const bbox = group . getCanvasBBox ( ) ; if ( bbox . width === 0 || bbox . height === 0 ) return ; const viewCenter = this . getViewCenter ( ) ; const groupCenter = { x : bbox . x + bbox . width / 2 , y : bbox . y + bbox . height / 2 , } ; graph . translate ( viewCenter . x - groupCenter . x , viewCenter . y - groupCenter . y ) ; const w = ( width - padding [ 1 ] - padding [ 3 ] ) / bbox . width ; const h = ( height - padding [ 0 ] - padding [ 2 ] ) / bbox . height ; let ratio = w ; if ( w > h ) { ratio = h ; } if ( ! graph . zoom ( ratio , viewCenter ) ) { console . warn ( 'zoom failed, ratio out of range, ratio: %f' , ratio ) ; } } public fitViewByRules ( rules ) { const { onlyOutOfViewPort = false , direction = 'both' , ratioRule = 'min' } = rules ; const { graph } = this ; const padding = this . getFormatPadding ( ) ; const width = graph . get ( 'width' ) ; const height = graph . get ( 'height' ) ; const group = graph . get ( 'group' ) ; group . resetMatrix ( ) ; const bbox = group . getCanvasBBox ( ) ; if ( bbox . width === 0 || bbox . height === 0 ) return ; const viewCenter = this . getViewCenter ( ) ; const groupCenter = { x : bbox . x + bbox . width / 2 , y : bbox . y + bbox . height / 2 , } ; graph . translate ( viewCenter . x - groupCenter . x , viewCenter . y - groupCenter . y ) ; const wRatio = ( width - padding [ 1 ] - padding [ 3 ] ) / bbox . width ; const hRatio = ( height - padding [ 0 ] - padding [ 2 ] ) / bbox . height ; let ratio ; if ( direction === 'x' ) { ratio = wRatio ; } else if ( direction === 'y' ) { ratio = hRatio ; } else { ratio = ratioRule === 'max' ? Math . max ( wRatio , hRatio ) : Math . min ( wRatio , hRatio ) ; } if ( onlyOutOfViewPort ) { ratio = ratio < 1 ? ratio : 1 ; } const initZoomRatio = graph . getZoom ( ) ; let endZoom = initZoomRatio * ratio ; const minZoom = graph . get ( 'minZoom' ) ; if ( endZoom < minZoom ) { endZoom = minZoom ; console . warn ( 'fitview failed, ratio out of range, ratio: %f' , ratio , 'graph minzoom has been used instead' ) ; } graph . zoomTo ( endZoom , viewCenter ) ; } public getFormatPadding ( ) { const padding = this . graph . get ( 'fitViewPadding' ) as Padding ; return formatPadding ( padding ) ; } public focusPoint ( point , animate ? , animateCfg ? ) { const viewCenter = this . getViewCenter ( ) ; const modelCenter = this . getPointByCanvas ( viewCenter . x , viewCenter . y ) ; let viewportMatrix = this . graph . get ( 'group' ) . getMatrix ( ) ; if ( ! viewportMatrix ) viewportMatrix = [ 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 ] ; if ( animate ) { const dx = ( modelCenter . x - point . x ) * viewportMatrix [ 0 ] ; const dy = ( modelCenter . y - point . y ) * viewportMatrix [ 4 ] ; let lastX = 0 ; let lastY = 0 ; let newX = 0 ; let newY = 0 ; this . graph . get ( 'canvas' ) . animate ( ( ratio ) => { newX = dx * ratio ; newY = dy * ratio ; this . graph . translate ( newX - lastX , newY - lastY ) ; lastX = newX ; lastY = newY ; } , { ... animateCfg , } ) ; } else { this . graph . translate ( ( modelCenter . x - point . x ) * viewportMatrix [ 0 ] , ( modelCenter . y - point . y ) * viewportMatrix [ 4 ] ) ; } } param canvasX param canvasY public getPointByCanvas ( canvasX , canvasY ) { let viewportMatrix = this . graph . get ( 'group' ) . getMatrix ( ) ; if ( ! viewportMatrix ) { viewportMatrix = [ 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 ] ; } const point = invertMatrix ( { x : canvasX , y : canvasY } , viewportMatrix ) ; return point ; } param clientX param clientY public getPointByClient ( clientX , clientY ) { const canvas = this . graph . get ( 'canvas' ) ; const canvasPoint = canvas . getPointByClient ( clientX , clientY ) ; return this . getPointByCanvas ( canvasPoint . x , canvasPoint . y ) ; } param x param y public getClientByPoint ( x , y ) { const canvas = this . graph . get ( 'canvas' ) ; const canvasPoint = this . getCanvasByPoint ( x , y ) ; const point = canvas . getClientByPoint ( canvasPoint . x , canvasPoint . y ) ; return { x : point . x , y : point . y } ; } param x param y public getCanvasByPoint ( x , y ) { let viewportMatrix = this . graph . get ( 'group' ) . getMatrix ( ) ; if ( ! viewportMatrix ) { viewportMatrix = [ 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 ] ; } return applyMatrix ( { x , y } , viewportMatrix ) ; } param item param { boolean } animate param { GraphAnimateConfig } animateCfg public focus ( item , animate ? , animateCfg ? ) { if ( isString ( item ) ) { item = this . graph . findById ( item ) ; } if ( item ) { let x = 0 , y = 0 ; if ( item . getType && item . getType ( ) === 'edge' ) { const sourceMatrix = ( item as IEdge ) . getSource ( ) . get ( 'group' ) . getMatrix ( ) ; const targetMatrix = ( item as IEdge ) . getTarget ( ) . get ( 'group' ) . getMatrix ( ) ; if ( sourceMatrix && targetMatrix ) { x = ( sourceMatrix [ 6 ] + targetMatrix [ 6 ] ) / 2 ; y = ( sourceMatrix [ 7 ] + targetMatrix [ 7 ] ) / 2 ; } else if ( sourceMatrix || targetMatrix ) { x = sourceMatrix ? sourceMatrix [ 6 ] : targetMatrix [ 6 ] ; y = sourceMatrix ? sourceMatrix [ 7 ] : targetMatrix [ 7 ] ; } } else { const group = item . get ( 'group' ) ; let matrix = group . getMatrix ( ) ; if ( ! matrix ) matrix = [ 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 ] ; x = matrix [ 6 ] ; y = matrix [ 7 ] ; } this . focusPoint ( { x , y } , animate , animateCfg ) ; } } param width param height public changeSize ( width , height ) { const { graph } = this ; if ( ! isNumber ( width ) || ! isNumber ( height ) ) { throw Error ( 'invalid canvas width & height, please make sure width & height type is number' ) ; } graph . set ( { width , height } ) ; const canvas = graph . get ( 'canvas' ) ; canvas . changeSize ( width , height ) ; const plugins = graph . get ( 'plugins' ) ; plugins . forEach ( ( plugin ) => { if ( plugin . get ( 'gridContainer' ) ) { plugin . positionInit ( ) ; } } ) ; } public destroy ( ) { ( this . graph as IAbstractGraph | null ) = null ; this . destroyed = false ; } }