import { symbol , symbolCircle , line , curveMonotoneX , curveMonotoneY , area , arc , pie , } from 'd3-shape' ; import { SeriesList , Series , PointList , Point , DataItems , AddSeriesFn , ScalesCache , ScaleSeriesPointsFn , GetPointTransformerFn , Colors , Rect , BarSeries , ScatterSeries , PieSeries , PointComponentProps , PathFn , PathStartCoordinates , PathEndCoordinates , Scales , } from '../../types' ; import { Size } from '@devexpress/dx-react-core' ; import { ARGUMENT_DOMAIN } from '../../constants' ; import { getValueDomainName , getWidth , rangesEqual } from '../../utils/scale' ; const getArg = ( { arg } ) => arg ; const getVal = ( { val } ) => val ; const getStartVal = ( { startVal } ) => startVal ! ; internal export const dArea = area < PointComponentProps > ( ) . x ( getArg ) . y1 ( getVal ) . y0 ( getStartVal ) as any ; internal export const dRotateArea = area < PointComponentProps > ( ) . x1 ( getStartVal ) . x0 ( getVal ) . y ( getArg ) as any ; internal export const dLine = line < PointComponentProps > ( ) . x ( getArg ) . y ( getVal ) as any ; internal export const dRotateLine = line < PointComponentProps > ( ) . x ( getVal ) . y ( getArg ) as any ; internal export const dSpline = line < PointComponentProps > ( ) . x ( getArg ) . y ( getVal ) . curve ( curveMonotoneX ) as any ; internal export const dRotateSpline = line < PointComponentProps > ( ) . x ( getVal ) . y ( getArg ) . curve ( curveMonotoneY ) as any ; internal export const dBar = ( arg , val , startVal , width , rotated ) => { const height = Math . abs ( val - startVal ! ) ; const minVal = Math . min ( val , startVal ! ) ; return { x : rotated ? minVal : arg - width / 2 , y : rotated ? arg - width / 2 : minVal , width : rotated ? height : width || 2 , height : rotated ? width || 2 : height , } ; } ; internal export const getPiePointTransformer = ( { argumentScale , valueScale , points , } ) => { const x = Math . max ( ... argumentScale . range ( ) ) / 2 ; const y = Math . max ( ... valueScale . range ( ) ) / 2 ; const maxRadius = Math . min ( x , y ) ; const pieData = pie < Point > ( ) . sort ( null ) . value ( d => d . value ) ( points as Point [ ] ) ; return ( point ) => { const { startAngle , endAngle } = pieData [ point . index ] ; return { ... point , arg : x , val : y , startAngle , endAngle , maxRadius , } ; } ; } ; internal export const getLinePointTransformer = ( { argumentScale , valueScale , } ) => point => ( { ... point , arg : argumentScale ( point . argument ) , val : valueScale ( point . value ) , } as any ) ; internal export const getScatterPointTransformer = ( ... args ) => getLinePointTransformer ( ... args ) ; internal export const getAreaPointTransformer = ( series ) => { const transform = getLinePointTransformer ( series ) ; const startVal = series . valueScale ( 0 ) ; return ( point ) => { const ret = transform ( point ) ; return { ... ret , startVal , } ; } ; } ; getAreaPointTransformer . isStartedFromZero = true ; internal export const getBarPointTransformer = ( { argumentScale , valueScale , } ) => { const startVal = valueScale ( 0 ) ; return point => ( { ... point , arg : argumentScale ( point . argument ) , val : valueScale ( point . value ) , startVal , maxBarWidth : getWidth ( argumentScale ) , } as any ) ; } ; getBarPointTransformer . isStartedFromZero = true ; getBarPointTransformer . isBroad = true ; getPiePointTransformer . getPointColor = ( palette , index ) => palette [ index % palette . length ] ; internal export const findSeriesByName = ( name , series ) : Series => series . find ( seriesItem => seriesItem . symbolName === name ) as Series ; internal export const dSymbol = ( { size } ) => symbol ( ) . size ( size ** 2 ) . type ( symbolCircle ) ( ) ! ; internal export const dPie = ( maxRadius , innerRadius , outerRadius , startAngle , endAngle ) => arc ( ) ( { startAngle , endAngle , innerRadius : innerRadius * maxRadius , outerRadius : outerRadius * maxRadius , } ) ! ; const getRect = ( cArg , cVal , dArg , dVal , rotated ) : Rect => { const minArg = cArg - dArg ; const minVal = cVal - dVal ; const maxArg = cArg + dArg ; const maxVal = cVal + dVal ; return rotated ? [ minVal , minArg , maxVal , maxArg ] : [ minArg , minVal , maxArg , maxVal ] ; } ; getBarPointTransformer . getTargetElement = ( point ) => { const { arg , val , startVal , barWidth , maxBarWidth , rotated , } = point as BarSeries . PointProps ; const halfWidth = barWidth * maxBarWidth / 2 ; const halfHeight = Math . abs ( startVal ! - val ) / 2 ; const centerVal = ( val + startVal ! ) / 2 ; return getRect ( arg , centerVal , halfWidth , halfHeight , rotated ) ; } ; getPiePointTransformer . getTargetElement = ( point ) => { const { arg : x , val : y , innerRadius , outerRadius , maxRadius , startAngle , endAngle , } = point as PieSeries . PointProps ; const center = arc ( ) . centroid ( { startAngle , endAngle , innerRadius : innerRadius * maxRadius , outerRadius : outerRadius * maxRadius , } ) ; const cx = center [ 0 ] + x ; const cy = center [ 1 ] + y ; return getRect ( cx , cy , 0.5 , 0.5 , false ) ; } ; getAreaPointTransformer . getTargetElement = ( { arg , val , rotated } ) => ( getRect ( arg , val , 1 , 1 , rotated ) ) ; getLinePointTransformer . getTargetElement = getAreaPointTransformer . getTargetElement ; getScatterPointTransformer . getTargetElement = ( obj ) => { const { arg , val , point , rotated } = obj as ScatterSeries . PointProps ; const t = point . size / 2 ; return getRect ( arg , val , t , t , rotated ) ; } ; const getUniqueName = ( list , name ) => { const names = new Set ( list . map ( item => item . name ) ) ; let ret = name ; while ( names . has ( ret ) ) { ret = ret . replace ( /\d*$/ , str => ( str ? + str + 1 : 0 ) ) ; } return ret ; } ; const createPoints = ( { argumentField , valueField , getPointTransformer , } , data , props , palette ) : PointList => { const points = [ ] ; data . forEach ( ( dataItem , index ) => { const argument = dataItem [ argumentField ] ; const value = dataItem [ valueField ] ; if ( argument !== undefined && value !== undefined ) { points . push ( { argument , value , index , ... props , color : getPointTransformer . getPointColor ? getPointTransformer . getPointColor ( palette , index ) : props . color , } ) ; } } ) ; return points ; } ; internal export const addSeries = ( series , data , palette , props , restProps ) => { const index = series . length ; const seriesColor = props . color || palette [ index % palette . length ] ; return [ ... series , { ... props , index , name : getUniqueName ( series , props . name ) , points : createPoints ( props , data , { ... restProps , color : seriesColor } , palette ) , color : seriesColor , } ] ; } ; const scalePoints = ( series , scales , rotated ) => { const transform = series . getPointTransformer ( { ... series , argumentScale : scales [ ARGUMENT_DOMAIN ] , valueScale : scales [ getValueDomainName ( series . scaleName ) ] , } ) ; const ret = { ... series , rotated , points : series . points . map ( point => ( { ... transform ( point ) , rotated , } ) ) , } ; return ret ; } ; internal export const scaleSeriesPoints = ( series , scales , rotated ) => series . map ( seriesItem => scalePoints ( seriesItem , scales , rotated ) ) ; internal export const getVisibility = ( pane , centerX , centerY , width , height ) => { return ( centerX - width / 2 < 0 && centerX + width / 2 < 0 ) || ( centerX - width / 2 > pane . width && centerX + width / 2 > pane . width ) || ( centerY - height / 2 < 0 && centerY + height / 2 < 0 ) || ( centerY - height / 2 > pane . height && centerY + height / 2 > pane . height ) ? 'hidden' : 'visible' ; } ; internal export const adjustBarSize = ( bar , { width , height } ) => { const x = Math . max ( 0 , bar . x ) ; const y = Math . max ( 0 , bar . y ) ; return { x , y , width : Math . min ( width , bar . x + bar . width ) - x , height : Math . min ( height , bar . y + bar . height ) - y , } ; } ; internal export const isValuesChanged = ( previous , current ) : boolean => { return Object . entries ( previous ) . some ( el => el [ 1 ] !== current [ el [ 0 ] ] ) ; } ; internal export const isCoordinatesChanged = ( { coordinates : prevCoordinates } , { coordinates } ) => { if ( prevCoordinates . length !== coordinates . length ) { return true ; } return prevCoordinates . some ( ( el , index ) => { return el . arg !== coordinates [ index ] . arg || el . val !== coordinates [ index ] . val ; } ) ; } ; internal export const isScalesChanged = ( previous , current ) : boolean => { return ! rangesEqual ( previous . argScale . range ( ) , current . argScale . range ( ) ) || ! rangesEqual ( previous . valScale . range ( ) , current . valScale . range ( ) ) ; } ;