import { Scale , ScaleBand , ScaleContinuous } from '../../../../scales' ; import { isBandScale , isContinuousScale } from '../../../../scales/types' ; import { isDefined , Position , Rotation } from '../../../../utils/common' ; import { AxisId , GroupId } from '../../../../utils/ids' ; import { Point } from '../../../../utils/point' ; import { AxisStyle } from '../../../../utils/themes/theme' ; import { PrimitiveValue } from '../../../partition_chart/layout/utils/group_by_rollup' ; import { SmallMultipleScales } from '../../state/selectors/compute_small_multiple_scales' ; import { isHorizontalRotation , isVerticalRotation } from '../../state/utils/common' ; import { getAxesSpecForSpecId } from '../../state/utils/spec' ; import { getPanelSize } from '../../utils/panel' ; import { AxisSpec , RectAnnotationDatum , RectAnnotationSpec } from '../../utils/specs' ; import { Bounds } from '../types' ; import { AnnotationRectProps } from './types' ; internal export function isWithinRectBounds ( { x , y } , { startX , endX , startY , endY } ) { const withinXBounds = x >= startX && x <= endX ; const withinYBounds = y >= startY && y <= endY ; return withinXBounds && withinYBounds ; } internal export function computeRectAnnotationDimensions ( annotationSpec , yScales , xScale , axesSpecs , smallMultiplesScales , chartRotation , getAxisStyle , isHistogram = false ) { const { dataValues , groupId , outside } = annotationSpec ; const { xAxis , yAxis } = getAxesSpecForSpecId ( axesSpecs , groupId ) ; const yScale = yScales . get ( groupId ) ; const rectsProps = [ ] ; const panelSize = getPanelSize ( smallMultiplesScales ) ; dataValues . forEach ( ( datum ) => { const { x0 : initialX0 , x1 : initialX1 , y0 : initialY0 , y1 : initialY1 } = datum . coordinates ; if ( initialX0 === null && initialX1 === null && initialY0 === null && initialY1 === null ) { return ; } let height ; const [ x0 , x1 ] = limitValueToDomainRange ( xScale , initialX0 , initialX1 , isHistogram ) ; if ( x0 === null || x1 === null ) { return ; } let xAndWidth = null ; if ( isBandScale ( xScale ) ) { xAndWidth = scaleXonBandScale ( xScale , x0 , x1 ) ; } else if ( isContinuousScale ( xScale ) ) { xAndWidth = scaleXonContinuousScale ( xScale , x0 , x1 , isHistogram ) ; } if ( ! xAndWidth ) { return ; } if ( ! yScale ) { if ( ! isDefined ( initialY0 ) && ! isDefined ( initialY1 ) ) { const isLeftSide = ( chartRotation === 0 && xAxis ?. position === Position . Bottom ) || ( chartRotation === 180 && xAxis ?. position === Position . Top ) || ( chartRotation === - 90 && yAxis ?. position === Position . Right ) || ( chartRotation === 90 && yAxis ?. position === Position . Left ) ; const orthoDimension = isHorizontalRotation ( chartRotation ) ? panelSize . height : panelSize . width ; const outsideDim = annotationSpec . outsideDimension ?? getOutsideDimension ( getAxisStyle ( xAxis ?. id ?? yAxis ?. id ) ) ; const rectDimensions = { ... xAndWidth , ... ( outside ? { y : isLeftSide ? orthoDimension : - outsideDim , height : outsideDim , } : { y : 0 , height : orthoDimension , } ) , } ; rectsProps . push ( { rect : rectDimensions , datum , } ) ; } return ; } const [ y0 , y1 ] = limitValueToDomainRange ( yScale , initialY0 , initialY1 ) ; if ( ! Number . isFinite ( y0 ) || ! Number . isFinite ( y1 ) ) return ; let scaledY1 = yScale . pureScale ( y1 ) ; const scaledY0 = yScale . pureScale ( y0 ) ; if ( Number . isNaN ( scaledY1 ) || Number . isNaN ( scaledY0 ) ) return ; height = Math . abs ( scaledY0 - scaledY1 ) ; if ( height === 0 && yScale . domain . length === 2 && yScale . domain [ 0 ] === yScale . domain [ 1 ] ) { height = panelSize . height ; scaledY1 = 0 ; } const orthoDimension = isVerticalRotation ( chartRotation ) ? panelSize . height : panelSize . width ; const isLeftSide = ( chartRotation === 0 && yAxis ?. position === Position . Left ) || ( chartRotation === 180 && yAxis ?. position === Position . Right ) || ( chartRotation === - 90 && xAxis ?. position === Position . Bottom ) || ( chartRotation === 90 && xAxis ?. position === Position . Top ) ; const outsideDim = annotationSpec . outsideDimension ?? getOutsideDimension ( getAxisStyle ( xAxis ?. id ?? yAxis ?. id ) ) ; const rectDimensions = { ... ( ! isDefined ( initialX0 ) && ! isDefined ( initialX1 ) && outside ? { x : isLeftSide ? - outsideDim : orthoDimension , width : outsideDim , } : xAndWidth ) , y : scaledY1 , height , } ; rectsProps . push ( { rect : rectDimensions , datum , } ) ; } ) ; return rectsProps . reduce < AnnotationRectProps [ ] > ( ( acc , props ) => { const duplicated = [ ] ; smallMultiplesScales . vertical . domain . forEach ( ( vDomainValue ) => { smallMultiplesScales . horizontal . domain . forEach ( ( hDomainValue ) => { const top = smallMultiplesScales . vertical . scale ( vDomainValue ) ; const left = smallMultiplesScales . horizontal . scale ( hDomainValue ) ; if ( Number . isNaN ( top + left ) ) return ; const panel = { ... panelSize , top , left } ; duplicated . push ( { ... props , panel } ) ; } ) ; } ) ; return [ ... acc , ... duplicated ] ; } , [ ] ) ; } function scaleXonBandScale ( xScale , x0 , x1 ) { const padding = ( xScale . step - xScale . originalBandwidth ) / 2 ; let scaledX1 = xScale . scale ( x1 ) ; let scaledX0 = xScale . scale ( x0 ) ; if ( Number . isNaN ( scaledX1 + scaledX0 ) ) { return null ; } scaledX1 += xScale . originalBandwidth + padding ; if ( scaledX1 > xScale . range [ 1 ] ) { [ , scaledX1 ] = xScale . range ; } scaledX0 -= padding ; if ( scaledX0 < xScale . range [ 0 ] ) { [ scaledX0 ] = xScale . range ; } const width = Math . abs ( scaledX1 - scaledX0 ) ; return { x : scaledX0 , width , } ; } function scaleXonContinuousScale ( xScale , x0 , x1 , isHistogramModeEnabled = false ) { if ( typeof x1 !== 'number' || typeof x0 !== 'number' ) { return null ; } const scaledX0 = xScale . scale ( x0 ) ; const scaledX1 = xScale . totalBarsInCluster > 0 && ! isHistogramModeEnabled ? xScale . scale ( x1 + xScale . minInterval ) : xScale . scale ( x1 ) ; const width = Math . abs ( scaledX1 - scaledX0 ) ; return Number . isNaN ( width ) ? null : { width , x : scaledX0 - ( xScale . bandwidthPadding / 2 ) * xScale . totalBarsInCluster } ; } param scale param minValue param maxValue param isHistogram function limitValueToDomainRange ( scale , minValue ? , maxValue ? , isHistogram = false ) { const [ domainStartValue ] = scale . domain ; const domainEndValue = scale . domain . length > 0 ? scale . domain [ scale . domain . length - 1 ] : scale . domain [ 0 ] ; const min = maxOf ( domainStartValue , minValue ) ; const max = minOf ( isHistogram ? domainEndValue + scale . minInterval : domainEndValue , maxValue ) ; return isContinuousScale ( scale ) && min !== null && max !== null && min > max ? [ null , null ] : [ min , max ] ; } function minOf ( base , value ? ) { return typeof value === 'number' ? Math . min ( value , base ) : typeof value === 'string' ? value : base ; } function maxOf ( base , value ) { return typeof value === 'number' ? Math . max ( value , base ) : typeof value === 'string' ? value : base ; } function getOutsideDimension ( style ) { const { visible , size , strokeWidth } = style . tickLine ; return visible && size > 0 && strokeWidth > 0 ? size : 0 ; }