Ghost

struxadotcloud/struxa

public
Fork0
CodeIssues0Pull requests0
1branch
struxapackagesuisrccomponentsdither-kitpolar.ts
feat: replace server info sparklines with dither-kit charts5845cd43 months ago123 lines · 3.2 KB
1type Row = Record<string, unknown>
2
3const TOP = -
4const TAU = Math
5
6export type PieSlice
7 name: string
8 value: number
9 start: number
10 end: number
11 mid: number
12}
13
14/** Slice angles from each data row's value under `dataKey`, named by `nameKey`. */
15export function pieSlices(
16 data: Row
17 dataKey: string,
18 nameKey: string
19): PieSlice[]
20 const vals =
21 const total =
22 let a =
23 return data.map
24 const v =
25 const span =
26 const slice =
27 name: String(r
28 value: v,
29 start: a,
30 end: a +
31 mid: a +
32 }
33 a += span
34 return slice
35 })
36}
37
38/** Which slice a pointer angle falls in (or -1). */
39export function sliceAtAngle(slices
40 // Normalize so comparisons against [start, end) (which begin at TOP) work.
41 let a =
42 while (a <
43 while (a >=
44 return slices.findIndex
45}
46
47export type RadarAxis
48
49/** Evenly-spaced spokes, one per data row, labelled by `nameKey`. */
50export function radarAxes(data
51 const n =
52 return data.map
53 label: String(r
54 angle: TOP +
55 }))
56}
57
58/** Nearest radar spoke to a pointer angle. */
59export function axisAtAngle(axes
60 let best =
61 let bestD =
62 axes.forEach((ax
63 let d =
64 d = Math.
65 if (d <
66 bestD = d
67 best = i
68 }
69 })
70 return best
71}
72
73export const polarX =
74 cx + Math.
75export const polarY =
76 cy + Math.
77
78/** Even-odd point-in-polygon test (polygon as flat [x0,y0,x1,y1,…]). */
79export function pointInPolygon(
80 px: number,
81 py: number,
82 poly: number[]
83): boolean {
84 let inside =
85 const n =
86 for (let i
87 const xi =
88 const yi =
89 const xj =
90 const yj =
91 if (yi >
92 inside = !inside
93 }
94 }
95 return inside
96}
97
98/** Distance from a point to the nearest edge of a polygon — drives the radial
99 * dither density (dense near the edge, thinning to the centre). */
100export function distToPolygonEdge(
101 px: number,
102 py: number,
103 poly: number[]
104): number {
105 let best =
106 const n =
107 for (let i
108 const xi =
109 const yi =
110 const xj =
111 const yj =
112 const dx =
113 const dy =
114 const len2 =
115 let t =
116 t = Math.
117 const ex =
118 const ey =
119 const d =
120 if (d <
121 }
122 return best
123}
Math
.
PI
/
2
.
PI
*
2
=
{
//
radians
[]
,
{
data
.
map
(
(
r
)
=>
Math
.
max
(
0
,
Number
(
r
[
dataKey
])
||
0
))
vals
.
reduce
(
(
a
,
b
)
=>
a
+
b
,
0
)
||
1
TOP
(
(
r
,
i
)
=>
{
vals
[
i
]
!
(
v
/
total
)
*
TAU
{
[
nameKey
]
??
i
)
,
span
,
span
/
2
,
:
PieSlice
[]
,
angle
:
number
)
:
number
{
angle
TOP
)
a
+=
TAU
TOP
+
TAU
)
a
-=
TAU
(
(
s
)
=>
a
>=
s
.
start
&&
a
<
s
.
end
)
=
{
label
:
string
;
angle
:
number
}
:
Row
[]
,
nameKey
:
string
)
:
RadarAxis
[]
{
Math
.
max
(
data
.
length
,
1
)
(
(
r
,
i
)
=>
(
{
[
nameKey
]
??
i
)
,
(
i
/
n
)
*
TAU
,
:
RadarAxis
[]
,
angle
:
number
)
:
number
{
0
Infinity
,
i
)
=>
{
Math
.
abs
(((
angle
-
ax
.
angle
+
Math
.
PI
*
3
)
%
TAU
)
-
Math
.
PI
)
abs
(
d
)
bestD
)
{
(
cx
:
number
,
r
:
number
,
angle
:
number
)
=>
cos
(
angle
)
*
r
(
cy
:
number
,
r
:
number
,
angle
:
number
)
=>
sin
(
angle
)
*
r
false
poly
.
length
/
2
=
0
,
j
=
n
-
1
;
i
<
n
;
j
=
i
++
)
{
poly
[
i
*
2
]
!
poly
[
i
*
2
+
1
]
!
poly
[
j
*
2
]
!
poly
[
j
*
2
+
1
]
!
py
!==
yj
>
py
&&
px
<
((
xj
-
xi
)
*
(
py
-
yi
))
/
(
yj
-
yi
)
+
xi
)
{
Infinity
poly
.
length
/
2
=
0
,
j
=
n
-
1
;
i
<
n
;
j
=
i
++
)
{
poly
[
i
*
2
]
!
poly
[
i
*
2
+
1
]
!
poly
[
j
*
2
]
!
poly
[
j
*
2
+
1
]
!
xj
-
xi
yj
-
yi
dx
*
dx
+
dy
*
dy
||
1
((
px
-
xi
)
*
dx
+
(
py
-
yi
)
*
dy
)
/
len2
max
(
0
,
Math
.
min
(
1
,
t
))
xi
+
t
*
dx
-
px
yi
+
t
*
dy
-
py
Math
.
hypot
(
ex
,
ey
)
best
)
best
=
d