import { Color } from '../../../common/colors' ; import { Scale } from '../../../scales' ; import { ScaleType } from '../../../scales/constants' ; import { TextMeasure } from '../../../utils/bbox/canvas_text_bbox_calculator' ; import { clamp , mergePartial } from '../../../utils/common' ; import { Dimensions } from '../../../utils/dimensions' ; import { BandedAccessorType , BarGeometry } from '../../../utils/geometry' ; import { BarSeriesStyle , DisplayValueStyle } from '../../../utils/themes/theme' ; import { IndexedGeometryMap } from '../utils/indexed_geometry_map' ; import { DataSeries , DataSeriesDatum , XYChartSeriesIdentifier } from '../utils/series' ; import { BarStyleAccessor , DisplayValueSpec , LabelOverflowConstraint , StackMode } from '../utils/specs' ; import { getDatumYValue } from './points' ; const PADDING = 1 ; const FONT_SIZE_FACTOR = 0.7 ; type BarTuple = { barGeometries : BarGeometry [ ] ; indexedGeometryMap : IndexedGeometryMap ; } ; internal export function renderBars ( measureText , orderIndex , dataSeries , xScale , yScale , panel , chartRotation , minBarHeight , color , sharedSeriesStyle , displayValueSettings ? , styleAccessor ? , stackMode ? ) { const { fontSize , fontFamily } = sharedSeriesStyle . displayValue ; const initialBarTuple = { barGeometries : [ ] , indexedGeometryMap : new IndexedGeometryMap ( ) } as BarTuple ; const isLogY = yScale . type === ScaleType . Log ; const isInvertedY = yScale . isInverted ; return dataSeries . data . reduce ( ( barTuple , datum ) => { const xScaled = xScale . scale ( datum . x ) ; if ( ! xScale . isValueInDomain ( datum . x ) || Number . isNaN ( xScaled ) ) { return barTuple ; } const { barGeometries , indexedGeometryMap } = barTuple ; const { y0 , y1 , initialY1 , filled } = datum ; const rawY = isLogY && ( y1 === 0 || y1 === null ) ? yScale . range [ 0 ] : yScale . scale ( y1 ) ; const y0Scaled = isLogY ? y0 === 0 || y0 === null ? yScale . range [ isInvertedY ? 1 : 0 ] : yScale . scale ( y0 ) : yScale . scale ( y0 === null ? 0 : y0 ) ; const finiteHeight = y0Scaled - rawY || 0 ; const absHeight = Math . abs ( finiteHeight ) ; const height = absHeight === 0 ? absHeight : Math . max ( minBarHeight , absHeight ) ; const heightExtension = height - absHeight ; const isUpsideDown = finiteHeight < 0 ; const finiteY = Number . isNaN ( y0Scaled + rawY ) ? 0 : rawY ; const y = isUpsideDown ? finiteY - height + heightExtension : finiteY - heightExtension ; const seriesIdentifier = { key : dataSeries . key , specId : dataSeries . specId , yAccessor : dataSeries . yAccessor , splitAccessors : dataSeries . splitAccessors , seriesKeys : dataSeries . seriesKeys , smHorizontalAccessorValue : dataSeries . smHorizontalAccessorValue , smVerticalAccessorValue : dataSeries . smVerticalAccessorValue , } ; const seriesStyle = getBarStyleOverrides ( datum , seriesIdentifier , sharedSeriesStyle , styleAccessor ) ; const maxPixelWidth = clamp ( seriesStyle . rect . widthRatio ?? 1 , 0 , 1 ) * xScale . bandwidth ; const minPixelWidth = clamp ( seriesStyle . rect . widthPixel ?? 0 , 0 , maxPixelWidth ) ; const width = clamp ( seriesStyle . rect . widthPixel ?? xScale . bandwidth , minPixelWidth , maxPixelWidth ) ; const x = xScaled + xScale . bandwidth * orderIndex + xScale . bandwidth / 2 - width / 2 ; const y1Value = getDatumYValue ( datum , false , false , stackMode ) ; const formattedDisplayValue = displayValueSettings ?. valueFormatter ?. ( y1Value ) ; const displayValueText = displayValueSettings ?. isAlternatingValueLabel && barGeometries . length % 2 ? undefined : formattedDisplayValue ; const { displayValueWidth , fixedFontScale } = computeBoxWidth ( displayValueText ?? '' , { padding : PADDING , fontSize , fontFamily , measureText , width } , displayValueSettings ) ; const isHorizontalRotation = chartRotation % 180 === 0 ; const referenceWidth = Math . max ( isHorizontalRotation ? displayValueWidth : fixedFontScale , 1 ) ; const textScalingFactor = getFinalFontScalingFactor ( ( width * FONT_SIZE_FACTOR ) / referenceWidth , fixedFontScale , fontSize ) ; const overflowConstraints = new Set ( displayValueSettings ?. overflowConstraints ?? [ LabelOverflowConstraint . ChartEdges , LabelOverflowConstraint . BarGeometry , ] ) ; const bboxWidthFactor = isHorizontalRotation ? textScalingFactor : 1 ; const displayValue = displayValueText && displayValueSettings ?. showValueLabel ? { fontScale : textScalingFactor , fontSize : fixedFontScale , text : displayValueText , width : bboxWidthFactor * displayValueWidth , height : textScalingFactor * fixedFontScale , overflowConstraints , isValueContainedInElement : displayValueSettings ?. isValueContainedInElement ?? false , } : undefined ; const barGeometry = { displayValue , x , y , transform : { x : 0 , y : 0 } , width , height , color , value : { x : datum . x , y : y1Value , mark : null , accessor : BandedAccessorType . Y1 , datum : datum . datum } , seriesIdentifier , seriesStyle , panel , } ; indexedGeometryMap . set ( barGeometry ) ; if ( y1 !== null && initialY1 !== null && filled ?. y1 === undefined ) { barGeometries . push ( barGeometry ) ; } return barTuple ; } , initialBarTuple ) ; } internal function computeBoxWidth ( text , { padding , fontSize , fontFamily , measureText , width , } , displayValueSettings ) { const fixedFontScale = Math . max ( typeof fontSize === 'number' ? fontSize : fontSize . min , 1 ) ; const computedDisplayValueWidth = measureText ( text , { fontFamily , fontWeight : 'normal' , fontStyle : 'normal' , fontVariant : 'normal' } , fixedFontScale ) . width ; if ( typeof fontSize !== 'number' ) { return { fixedFontScale , displayValueWidth : computedDisplayValueWidth + padding , } ; } return { fixedFontScale , displayValueWidth : displayValueSettings && displayValueSettings . isValueContainedInElement ? width : computedDisplayValueWidth , } ; } internal function getFinalFontScalingFactor ( scale , fixedFontSize , limits ) { if ( typeof limits === 'number' ) { return 1 ; } const finalFontSize = scale * fixedFontSize ; if ( finalFontSize > limits . max ) { return limits . max / fixedFontSize ; } if ( finalFontSize < limits . min ) { return limits . min / fixedFontSize ; } return scale ; } internal export function getBarStyleOverrides ( datum , seriesIdentifier , seriesStyle , styleAccessor ? ) { const styleOverride = styleAccessor && styleAccessor ( datum , seriesIdentifier ) ; if ( ! styleOverride ) { return seriesStyle ; } if ( typeof styleOverride === 'string' ) { return { ... seriesStyle , rect : { ... seriesStyle . rect , fill : styleOverride } } ; } return mergePartial ( seriesStyle , styleOverride ) ; }