import { TAU } from '../../../../common/constants' ; import { Circline , CirclineArc , CirclinePredicate , Coordinate , Distance , PointObject , Radian , Radius , RingSectorConstruction , trueBearingToStandardPositionAngle , } from '../../../../common/geometry' ; import { PartitionStyle } from '../../../../utils/themes/partition' ; import { PartitionSpec } from '../../specs' ; import { AngleFromTo , LayerFromTo , ShapeTreeNode } from '../types/viewmodel_types' ; function euclideanDistance ( { x : x1 , y : y1 } , { x : x2 , y : y2 } ) { return Math . sqrt ( Math . pow ( x1 - x2 , 2 ) + Math . pow ( y1 - y2 , 2 ) ) ; } function fullyContained ( c1 , c2 ) { return euclideanDistance ( c1 , c2 ) + c2 . r <= c1 . r ; } function noOverlap ( c1 , c2 ) { return euclideanDistance ( c1 , c2 ) >= c1 . r + c2 . r ; } function circlineIntersect ( c1 , c2 ) { const d = Math . sqrt ( ( c1 . x - c2 . x ) * ( c1 . x - c2 . x ) + ( c1 . y - c2 . y ) * ( c1 . y - c2 . y ) ) ; if ( c1 . r + c2 . r >= d && d >= Math . abs ( c1 . r - c2 . r ) ) { const a1 = d + c1 . r + c2 . r ; const a2 = d + c1 . r - c2 . r ; const a3 = d - c1 . r + c2 . r ; const a4 = - d + c1 . r + c2 . r ; const area = Math . sqrt ( a1 * a2 * a3 * a4 ) / 4 ; const xAux1 = ( c1 . x + c2 . x ) / 2 + ( ( c2 . x - c1 . x ) * ( c1 . r * c1 . r - c2 . r * c2 . r ) ) / ( 2 * d * d ) ; const xAux2 = ( 2 * ( c1 . y - c2 . y ) * area ) / ( d * d ) ; const x1 = xAux1 + xAux2 ; const x2 = xAux1 - xAux2 ; const yAux1 = ( c1 . y + c2 . y ) / 2 + ( ( c2 . y - c1 . y ) * ( c1 . r * c1 . r - c2 . r * c2 . r ) ) / ( 2 * d * d ) ; const yAux2 = ( 2 * ( c1 . x - c2 . x ) * area ) / ( d * d ) ; const y1 = yAux1 - yAux2 ; const y2 = yAux1 + yAux2 ; return [ { x : x1 , y : y1 } , { x : x2 , y : y2 } , ] ; } return [ ] ; } function circlineValidSectors ( refC , c ) { const { inside } = refC ; const { x , y , r , from , to } = c ; const fullContainment = fullyContained ( refC , c ) ; const fullyOutside = noOverlap ( refC , c ) || fullyContained ( c , refC ) ; if ( ( inside && fullContainment ) || ( ! inside && fullyOutside ) ) { return [ ] ; } if ( ( inside && fullyOutside ) || ( ! inside && fullContainment ) ) { return [ c ] ; } const circlineIntersections = circlineIntersect ( refC , c ) ; if ( circlineIntersections . length !== 2 ) return [ ] ; const [ p1 , p2 ] = circlineIntersections ; const aPre1 = Math . atan2 ( p1 . y - c . y , p1 . x - c . x ) ; const aPre2 = Math . atan2 ( p2 . y - c . y , p2 . x - c . x ) ; const a1p = Math . max ( from , Math . min ( to , aPre1 < 0 ? aPre1 + TAU : aPre1 ) ) ; const a2p = Math . max ( from , Math . min ( to , aPre2 < 0 ? aPre2 + TAU : aPre2 ) ) ; const a1 = Math . min ( a1p , a2p ) ; const a2 = a1p === a2p ? TAU : Math . max ( a1p , a2p ) ; const breakpoints = [ from ] ; if ( from < a1 && a1 < to ) breakpoints . push ( a1 ) ; if ( from < a2 && a2 < to ) breakpoints . push ( a2 ) ; breakpoints . push ( to ) ; const predicate = inside ? noOverlap : fullyContained ; const result = [ ] ; for ( let i = 0 ; i < breakpoints . length - 1 ; i ++ ) { const from = breakpoints [ i ] ; const to = breakpoints [ i + 1 ] ; const midAngle = ( from + to ) / 2 ; const xx = x + r * Math . cos ( midAngle ) ; const yy = y + r * Math . sin ( midAngle ) ; if ( predicate ( refC , { x : xx , y : yy , r : 0 } ) ) result . push ( { x , y , r , from , to } ) ; } return result ; } internal export function conjunctiveConstraint ( constraints , c ) { let valids = [ c ] ; for ( let i = 0 ; i < constraints . length ; i ++ ) { const refC = constraints [ i ] ; const nextValids = [ ] ; for ( let j = 0 ; j < valids . length ; j ++ ) { const cc = valids [ j ] ; const currentValids = circlineValidSectors ( refC , cc ) ; nextValids . push ( ... currentValids ) ; } valids = nextValids ; } return valids ; } internal export const INFINITY_RADIUS = 1e4 ; internal export function angleToCircline ( midRadius , alpha , direction ) { const sectorRadiusLineX = Math . cos ( alpha ) * midRadius ; const sectorRadiusLineY = Math . sin ( alpha ) * midRadius ; const normalAngle = alpha + ( direction * Math . PI ) / 2 ; const x = sectorRadiusLineX + INFINITY_RADIUS * Math . cos ( normalAngle ) ; const y = sectorRadiusLineY + INFINITY_RADIUS * Math . sin ( normalAngle ) ; return { x , y , r : INFINITY_RADIUS , inside : false , from : 0 , to : TAU } ; } function ringSectorStartAngle ( d ) { return trueBearingToStandardPositionAngle ( d . x0 + Math . max ( 0 , d . x1 - d . x0 - TAU / 2 ) / 2 ) ; } function ringSectorEndAngle ( d ) { return trueBearingToStandardPositionAngle ( d . x1 - Math . max ( 0 , d . x1 - d . x0 - TAU / 2 ) / 2 ) ; } function ringSectorInnerRadius ( innerRadius , ringThickness ) { return ( d ) : Radius => innerRadius + d . y0 * ringThickness ; } function ringSectorOuterRadius ( innerRadius , ringThickness ) { return ( d ) : Radius => innerRadius + ( d . y0 + 1 ) * ringThickness ; } internal export function ringSectorConstruction ( { fillOutside , radiusOutside , fillRectangleWidth , fillRectangleHeight } , { circlePadding , radialPadding } , innerRadius , ringThickness ) { return ( ringSector ) : RingSectorConstruction => { const radiusGetter = fillOutside ? ringSectorOuterRadius : ringSectorInnerRadius ; const geometricInnerRadius = radiusGetter ( innerRadius , ringThickness ) ( ringSector ) ; const innerR = geometricInnerRadius + circlePadding * 2 ; const outerR = Math . max ( innerR , ringSectorOuterRadius ( innerRadius , ringThickness ) ( ringSector ) - circlePadding + ( fillOutside ? radiusOutside : 0 ) ) ; const startAngle = ringSectorStartAngle ( ringSector ) ; const endAngle = ringSectorEndAngle ( ringSector ) ; const innerCircline = { x : 0 , y : 0 , r : innerR , inside : true , from : 0 , to : TAU } ; const outerCircline = { x : 0 , y : 0 , r : outerR , inside : false , from : 0 , to : TAU } ; const midRadius = ( innerR + outerR ) / 2 ; const sectorStartCircle = angleToCircline ( midRadius , startAngle - radialPadding , - 1 ) ; const sectorEndCircle = angleToCircline ( midRadius , endAngle + radialPadding , 1 ) ; const outerRadiusFromRectangleWidth = fillRectangleWidth / 2 ; const outerRadiusFromRectanglHeight = fillRectangleHeight / 2 ; const fullCircle = ringSector . x0 === 0 && ringSector . x1 === TAU && geometricInnerRadius === 0 ; const sectorCirclines = [ ... ( fullCircle && innerRadius === 0 ? [ ] : [ innerCircline ] ) , outerCircline , ... ( fullCircle ? [ ] : [ sectorStartCircle , sectorEndCircle ] ) , ] ; const rectangleCirclines = outerRadiusFromRectangleWidth === Infinity && outerRadiusFromRectanglHeight === Infinity ? [ ] : [ { x : INFINITY_RADIUS - outerRadiusFromRectangleWidth , y : 0 , r : INFINITY_RADIUS , inside : true } , { x : - INFINITY_RADIUS + outerRadiusFromRectangleWidth , y : 0 , r : INFINITY_RADIUS , inside : true } , { x : 0 , y : INFINITY_RADIUS - outerRadiusFromRectanglHeight , r : INFINITY_RADIUS , inside : true } , { x : 0 , y : - INFINITY_RADIUS + outerRadiusFromRectanglHeight , r : INFINITY_RADIUS , inside : true } , ] ; return [ ... sectorCirclines , ... rectangleCirclines ] ; } ; } internal export function makeRowCircline ( cx , cy , radialOffset , rotation , fontSize , offsetSign ) { const r = INFINITY_RADIUS ; const offset = ( offsetSign * fontSize ) / 2 ; const topRadius = r - offset ; const x = cx + topRadius * Math . cos ( - rotation + TAU / 4 ) ; const y = cy + topRadius * Math . cos ( - rotation + TAU / 2 ) ; return { r : r + radialOffset , x , y } ; }