import { colorToRgba } from '../../../../common/color_library_wrappers' ; import { Color } from '../../../../common/colors' ; import { TAU } from '../../../../common/constants' ; import { fillTextColor } from '../../../../common/fill_text_color' ; import { Distance , meanAngle , Pixels , PointTuple , PointTuples , trueBearingToStandardPositionAngle , } from '../../../../common/geometry' ; import { cutToLength , fitText , Font , measureOneBoxWidth } from '../../../../common/text_utils' ; import { TextMeasure } from '../../../../utils/bbox/canvas_text_bbox_calculator' ; import { ColorVariant , isRTLString , ValueFormatter } from '../../../../utils/common' ; import { Logger } from '../../../../utils/logger' ; import { Point } from '../../../../utils/point' ; import { LinkLabelConfig , PartitionStyle } from '../../../../utils/themes/partition' ; import { BackgroundStyle } from '../../../../utils/themes/theme' ; import { LinkLabelVM , RawTextGetter , ShapeTreeNode , ValueGetterFunction } from '../types/viewmodel_types' ; internal export interface LinkLabelsViewModelSpec { linkLabels : LinkLabelVM [ ] ; labelFontSpec : Font ; valueFontSpec : Font ; strokeColor : Color ; } internal export function linkTextLayout ( rectWidth , rectHeight , measure , style , nodesWithoutRoom , currentY , anchorRadius , rawTextGetter , valueGetter , valueFormatter , maxTextLength , diskCenter , { color : backgroundColor , fallbackColor : fallbackBGColor } ) { const { linkLabel } = style ; const maxDepth = nodesWithoutRoom . reduce ( ( p , n ) => Math . max ( p , n . depth ) , 0 ) ; const yRelativeIncrement = Math . sin ( linkLabel . stemAngle ) * linkLabel . minimumStemLength ; const rowPitch = linkLabel . fontSize + linkLabel . spacing ; const linkLabels = nodesWithoutRoom . filter ( ( n ) => n . depth === maxDepth ) . sort ( ( n1 , n2 ) => Math . abs ( n2 . x0 - n2 . x1 ) - Math . abs ( n1 . x0 - n1 . x1 ) ) . slice ( 0 , linkLabel . maxCount ) . sort ( linkLabelCompare ) . map ( nodeToLinkLabel ( { linkLabel , anchorRadius , currentY , rowPitch , yRelativeIncrement , rawTextGetter , maxTextLength , valueFormatter , valueGetter , measure , rectWidth , rectHeight , diskCenter , } ) ) . filter ( ( { text } ) => text !== '' ) ; if ( colorToRgba ( backgroundColor ) [ 3 ] < 1 ) { Logger . expected ( 'Text contrast requires an opaque background color, using fallbackColor' , 'an opaque color' , backgroundColor ) ; } const textColor = linkLabel . textColor === ColorVariant . Adaptive ? fillTextColor ( fallbackBGColor , null , backgroundColor ) : linkLabel . textColor ; const labelFontSpec = { ... linkLabel , textColor } ; const valueFontSpec = { ... linkLabel , ... linkLabel . valueFont , textColor } ; return { linkLabels , valueFontSpec , labelFontSpec , strokeColor : textColor } ; } function linkLabelCompare ( n1 , n2 ) { const mid1 = meanAngle ( n1 . x0 , n1 . x1 ) ; const mid2 = meanAngle ( n2 . x0 , n2 . x1 ) ; const dist1 = Math . min ( Math . abs ( mid1 - TAU / 4 ) , Math . abs ( mid1 - ( 3 * TAU ) / 4 ) ) ; const dist2 = Math . min ( Math . abs ( mid2 - TAU / 4 ) , Math . abs ( mid2 - ( 3 * TAU ) / 4 ) ) ; return dist1 - dist2 ; } function nodeToLinkLabel ( { linkLabel , anchorRadius , currentY , rowPitch , yRelativeIncrement , rawTextGetter , maxTextLength , valueFormatter , valueGetter , measure , rectWidth , rectHeight , diskCenter , } ) { const labelFont = linkLabel ; const valueFont = { ... labelFont , ... linkLabel . valueFont } ; return function nodeToLinkLabelMap ( node ) : LinkLabelVM { const midAngle = trueBearingToStandardPositionAngle ( ( node . x0 + node . x1 ) / 2 ) ; const north = midAngle < TAU / 2 ? 1 : - 1 ; const rightSide = TAU / 4 < midAngle && midAngle < ( 3 * TAU ) / 4 ? 0 : 1 ; const west = rightSide ? 1 : - 1 ; const cos = Math . cos ( midAngle ) ; const sin = Math . sin ( midAngle ) ; const x0 = cos * anchorRadius ; const y0 = sin * anchorRadius ; const x = cos * ( anchorRadius + linkLabel . radiusPadding ) ; const y = sin * ( anchorRadius + linkLabel . radiusPadding ) ; const stemFromX = x ; const stemFromY = y ; const poolIndex = rightSide + ( 1 - north ) ; const relativeY = north * y ; const yOffset = Math . max ( currentY [ poolIndex ] + rowPitch , relativeY + yRelativeIncrement , rowPitch / 2 ) ; currentY [ poolIndex ] = yOffset ; const cy = north * yOffset ; const stemToX = x + north * west * cy - west * relativeY ; const stemToY = cy ; const translateX = stemToX + west * ( linkLabel . horizontalStemLength + linkLabel . gap ) ; const translate = [ translateX , stemToY ] ; const linkLabels = [ [ x0 , y0 ] , [ stemFromX , stemFromY ] , [ stemToX , stemToY ] , [ stemToX + west * linkLabel . horizontalStemLength , stemToY ] , ] ; const rawLabelText = rawTextGetter ( node ) ; const isRTL = isRTLString ( rawLabelText ) ; const valueText = valueFormatter ( valueGetter ( node ) ) ; const valueWidth = measureOneBoxWidth ( measure , linkLabel . fontSize , { ... valueFont , text : valueText , isValue : false , } ) ; const widthAdjustment = valueWidth + 2 * linkLabel . fontSize ; const labelText = cutToLength ( rawLabelText , maxTextLength ) ; const allottedLabelWidth = Math . max ( 0 , rightSide ? rectWidth - diskCenter . x - translateX - widthAdjustment : diskCenter . x + translateX - widthAdjustment ) ; const { text , width } = linkLabel . fontSize / 2 <= cy + diskCenter . y && cy + diskCenter . y <= rectHeight - linkLabel . fontSize / 2 ? fitText ( measure , labelText , allottedLabelWidth , linkLabel . fontSize , labelFont ) : { text : '' , width : 0 } ; return { isRTL , linkLabels , translate , text , valueText , width , valueWidth , textAlign : rightSide ? 'left' : 'right' , } ; } ; }