import { vec2 } from '@antv/matrix-util' ; import { catmullRom2Bezier } from '@antv/path-util' ; import { IPoint } from '../types' ; param { String } str param { Object } o const substitute = ( str , o ) : string => { if ( ! str || ! o ) { return str ; } return str . replace ( /\\?\{([^{}]+)\}/g , ( match , name ) => { if ( match . charAt ( 0 ) === '\\' ) { return match . slice ( 1 ) ; } let res = o [ name ] ; if ( res === 0 ) res = '0' ; return res || '' ; } ) ; } ; param points export const getSpline = ( points ) => { const data = [ ] ; if ( points . length < 2 ) { throw new Error ( `point length must largn than 2, now it's ${ points . length }` ) ; } for ( const point of points ) { const { x , y } = point ; data . push ( x ) ; data . push ( y ) ; } const spliePath = catmullRom2Bezier ( data ) ; spliePath . unshift ( [ 'M' , points [ 0 ] . x , points [ 0 ] . y ] ) ; return spliePath ; } ; param { IPoint } startPoint param { IPoint } endPoint param { Number } percent param { Number } offset return { IPoint } export const getControlPoint = ( startPoint , endPoint , percent = 0 , offset = 0 ) : IPoint => { const point = { x : ( 1 - percent ) * startPoint . x + percent * endPoint . x , y : ( 1 - percent ) * startPoint . y + percent * endPoint . y , } ; let tangent = [ 0 , 0 ] ; vec2 . normalize ( tangent , [ endPoint . x - startPoint . x , endPoint . y - startPoint . y ] ) ; if ( ! tangent || ( ! tangent [ 0 ] && ! tangent [ 1 ] ) ) { tangent = [ 0 , 0 ] ; } const perpendicular = [ - tangent [ 1 ] * offset , tangent [ 0 ] * offset ] ; point . x += perpendicular [ 0 ] ; point . y += perpendicular [ 1 ] ; return point ; } ; param { Array } points param { Boolen } z return { Array } export const pointsToPolygon = ( points , z ? ) : string => { const { length } = points ; if ( ! length ) { return '' ; } let path = '' ; let str = '' ; for ( let i = 0 ; i < length ; i ++ ) { const item = points [ i ] ; if ( i === 0 ) { str = 'M{x} {y}' ; } else { str = 'L{x} {y}' ; } path += substitute ( str , item ) ; } if ( z ) { path += 'Z' ; } return path ; } ; export const pathToPoints = ( path ) => { const points = [ ] ; path . forEach ( ( seg ) => { const command = seg [ 0 ] ; if ( command !== 'A' ) { for ( let i = 1 ; i < seg . length ; i = i + 2 ) { points . push ( [ seg [ i ] , seg [ i + 1 ] ] ) ; } } else { const length = seg . length ; points . push ( [ seg [ length - 2 ] , seg [ length - 1 ] ] ) ; } } ) ; return points ; } ; param points export const getClosedSpline = ( points ) => { if ( points . length < 2 ) { throw new Error ( `point length must largn than 2, now it's ${ points . length }` ) ; } const first = points [ 0 ] ; const second = points [ 1 ] ; const last = points [ points . length - 1 ] ; const lastSecond = points [ points . length - 2 ] ; points . unshift ( last ) ; points . unshift ( lastSecond ) ; points . push ( first ) ; points . push ( second ) ; const closedPath = [ ] ; for ( let i = 1 ; i < points . length - 2 ; i += 1 ) { const x0 = points [ i - 1 ] . x ; const y0 = points [ i - 1 ] . y ; const x1 = points [ i ] . x ; const y1 = points [ i ] . y ; const x2 = points [ i + 1 ] . x ; const y2 = points [ i + 1 ] . y ; const x3 = i !== points . length - 2 ? points [ i + 2 ] . x : x2 ; const y3 = i !== points . length - 2 ? points [ i + 2 ] . y : y2 ; const cp1x = x1 + ( x2 - x0 ) / 6 ; const cp1y = y1 + ( y2 - y0 ) / 6 ; const cp2x = x2 - ( x3 - x1 ) / 6 ; const cp2y = y2 - ( y3 - y1 ) / 6 ; closedPath . push ( [ 'C' , cp1x , cp1y , cp2x , cp2y , x2 , y2 ] ) ; } closedPath . unshift ( [ 'M' , last . x , last . y ] ) ; return closedPath ; } ; const vecScaleTo = ( v , length ) => { return vec2 . scale ( [ 0 , 0 ] , vec2 . normalize ( [ 0 , 0 ] , v ) , length ) ; } ; const unitNormal = ( p0 , p1 ) : vec2 => { const n = [ p0 [ 1 ] - p1 [ 1 ] , p1 [ 0 ] - p0 [ 0 ] ] ; const nLength = Math . sqrt ( n [ 0 ] * n [ 0 ] + n [ 1 ] * n [ 1 ] ) ; if ( nLength === 0 ) { throw new Error ( 'p0 should not be equal to p1' ) ; } return [ n [ 0 ] / nLength , n [ 1 ] / nLength ] ; } ; const vecFrom = ( p0 , p1 ) : vec2 => { return [ p1 [ 0 ] - p0 [ 0 ] , p1 [ 1 ] - p0 [ 1 ] ] as vec2 ; } ; param polyPoints param padding export function roundedHull ( polyPoints , padding ) { const roundedHull1 = ( points ) => { const p1 = [ points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] - padding ] ; const p2 = [ points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] + padding ] ; return `M ${ p1 } A ${ padding },${ padding },0,0,0,${ p2 } A ${ padding },${ padding },0,0,0,${ p1 }` ; } ; const roundedHull2 = ( points ) => { const offsetVector = vec2 . scale ( [ 0 , 0 ] , unitNormal ( points [ 0 ] , points [ 1 ] ) , padding ) ; const invOffsetVector = vec2 . scale ( [ 0 , 0 ] , offsetVector , - 1 ) ; const p0 = vec2 . add ( [ 0 , 0 ] , points [ 0 ] , offsetVector ) ; const p1 = vec2 . add ( [ 0 , 0 ] , points [ 1 ] , offsetVector ) ; const p2 = vec2 . add ( [ 0 , 0 ] , points [ 1 ] , invOffsetVector ) ; const p3 = vec2 . add ( [ 0 , 0 ] , points [ 0 ] , invOffsetVector ) ; return `M ${ p0 } L ${ p1 } A ${ [ padding , padding , '0,0,0' , p2 ] . join ( ',' ) } L ${ p3 } A ${ [ padding , padding , '0,0,0' , p0 , ] . join ( ',' ) }` ; } ; if ( ! polyPoints || polyPoints . length < 1 ) return '' ; if ( polyPoints . length === 1 ) return roundedHull1 ( polyPoints ) ; if ( polyPoints . length === 2 ) return roundedHull2 ( polyPoints ) ; let segments = new Array ( polyPoints . length ) ; for ( let segmentIndex = 0 ; segmentIndex < segments . length ; ++ segmentIndex ) { const p0 = segmentIndex === 0 ? polyPoints [ polyPoints . length - 1 ] : polyPoints [ segmentIndex - 1 ] ; const p1 = polyPoints [ segmentIndex ] ; const offset = vec2 . scale ( [ 0 , 0 ] , unitNormal ( p0 , p1 ) , padding ) ; segments [ segmentIndex ] = [ vec2 . add ( [ 0 , 0 ] , p0 , offset ) , vec2 . add ( [ 0 , 0 ] , p1 , offset ) ] ; } const arcData = `A ${ [ padding , padding , '0,0,0,' ] . join ( ',' ) }` ; segments = segments . map ( ( segment , index ) => { let pathFragment = '' ; if ( index === 0 ) { pathFragment = `M ${ segments [ segments . length - 1 ] [ 1 ] } ` ; } pathFragment += `${ arcData + segment [ 0 ] } L ${ segment [ 1 ] }` ; return pathFragment ; } ) ; return segments . join ( ' ' ) ; } param polyPoints param padding export function paddedHull ( polyPoints , padding ) { const pointCount = polyPoints . length ; const smoothHull1 = ( points ) => { const p1 = [ points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] - padding ] ; const p2 = [ points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] + padding ] ; return `M ${ p1 } A ${ [ padding , padding , '0,0,0' , p2 ] . join ( ',' ) } A ${ [ padding , padding , '0,0,0' , p1 , ] . join ( ',' ) }` ; } ; const smoothHull2 = ( points ) => { const v = vecFrom ( points [ 0 ] , points [ 1 ] ) ; const extensionVec = vecScaleTo ( v , padding ) ; const extension0 = vec2 . add ( [ 0 , 0 ] , points [ 0 ] , vec2 . scale ( [ 0 , 0 ] , extensionVec , - 1 ) ) ; const extension1 = vec2 . add ( [ 0 , 0 ] , points [ 1 ] , extensionVec ) ; const tangentHalfLength = 1.2 * padding ; const controlDelta = vecScaleTo ( vec2 . normalize ( [ 0 , 0 ] , v ) , tangentHalfLength ) ; const invControlDelta = vec2 . scale ( [ 0 , 0 ] , controlDelta , - 1 ) ; const control0 = vec2 . add ( [ 0 , 0 ] , extension0 , invControlDelta ) ; const control1 = vec2 . add ( [ 0 , 0 ] , extension1 , invControlDelta ) ; const control3 = vec2 . add ( [ 0 , 0 ] , extension0 , controlDelta ) ; return `M ${ extension0 } C ${ [ control0 , control1 , extension1 ] . join ( ',' ) } S ${ [ control3 , extension0 , ] . join ( ',' ) } Z` ; } ; if ( ! polyPoints || pointCount < 1 ) return '' ; if ( pointCount === 1 ) return smoothHull1 ( polyPoints ) ; if ( pointCount === 2 ) return smoothHull2 ( polyPoints ) ; const hullPoints = polyPoints . map ( ( point , index ) => { const pNext = polyPoints [ ( index + 1 ) % pointCount ] ; return { p : point , v : vec2 . normalize ( [ 0 , 0 ] , vecFrom ( point , pNext ) ) , } ; } ) ; for ( let i = 0 ; i < hullPoints . length ; ++ i ) { const priorIndex = i > 0 ? i - 1 : pointCount - 1 ; const extensionVec = vec2 . normalize ( [ 0 , 0 ] , vec2 . add ( [ 0 , 0 ] , hullPoints [ priorIndex ] . v , vec2 . scale ( [ 0 , 0 ] , hullPoints [ i ] . v , - 1 ) ) ) ; hullPoints [ i ] . p = vec2 . add ( [ 0 , 0 ] , hullPoints [ i ] . p as vec2 , vec2 . scale ( [ 0 , 0 ] , extensionVec , padding ) ) ; } return hullPoints . map ( ( obj ) => { const point = obj . p ; return { x : point [ 0 ] , y : point [ 1 ] } ; } ) ; }