import type { Fn , FnN2 } from "@thi.ng/api" ; import { pointInRect } from "@thi.ng/geom-isec/point" ; import { polygon } from "@thi.ng/geom/polygon" ; import type { IGridLayout , LayoutBox } from "@thi.ng/layout" ; import { isLayout } from "@thi.ng/layout/checks" ; import { HALF_PI , PI , TAU } from "@thi.ng/math/api" ; import { fitClamped , norm } from "@thi.ng/math/fit" ; import { mix } from "@thi.ng/math/mix" ; import { map } from "@thi.ng/transducers/map" ; import { normRange } from "@thi.ng/transducers/norm-range" ; import type { Vec } from "@thi.ng/vectors" ; import { cartesian2 } from "@thi.ng/vectors/cartesian" ; import { hash } from "@thi.ng/vectors/hash" ; import { dialVal } from "../behaviors/dial.js" ; import { handleSlider1Keys } from "../behaviors/slider.js" ; import type { IMGUI } from "../gui.js" ; import { dialValueLabel } from "./textlabel.js" ; import { tooltipRaw } from "./tooltip.js" ; const ringHeight = ( w , thetaGap ) => ( w / 2 ) * ( 1 + Math . sin ( HALF_PI + thetaGap / 2 ) ) ; const arcVerts = ( o , r , start , end , thetaRes = 12 ) : Iterable < Vec > => r > 1 ? map ( ( t ) => cartesian2 ( null , [ r , mix ( start , end , t ) ] , o ) , normRange ( Math . max ( 1 , Math . abs ( end - start ) / ( PI / thetaRes ) ) | 0 ) ) : [ o ] ; export const ring = ( gui , layout , id , min , max , prec , val , thetaGap , rscale , label ? , fmt ? , info ? ) => { let h ; let box ; if ( isLayout ( layout ) ) { h = ringHeight ( layout . cellW , thetaGap ) ; box = layout . next ( [ 1 , layout . rowsForHeight ( h ) + 1 ] ) ; } else { h = ringHeight ( layout . cw , thetaGap ) ; box = layout ; } return ringRaw ( gui , id , box . x , box . y , box . w , h , min , max , prec , val , thetaGap , rscale , 0 , h + box . ch / 2 + gui . theme . baseLine , label , fmt , info ) ; } ; export const ringGroup = ( gui , layout , id , min , max , prec , horizontal , thetaGap , rscale , vals , label , fmt ? , info = [ ] ) => { const n = vals . length ; const nested = horizontal ? layout . nest ( n , [ n , 1 ] ) : layout . nest ( 1 , [ 1 , ( layout . rowsForHeight ( ringHeight ( layout . cellW , thetaGap ) ) + 1 ) * n , ] ) ; let res ; let idx = - 1 ; for ( let i = 0 ; i < n ; i ++ ) { const v = ring ( gui , nested , `${ id }-${ i }` , min , max , prec , vals [ i ] , thetaGap , rscale , label [ i ] , fmt , info [ i ] ) ; if ( v !== undefined ) { res = v ; idx = i ; } } return res !== undefined ? [ idx , res ] : undefined ; } ; export const ringRaw = ( gui , id , x , y , w , h , min , max , prec , val , thetaGap , rscale , lx , ly , label ? , fmt ? , info ? ) => { const r = w / 2 ; const key = hash ( [ x , y , r ] ) ; gui . registerID ( id , key ) ; const pos = [ x + r , y + r ] ; const startTheta = HALF_PI + thetaGap / 2 ; const endTheta = HALF_PI + TAU - thetaGap / 2 ; const draw = gui . draw ; const aid = gui . activeID ; const hover = ! gui . disabled && ( aid === id || ( aid === "" && pointInRect ( gui . mouse , [ x , y ] , [ w , h ] ) ) ) ; let v = val ; let res ; if ( hover ) { gui . setCursor ( "pointer" ) ; gui . hotID = id ; if ( gui . isMouseDown ( ) ) { gui . activeID = id ; res = dialVal ( gui . mouse , pos , startTheta , thetaGap , min , max , prec ) ; } info && draw && tooltipRaw ( gui , info ) ; } const focused = gui . requestFocus ( id ) ; if ( draw ) { const valTheta = startTheta + ( TAU - thetaGap ) * norm ( v , min , max ) ; const r2 = r * rscale ; const numV = fitClamped ( r , 15 , 80 , 12 , 30 ) ; const shape = ( max ) => ( ) => polygon ( [ ... arcVerts ( pos , r , startTheta , max , numV ) , ... arcVerts ( pos , r2 , max , startTheta , numV ) , ] , { } ) ; const bgShape = gui . resource ( id , key , shape ( endTheta ) ) ; const valShape = gui . resource ( id , v , shape ( valTheta ) ) ; const valLabel = dialValueLabel ( gui , id , key , v , x + lx , y + ly , label , fmt ) ; bgShape . attribs . fill = gui . bgColor ( hover || focused ) ; bgShape . attribs . stroke = gui . focusColor ( id ) ; valShape . attribs . fill = gui . fgColor ( hover ) ; gui . add ( bgShape , valShape , valLabel ) ; } if ( focused && ( v = handleSlider1Keys ( gui , min , max , prec , v ) ) !== undefined ) { return v ; } gui . lastID = id ; return res ; } ;