@motionscript/charts 0.1.0 → 0.1.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +98 -28
  3. package/dist/bar-chart/bar-chart.d.ts +242 -119
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  5. package/dist/bar-chart/bar-chart.js +231 -260
  6. package/dist/bar-chart/bar-chart.js.map +1 -1
  7. package/dist/bar-chart/category-axis.d.ts +16 -38
  8. package/dist/bar-chart/category-axis.d.ts.map +1 -1
  9. package/dist/bar-chart/category-axis.js +59 -68
  10. package/dist/bar-chart/category-axis.js.map +1 -1
  11. package/dist/bar-chart/chart-bars.d.ts +60 -91
  12. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  13. package/dist/bar-chart/chart-bars.js +88 -125
  14. package/dist/bar-chart/chart-bars.js.map +1 -1
  15. package/dist/bar-chart/index.d.ts +5 -13
  16. package/dist/bar-chart/index.d.ts.map +1 -1
  17. package/dist/bar-chart/index.js +2 -9
  18. package/dist/bar-chart/index.js.map +1 -1
  19. package/dist/bar-chart/shared.d.ts +40 -132
  20. package/dist/bar-chart/shared.d.ts.map +1 -1
  21. package/dist/bar-chart/shared.js +70 -158
  22. package/dist/bar-chart/shared.js.map +1 -1
  23. package/dist/browser/chunks/chunk-R3Q5EE6E.js +2 -0
  24. package/dist/browser/chunks/chunk-R3Q5EE6E.js.map +7 -0
  25. package/dist/browser/index.js +1 -1
  26. package/dist/browser/index.js.map +4 -4
  27. package/dist/browser/kit.js +1 -1
  28. package/dist/browser/manifest.json +1 -1
  29. package/dist/engine.d.ts +8 -17
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +8 -17
  32. package/dist/engine.js.map +1 -1
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/kit/cartesian.d.ts +49 -35
  38. package/dist/kit/cartesian.d.ts.map +1 -1
  39. package/dist/kit/cartesian.js +80 -42
  40. package/dist/kit/cartesian.js.map +1 -1
  41. package/dist/kit/chart-canvas.d.ts +37 -40
  42. package/dist/kit/chart-canvas.d.ts.map +1 -1
  43. package/dist/kit/chart-canvas.js +53 -47
  44. package/dist/kit/chart-canvas.js.map +1 -1
  45. package/dist/kit/chart-legend.d.ts +16 -59
  46. package/dist/kit/chart-legend.d.ts.map +1 -1
  47. package/dist/kit/chart-legend.js +38 -76
  48. package/dist/kit/chart-legend.js.map +1 -1
  49. package/dist/kit/entries.d.ts +2 -0
  50. package/dist/kit/entries.d.ts.map +1 -1
  51. package/dist/kit/entries.js +2 -0
  52. package/dist/kit/entries.js.map +1 -1
  53. package/dist/kit/horizontal-axis.d.ts +77 -0
  54. package/dist/kit/horizontal-axis.d.ts.map +1 -0
  55. package/dist/kit/horizontal-axis.js +99 -0
  56. package/dist/kit/horizontal-axis.js.map +1 -0
  57. package/dist/kit/motion.d.ts +16 -3
  58. package/dist/kit/motion.d.ts.map +1 -1
  59. package/dist/kit/motion.js +15 -1
  60. package/dist/kit/motion.js.map +1 -1
  61. package/dist/{pie-chart → kit}/pie-legend.d.ts +14 -29
  62. package/dist/kit/pie-legend.d.ts.map +1 -0
  63. package/dist/kit/pie-legend.js +93 -0
  64. package/dist/kit/pie-legend.js.map +1 -0
  65. package/dist/kit/polar.d.ts +31 -0
  66. package/dist/kit/polar.d.ts.map +1 -0
  67. package/dist/kit/polar.js +33 -0
  68. package/dist/kit/polar.js.map +1 -0
  69. package/dist/kit/shared.d.ts +60 -214
  70. package/dist/kit/shared.d.ts.map +1 -1
  71. package/dist/kit/shared.js +91 -291
  72. package/dist/kit/shared.js.map +1 -1
  73. package/dist/kit/style.d.ts +254 -0
  74. package/dist/kit/style.d.ts.map +1 -0
  75. package/dist/kit/style.js +197 -0
  76. package/dist/kit/style.js.map +1 -0
  77. package/dist/kit/vertical-axis.d.ts +36 -45
  78. package/dist/kit/vertical-axis.d.ts.map +1 -1
  79. package/dist/kit/vertical-axis.js +69 -81
  80. package/dist/kit/vertical-axis.js.map +1 -1
  81. package/dist/line-chart/chart-line.d.ts +107 -40
  82. package/dist/line-chart/chart-line.d.ts.map +1 -1
  83. package/dist/line-chart/chart-line.js +172 -89
  84. package/dist/line-chart/chart-line.js.map +1 -1
  85. package/dist/line-chart/chart-region.d.ts +14 -27
  86. package/dist/line-chart/chart-region.d.ts.map +1 -1
  87. package/dist/line-chart/chart-region.js +13 -28
  88. package/dist/line-chart/chart-region.js.map +1 -1
  89. package/dist/line-chart/index.d.ts +5 -18
  90. package/dist/line-chart/index.d.ts.map +1 -1
  91. package/dist/line-chart/index.js +3 -16
  92. package/dist/line-chart/index.js.map +1 -1
  93. package/dist/line-chart/line-chart.d.ts +272 -119
  94. package/dist/line-chart/line-chart.d.ts.map +1 -1
  95. package/dist/line-chart/line-chart.js +309 -257
  96. package/dist/line-chart/line-chart.js.map +1 -1
  97. package/dist/line-chart/shared.d.ts +73 -130
  98. package/dist/line-chart/shared.d.ts.map +1 -1
  99. package/dist/line-chart/shared.js +195 -156
  100. package/dist/line-chart/shared.js.map +1 -1
  101. package/dist/line-chart/x-scale.d.ts +16 -55
  102. package/dist/line-chart/x-scale.d.ts.map +1 -1
  103. package/dist/line-chart/x-scale.js +44 -111
  104. package/dist/line-chart/x-scale.js.map +1 -1
  105. package/dist/nodes.d.ts +3 -13
  106. package/dist/nodes.d.ts.map +1 -1
  107. package/dist/nodes.js +9 -13
  108. package/dist/nodes.js.map +1 -1
  109. package/dist/pie-chart/index.d.ts +3 -15
  110. package/dist/pie-chart/index.d.ts.map +1 -1
  111. package/dist/pie-chart/index.js +1 -11
  112. package/dist/pie-chart/index.js.map +1 -1
  113. package/dist/pie-chart/pie-chart.d.ts +147 -85
  114. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  115. package/dist/pie-chart/pie-chart.js +65 -86
  116. package/dist/pie-chart/pie-chart.js.map +1 -1
  117. package/dist/pie-chart/pie-ring.d.ts +65 -196
  118. package/dist/pie-chart/pie-ring.d.ts.map +1 -1
  119. package/dist/pie-chart/pie-ring.js +154 -292
  120. package/dist/pie-chart/pie-ring.js.map +1 -1
  121. package/dist/pie-chart/shared.d.ts +78 -172
  122. package/dist/pie-chart/shared.d.ts.map +1 -1
  123. package/dist/pie-chart/shared.js +54 -177
  124. package/dist/pie-chart/shared.js.map +1 -1
  125. package/dist/radar-chart/index.d.ts +5 -0
  126. package/dist/radar-chart/index.d.ts.map +1 -0
  127. package/dist/radar-chart/index.js +5 -0
  128. package/dist/radar-chart/index.js.map +1 -0
  129. package/dist/radar-chart/radar-chart.d.ts +218 -0
  130. package/dist/radar-chart/radar-chart.d.ts.map +1 -0
  131. package/dist/radar-chart/radar-chart.js +224 -0
  132. package/dist/radar-chart/radar-chart.js.map +1 -0
  133. package/dist/radar-chart/radar-grid.d.ts +43 -0
  134. package/dist/radar-chart/radar-grid.d.ts.map +1 -0
  135. package/dist/radar-chart/radar-grid.js +61 -0
  136. package/dist/radar-chart/radar-grid.js.map +1 -0
  137. package/dist/radar-chart/radar-series.d.ts +59 -0
  138. package/dist/radar-chart/radar-series.d.ts.map +1 -0
  139. package/dist/radar-chart/radar-series.js +71 -0
  140. package/dist/radar-chart/radar-series.js.map +1 -0
  141. package/dist/radar-chart/shared.d.ts +73 -0
  142. package/dist/radar-chart/shared.d.ts.map +1 -0
  143. package/dist/radar-chart/shared.js +123 -0
  144. package/dist/radar-chart/shared.js.map +1 -0
  145. package/dist/radial-chart/index.d.ts +6 -0
  146. package/dist/radial-chart/index.d.ts.map +1 -0
  147. package/dist/radial-chart/index.js +4 -0
  148. package/dist/radial-chart/index.js.map +1 -0
  149. package/dist/radial-chart/radial-chart.d.ts +216 -0
  150. package/dist/radial-chart/radial-chart.d.ts.map +1 -0
  151. package/dist/radial-chart/radial-chart.js +174 -0
  152. package/dist/radial-chart/radial-chart.js.map +1 -0
  153. package/dist/radial-chart/radial-rings.d.ts +121 -0
  154. package/dist/radial-chart/radial-rings.d.ts.map +1 -0
  155. package/dist/radial-chart/radial-rings.js +273 -0
  156. package/dist/radial-chart/radial-rings.js.map +1 -0
  157. package/dist/radial-chart/shared.d.ts +73 -0
  158. package/dist/radial-chart/shared.d.ts.map +1 -0
  159. package/dist/radial-chart/shared.js +79 -0
  160. package/dist/radial-chart/shared.js.map +1 -0
  161. package/package.json +4 -4
  162. package/registry.json +21 -6
  163. package/src/bar-chart/bar-chart.ts +274 -315
  164. package/src/bar-chart/category-axis.ts +53 -88
  165. package/src/bar-chart/chart-bars.ts +108 -205
  166. package/src/bar-chart/index.ts +8 -19
  167. package/src/bar-chart/shared.ts +81 -309
  168. package/src/engine.ts +8 -17
  169. package/src/index.ts +2 -0
  170. package/src/kit/cartesian.ts +118 -55
  171. package/src/kit/chart-canvas.ts +50 -80
  172. package/src/kit/chart-legend.ts +36 -97
  173. package/src/kit/entries.ts +4 -1
  174. package/src/kit/horizontal-axis.ts +114 -0
  175. package/src/kit/motion.ts +18 -2
  176. package/src/{pie-chart → kit}/pie-legend.ts +25 -65
  177. package/src/kit/polar.ts +54 -0
  178. package/src/kit/shared.ts +104 -487
  179. package/src/kit/style.ts +257 -0
  180. package/src/kit/vertical-axis.ts +81 -115
  181. package/src/line-chart/chart-line.ts +185 -91
  182. package/src/line-chart/chart-region.ts +14 -31
  183. package/src/line-chart/index.ts +14 -22
  184. package/src/line-chart/line-chart.ts +377 -315
  185. package/src/line-chart/shared.ts +229 -304
  186. package/src/line-chart/x-scale.ts +44 -121
  187. package/src/nodes.ts +9 -13
  188. package/src/pie-chart/index.ts +3 -35
  189. package/src/pie-chart/pie-chart.ts +83 -134
  190. package/src/pie-chart/pie-ring.ts +162 -368
  191. package/src/pie-chart/shared.ts +92 -379
  192. package/src/radar-chart/index.ts +4 -0
  193. package/src/radar-chart/radar-chart.ts +287 -0
  194. package/src/radar-chart/radar-grid.ts +78 -0
  195. package/src/radar-chart/radar-series.ts +88 -0
  196. package/src/radar-chart/shared.ts +146 -0
  197. package/src/radial-chart/index.ts +5 -0
  198. package/src/radial-chart/radial-chart.ts +210 -0
  199. package/src/radial-chart/radial-rings.ts +328 -0
  200. package/src/radial-chart/shared.ts +114 -0
  201. package/dist/browser/chunks/chunk-TJNHLQGV.js +0 -2
  202. package/dist/browser/chunks/chunk-TJNHLQGV.js.map +0 -7
  203. package/dist/kit/chart-border.d.ts +0 -27
  204. package/dist/kit/chart-border.d.ts.map +0 -1
  205. package/dist/kit/chart-border.js +0 -28
  206. package/dist/kit/chart-border.js.map +0 -1
  207. package/dist/line-chart/horizontal-axis.d.ts +0 -79
  208. package/dist/line-chart/horizontal-axis.d.ts.map +0 -1
  209. package/dist/line-chart/horizontal-axis.js +0 -129
  210. package/dist/line-chart/horizontal-axis.js.map +0 -1
  211. package/dist/pie-chart/pie-legend.d.ts.map +0 -1
  212. package/dist/pie-chart/pie-legend.js +0 -117
  213. package/dist/pie-chart/pie-legend.js.map +0 -1
  214. package/src/kit/chart-border.ts +0 -50
  215. package/src/line-chart/horizontal-axis.ts +0 -151
@@ -0,0 +1,121 @@
1
+ import { PathBuilder, type NodeOf, type Node2D, type Node2DProps as _Node2DProps, type Fill as _Fill, type FillResolved as _FillResolved, type TextStyle, type Vector2 } from "@motionscript/sdk";
2
+ import { type CenterConfig } from "../kit/polar.js";
3
+ import { type RadialMarks, type RadialRow } from "./shared.js";
4
+ interface RingsState {
5
+ readonly rows: RadialRow[];
6
+ readonly max: number;
7
+ readonly marks: RadialMarks;
8
+ readonly label: TextStyle;
9
+ readonly centerStyle: CenterConfig;
10
+ readonly ringLabels: boolean;
11
+ readonly pops: readonly number[];
12
+ readonly dimOpacity: number;
13
+ readonly centerLabel: string;
14
+ readonly centerCaption: string;
15
+ }
16
+ /** Every radius and angle for one box, derived wherever read so the drawing and the labels cannot disagree. */
17
+ export declare class RadialGeometry {
18
+ private readonly s;
19
+ private readonly box;
20
+ constructor(s: RingsState, box: {
21
+ readonly width: number;
22
+ readonly height: number;
23
+ });
24
+ get count(): number;
25
+ get outerRadius(): number;
26
+ get holeRadius(): number;
27
+ /** Radial depth of one ring. */
28
+ get thickness(): number;
29
+ /** Rim of ring `index`; ring 0 is the outermost. */
30
+ ringOuter(index: number): number;
31
+ ringMid(index: number): number;
32
+ get fractions(): number[];
33
+ get startRad(): number;
34
+ get sweepRad(): number;
35
+ popOf(index: number): number;
36
+ get selectionStrength(): number;
37
+ ringOpacity(index: number): number;
38
+ /** How far ring `index`'s sweep has come at entrance progress `growth`: rings start in turn, outer first. */
39
+ progress(index: number, growth: number): number;
40
+ get labelFont(): number;
41
+ /** Where ring `index`'s caption is pinned: just behind its start, reading away from the sweep. */
42
+ labelAnchor(index: number): {
43
+ at: Vector2;
44
+ side: 1 | -1;
45
+ };
46
+ get centerFont(): number;
47
+ }
48
+ /**
49
+ * An arc band between `rIn` and `rOut` over `a0`..`a1` (radians clockwise from
50
+ * 12 o'clock), the ends squared or capped with half circles. A band shorter
51
+ * than its caps is two overlapping caps, which fill as a pill.
52
+ */
53
+ export declare function bandPath(a0: number, a1: number, rIn: number, rOut: number, round: boolean): PathBuilder;
54
+ /**
55
+ * The rings: row `i` is the `i`-th ring from the outside, an arc from
56
+ * `startAngle` whose sweep is `value / max` of the full sweep, over an
57
+ * optional full-sweep track. A selected ring thickens and the rest dims.
58
+ */
59
+ export declare const RadialRings: import("@motionscript/sdk").Component<"radialRings", import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
60
+ readonly rows: import("@motionscript/sdk").Type<import("../index.js").PieSlice[], import("../index.js").PieSlice[], false>;
61
+ readonly max: 0;
62
+ readonly marks: import("@motionscript/sdk").Type<RadialMarks, RadialMarks, false>;
63
+ readonly label: import("@motionscript/sdk").Type<TextStyle, TextStyle, false>;
64
+ readonly centerStyle: import("@motionscript/sdk").Type<CenterConfig, CenterConfig, false>;
65
+ /** The empty-ring colour drawn under each arc. */
66
+ readonly trackFill: import("@motionscript/sdk").Type<_Fill, _FillResolved[], false>;
67
+ readonly ringLabels: import("@motionscript/sdk").Type<boolean, boolean, false>;
68
+ /** Entrance progress `[0, 1]`: rings sweep in turn. */
69
+ readonly growth: 1;
70
+ readonly labelReveal: 1;
71
+ readonly pops: import("@motionscript/sdk").Type<number[], number[], false>;
72
+ readonly dimOpacity: 0.25;
73
+ readonly centerLabel: "";
74
+ readonly centerCaption: "";
75
+ readonly centerReveal: 1;
76
+ }>, {
77
+ readonly sweep: unknown;
78
+ readonly revealLabels: unknown;
79
+ readonly revealCenter: unknown;
80
+ }, null, Node2D<_Node2DProps>, import("@motionscript/sdk").CallProps<import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
81
+ readonly rows: import("@motionscript/sdk").Type<import("../index.js").PieSlice[], import("../index.js").PieSlice[], false>;
82
+ readonly max: 0;
83
+ readonly marks: import("@motionscript/sdk").Type<RadialMarks, RadialMarks, false>;
84
+ readonly label: import("@motionscript/sdk").Type<TextStyle, TextStyle, false>;
85
+ readonly centerStyle: import("@motionscript/sdk").Type<CenterConfig, CenterConfig, false>;
86
+ /** The empty-ring colour drawn under each arc. */
87
+ readonly trackFill: import("@motionscript/sdk").Type<_Fill, _FillResolved[], false>;
88
+ readonly ringLabels: import("@motionscript/sdk").Type<boolean, boolean, false>;
89
+ /** Entrance progress `[0, 1]`: rings sweep in turn. */
90
+ readonly growth: 1;
91
+ readonly labelReveal: 1;
92
+ readonly pops: import("@motionscript/sdk").Type<number[], number[], false>;
93
+ readonly dimOpacity: 0.25;
94
+ readonly centerLabel: "";
95
+ readonly centerCaption: "";
96
+ readonly centerReveal: 1;
97
+ }>, null, import("@motionscript/sdk").BuiltNode<import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
98
+ readonly rows: import("@motionscript/sdk").Type<import("../index.js").PieSlice[], import("../index.js").PieSlice[], false>;
99
+ readonly max: 0;
100
+ readonly marks: import("@motionscript/sdk").Type<RadialMarks, RadialMarks, false>;
101
+ readonly label: import("@motionscript/sdk").Type<TextStyle, TextStyle, false>;
102
+ readonly centerStyle: import("@motionscript/sdk").Type<CenterConfig, CenterConfig, false>;
103
+ /** The empty-ring colour drawn under each arc. */
104
+ readonly trackFill: import("@motionscript/sdk").Type<_Fill, _FillResolved[], false>;
105
+ readonly ringLabels: import("@motionscript/sdk").Type<boolean, boolean, false>;
106
+ /** Entrance progress `[0, 1]`: rings sweep in turn. */
107
+ readonly growth: 1;
108
+ readonly labelReveal: 1;
109
+ readonly pops: import("@motionscript/sdk").Type<number[], number[], false>;
110
+ readonly dimOpacity: 0.25;
111
+ readonly centerLabel: "";
112
+ readonly centerCaption: "";
113
+ readonly centerReveal: 1;
114
+ }>, {
115
+ readonly sweep: unknown;
116
+ readonly revealLabels: unknown;
117
+ readonly revealCenter: unknown;
118
+ }, Node2D<_Node2DProps>>>>;
119
+ export type RadialRings = NodeOf<typeof RadialRings>;
120
+ export {};
121
+ //# sourceMappingURL=radial-rings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radial-rings.d.ts","sourceRoot":"","sources":["../../src/radial-chart/radial-rings.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EAQX,KAAK,MAAM,EAEX,KAAK,MAAM,EACX,KAAK,WAAW,IAAI,YAAY,EAChC,KAAK,IAAI,IAAI,KAAK,EAClB,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,mBAAmB,CAAA;AAK1B,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC1F,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AAoB5E,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAA;IAClC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAMD,+GAA+G;AAC/G,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,CAAC,EAAE,UAAU,EACb,GAAG,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAG3E,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,gCAAgC;IAChC,IAAI,SAAS,IAAI,MAAM,CAItB;IAED,oDAAoD;IACpD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIhC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI9B,IAAI,SAAS,IAAI,MAAM,EAAE,CAExB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI5B,IAAI,iBAAiB,IAAI,MAAM,CAI9B;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIlC,6GAA6G;IAC7G,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAK/C,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,kGAAkG;IAClG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;KAAE;IAQzD,IAAI,UAAU,IAAI,MAAM,CAQvB;CACF;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,WAAW,CAiCvG;AA8DD;;;;GAIG;AACH,eAAO,MAAM,WAAW;;;;;;IAQpB,kDAAkD;;;IAGlD,uDAAuD;;;;;;;;;;;;;;;;;;IAHvD,kDAAkD;;;IAGlD,uDAAuD;;;;;;;;;;;;;;IAHvD,kDAAkD;;;IAGlD,uDAAuD;;;;;;;;;;;;0BAyDzD,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAA"}
@@ -0,0 +1,273 @@
1
+ import { Graphics2D, PathBuilder, Rect, Text, Types, clamp, component2d, fillOps, textOps, } from "@motionscript/sdk";
2
+ import { asParts, fresh } from "../kit/entries.js";
3
+ import { frameEase } from "../kit/motion.js";
4
+ import { loadPaints } from "../kit/paints.js";
5
+ import { DEG, ringFills, ringPoint, selectionFade } from "../kit/polar.js";
6
+ import { radialFractions } from "./shared.js";
7
+ /** Share of the entrance one ring's sweep starts after the last. */
8
+ const STAGGER = 0.12;
9
+ /** A selected ring's thickness grows by this share of itself, on each side. */
10
+ const GROW = 0.12;
11
+ const ARC_STEP = 4 * DEG;
12
+ const LABEL_PAD = 10;
13
+ const CENTER_FIT = 0.78;
14
+ const GLYPH_RATIO = 0.62;
15
+ /** How far the radius is held in from the box, for the sake of a ring's growth. */
16
+ const EDGE = 4;
17
+ const DEFAULT_MARKS = { innerRadius: 0.3, ringGap: 8, sweep: 360, startAngle: 0, track: true, roundCaps: true };
18
+ const DEFAULT_LABEL = { fontSize: 28, fontWeight: 500, textAlign: "center", fill: "#a1a1aa" };
19
+ const DEFAULT_CENTER = {
20
+ labelStyle: { fontSize: 50, fontWeight: 800, textAlign: "center", fill: "#f4f4f5" },
21
+ captionStyle: { fontSize: 22, fontWeight: 500, textAlign: "center", fill: "#a1a1aa" },
22
+ };
23
+ function sizeOf(style, fallback) {
24
+ return typeof style.fontSize === "number" ? style.fontSize : fallback;
25
+ }
26
+ /** Every radius and angle for one box, derived wherever read so the drawing and the labels cannot disagree. */
27
+ export class RadialGeometry {
28
+ s;
29
+ box;
30
+ constructor(s, box) {
31
+ this.s = s;
32
+ this.box = box;
33
+ }
34
+ get count() {
35
+ return this.s.rows.length;
36
+ }
37
+ get outerRadius() {
38
+ return Math.max(0, Math.min(this.box.width, this.box.height) / 2 - EDGE);
39
+ }
40
+ get holeRadius() {
41
+ return clamp(this.s.marks.innerRadius, 0, 0.9) * this.outerRadius;
42
+ }
43
+ /** Radial depth of one ring. */
44
+ get thickness() {
45
+ const n = this.count;
46
+ if (n === 0)
47
+ return 0;
48
+ return Math.max(0, (this.outerRadius - this.holeRadius - (n - 1) * Math.max(0, this.s.marks.ringGap)) / n);
49
+ }
50
+ /** Rim of ring `index`; ring 0 is the outermost. */
51
+ ringOuter(index) {
52
+ return this.outerRadius - index * (this.thickness + Math.max(0, this.s.marks.ringGap));
53
+ }
54
+ ringMid(index) {
55
+ return this.ringOuter(index) - this.thickness / 2;
56
+ }
57
+ get fractions() {
58
+ return radialFractions(this.s.rows, this.s.max);
59
+ }
60
+ get startRad() {
61
+ return this.s.marks.startAngle * DEG;
62
+ }
63
+ get sweepRad() {
64
+ return clamp(this.s.marks.sweep, 0, 360) * DEG;
65
+ }
66
+ popOf(index) {
67
+ return clamp(this.s.pops[index] ?? 0, 0, 1);
68
+ }
69
+ get selectionStrength() {
70
+ let strongest = 0;
71
+ for (let i = 0; i < this.count; i++)
72
+ strongest = Math.max(strongest, this.popOf(i));
73
+ return strongest;
74
+ }
75
+ ringOpacity(index) {
76
+ return selectionFade(this.selectionStrength, this.s.dimOpacity, this.popOf(index));
77
+ }
78
+ /** How far ring `index`'s sweep has come at entrance progress `growth`: rings start in turn, outer first. */
79
+ progress(index, growth) {
80
+ const total = 1 + STAGGER * Math.max(0, this.count - 1);
81
+ return clamp(growth * total - index * STAGGER, 0, 1);
82
+ }
83
+ get labelFont() {
84
+ return Math.max(1, Math.min(sizeOf(this.s.label, 28), this.thickness * 0.62));
85
+ }
86
+ /** Where ring `index`'s caption is pinned: just behind its start, reading away from the sweep. */
87
+ labelAnchor(index) {
88
+ const a = this.startRad;
89
+ const back = { x: -Math.cos(a), y: Math.sin(a) };
90
+ const p = ringPoint(this.ringMid(index), a);
91
+ const side = back.x <= 0 ? -1 : 1;
92
+ return { at: { x: p.x + back.x * LABEL_PAD, y: p.y + back.y * LABEL_PAD }, side };
93
+ }
94
+ get centerFont() {
95
+ const { centerStyle, centerLabel, centerCaption } = this.s;
96
+ const wanted = sizeOf(centerStyle.labelStyle, 50);
97
+ const hole = this.holeRadius;
98
+ if (hole <= 0)
99
+ return wanted;
100
+ const ratio = sizeOf(centerStyle.captionStyle, 22) / Math.max(1, wanted);
101
+ const glyphs = Math.max(1, centerLabel.length, centerCaption.length * ratio);
102
+ return Math.min(wanted, (hole * 2 * CENTER_FIT) / (glyphs * GLYPH_RATIO));
103
+ }
104
+ }
105
+ /**
106
+ * An arc band between `rIn` and `rOut` over `a0`..`a1` (radians clockwise from
107
+ * 12 o'clock), the ends squared or capped with half circles. A band shorter
108
+ * than its caps is two overlapping caps, which fill as a pill.
109
+ */
110
+ export function bandPath(a0, a1, rIn, rOut, round) {
111
+ const path = new PathBuilder();
112
+ const mid = (rIn + rOut) / 2;
113
+ const cap = (rOut - rIn) / 2;
114
+ const delta = round && mid > 0 ? Math.min(cap / mid, (a1 - a0) / 2) : 0;
115
+ const from = a0 + delta;
116
+ const to = a1 - delta;
117
+ const steps = Math.max(2, Math.ceil(Math.abs(to - from) / ARC_STEP));
118
+ const arc = (r, x, y, first) => {
119
+ for (let i = 0; i <= steps; i++) {
120
+ const p = ringPoint(r, x + ((y - x) * i) / steps);
121
+ if (first && i === 0)
122
+ path.moveTo(p.x, p.y);
123
+ else
124
+ path.lineTo(p.x, p.y);
125
+ }
126
+ };
127
+ // The end at `angle` rounded through a tip `direction` along the sweep.
128
+ const semicircle = (angle, direction, fromOuter) => {
129
+ const centre = ringPoint(mid, angle);
130
+ const radial = { x: Math.sin(angle), y: Math.cos(angle) };
131
+ const tangent = { x: Math.cos(angle) * direction, y: -Math.sin(angle) * direction };
132
+ const turns = 12;
133
+ for (let i = 1; i < turns; i++) {
134
+ const phi = (Math.PI * i) / turns;
135
+ const r = fromOuter ? Math.cos(phi) : -Math.cos(phi);
136
+ path.lineTo(centre.x + cap * (radial.x * r + tangent.x * Math.sin(phi)), centre.y + cap * (radial.y * r + tangent.y * Math.sin(phi)));
137
+ }
138
+ };
139
+ arc(rOut, from, to, true);
140
+ if (round)
141
+ semicircle(to, 1, true);
142
+ arc(rIn, to, from, false);
143
+ if (round)
144
+ semicircle(from, -1, false);
145
+ path.close();
146
+ return path;
147
+ }
148
+ const NO_BOX = { width: 0, height: 0 };
149
+ function labelBlocks(s) {
150
+ const blocks = [];
151
+ const geometry = () => new RadialGeometry(s, blocks[0]?.parent?.layoutBounds ?? NO_BOX);
152
+ const side = geometry().labelAnchor(0).side;
153
+ s.rows.forEach((_, index) => {
154
+ const anchor = () => geometry().labelAnchor(index).at;
155
+ blocks.push(new Rect({
156
+ flow: "vertical",
157
+ width: "hug",
158
+ height: "hug",
159
+ align: side > 0 ? "centerLeft" : "centerRight",
160
+ opacity: () => (s.ringLabels ? clamp(s.labelReveal, 0, 1) * geometry().ringOpacity(index) : 0),
161
+ centerLeft: side > 0 ? anchor : undefined,
162
+ centerRight: side > 0 ? undefined : anchor,
163
+ children: [
164
+ new Text({
165
+ ...textOps.styleProps(s.label),
166
+ text: () => s.rows[index]?.category ?? "",
167
+ textAlign: side > 0 ? "left" : "right",
168
+ fontSize: () => geometry().labelFont,
169
+ }),
170
+ ],
171
+ }));
172
+ });
173
+ if (s.centerLabel !== "" || s.centerCaption !== "") {
174
+ const children = [];
175
+ if (s.centerLabel !== "") {
176
+ children.push(new Text({ ...textOps.styleProps(s.centerStyle.labelStyle), text: s.centerLabel, fontSize: () => geometry().centerFont }));
177
+ }
178
+ if (s.centerCaption !== "") {
179
+ children.push(new Text({
180
+ ...textOps.styleProps(s.centerStyle.captionStyle),
181
+ text: s.centerCaption,
182
+ fontSize: () => geometry().centerFont * (sizeOf(s.centerStyle.captionStyle, 22) / sizeOf(s.centerStyle.labelStyle, 50)),
183
+ }));
184
+ }
185
+ blocks.push(new Rect({
186
+ flow: "vertical",
187
+ width: "hug",
188
+ height: "hug",
189
+ gap: 2,
190
+ align: "center",
191
+ opacity: () => clamp(s.centerReveal, 0, 1),
192
+ center: { x: 0, y: 0 },
193
+ children,
194
+ }));
195
+ }
196
+ return asParts(blocks);
197
+ }
198
+ /**
199
+ * The rings: row `i` is the `i`-th ring from the outside, an arc from
200
+ * `startAngle` whose sweep is `value / max` of the full sweep, over an
201
+ * optional full-sweep track. A selected ring thickens and the rest dims.
202
+ */
203
+ export const RadialRings = component2d({
204
+ key: "radialRings",
205
+ state: {
206
+ rows: fresh(() => []),
207
+ max: 0,
208
+ marks: Types.custom(DEFAULT_MARKS),
209
+ label: Types.custom(DEFAULT_LABEL),
210
+ centerStyle: Types.custom(DEFAULT_CENTER),
211
+ /** The empty-ring colour drawn under each arc. */
212
+ trackFill: Types.fill("white/15"),
213
+ ringLabels: Types.boolean(false),
214
+ /** Entrance progress `[0, 1]`: rings sweep in turn. */
215
+ growth: 1,
216
+ labelReveal: 1,
217
+ pops: fresh(() => []),
218
+ dimOpacity: 0.25,
219
+ centerLabel: "",
220
+ centerCaption: "",
221
+ centerReveal: 1,
222
+ },
223
+ defaults: { width: "fill", height: "fill" },
224
+ children: (state) => labelBlocks(state),
225
+ load: (loader, state) => loadPaints(loader, { fills: [...ringFills(state.rows), fillOps.resolve(state.trackFill)] }),
226
+ commands: {
227
+ /** Sweep the rings in, outer first. */
228
+ sweep: (timeline) => {
229
+ const ease = frameEase(timeline);
230
+ return timeline.animate((t) => ({ growth: ease(t) }));
231
+ },
232
+ /** Fade the ring captions up. */
233
+ revealLabels: (timeline) => {
234
+ const ease = frameEase(timeline);
235
+ return timeline.animate((t) => ({ labelReveal: ease(t) }));
236
+ },
237
+ /** Fade the centre summary up. */
238
+ revealCenter: (timeline, state) => {
239
+ const ease = frameEase(timeline);
240
+ const from = clamp(state.centerReveal, 0, 1);
241
+ return timeline.animate((t) => ({ centerReveal: from + (1 - from) * ease(t) }));
242
+ },
243
+ },
244
+ render: (ctx, state) => {
245
+ const g = new RadialGeometry(state, ctx.size);
246
+ const thickness = g.thickness;
247
+ if (g.outerRadius > 0 && g.count > 0 && thickness > 0) {
248
+ const half = g.outerRadius + EDGE + 1;
249
+ const frame = { centerBounds: [-half, -half, half, half], width: half * 2, height: half * 2 };
250
+ const fills = ringFills(state.rows);
251
+ const fractions = g.fractions;
252
+ const a0 = g.startRad;
253
+ const round = state.marks.roundCaps;
254
+ // Selected rings last, so a thickened ring is never under its neighbour.
255
+ const order = [...state.rows.keys()].sort((a, b) => g.popOf(a) - g.popOf(b));
256
+ for (const i of order) {
257
+ const grow = thickness * GROW * g.popOf(i);
258
+ const rOut = g.ringOuter(i) + grow;
259
+ const rIn = g.ringOuter(i) - thickness - grow;
260
+ const opacity = g.ringOpacity(i);
261
+ if (state.marks.track && g.sweepRad > 0) {
262
+ ctx.draw(new Graphics2D().path(bandPath(a0, a0 + g.sweepRad, rIn, rOut, round).toPathState(frame)).fill(state.trackFill).opacity(opacity));
263
+ }
264
+ const span = g.sweepRad * fractions[i] * g.progress(i, state.growth);
265
+ if (span <= 1e-4)
266
+ continue;
267
+ ctx.draw(new Graphics2D().path(bandPath(a0, a0 + span, rIn, rOut, round).toPathState(frame)).fill(fills[i] ?? []).opacity(opacity));
268
+ }
269
+ }
270
+ ctx.children();
271
+ },
272
+ });
273
+ //# sourceMappingURL=radial-rings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radial-rings.js","sourceRoot":"","sources":["../../src/radial-chart/radial-rings.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Graphics2D,\n PathBuilder,\n Rect,\n Text,\n Types,\n clamp,\n component2d,\n fillOps,\n textOps,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D,\n type Node2DProps as _Node2DProps,\n type Fill as _Fill,\n type FillResolved as _FillResolved,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { DEG, ringFills, ringPoint, selectionFade, type CenterConfig } from \"../kit/polar\"\nimport { radialFractions, type RadialMarks, type RadialRow } from \"./shared\"\n\n/** Share of the entrance one ring's sweep starts after the last. */\nconst STAGGER = 0.12\n/** A selected ring's thickness grows by this share of itself, on each side. */\nconst GROW = 0.12\nconst ARC_STEP = 4 * DEG\nconst LABEL_PAD = 10\nconst CENTER_FIT = 0.78\nconst GLYPH_RATIO = 0.62\n/** How far the radius is held in from the box, for the sake of a ring's growth. */\nconst EDGE = 4\n\nconst DEFAULT_MARKS: RadialMarks = { innerRadius: 0.3, ringGap: 8, sweep: 360, startAngle: 0, track: true, roundCaps: true }\nconst DEFAULT_LABEL: TextStyle = { fontSize: 28, fontWeight: 500, textAlign: \"center\", fill: \"#a1a1aa\" }\nconst DEFAULT_CENTER: CenterConfig = {\n labelStyle: { fontSize: 50, fontWeight: 800, textAlign: \"center\", fill: \"#f4f4f5\" },\n captionStyle: { fontSize: 22, fontWeight: 500, textAlign: \"center\", fill: \"#a1a1aa\" },\n}\n\ninterface RingsState {\n readonly rows: RadialRow[]\n readonly max: number\n readonly marks: RadialMarks\n readonly label: TextStyle\n readonly centerStyle: CenterConfig\n readonly ringLabels: boolean\n readonly pops: readonly number[]\n readonly dimOpacity: number\n readonly centerLabel: string\n readonly centerCaption: string\n}\n\nfunction sizeOf(style: TextStyle, fallback: number): number {\n return typeof style.fontSize === \"number\" ? style.fontSize : fallback\n}\n\n/** Every radius and angle for one box, derived wherever read so the drawing and the labels cannot disagree. */\nexport class RadialGeometry {\n constructor(\n private readonly s: RingsState,\n private readonly box: { readonly width: number; readonly height: number }\n ) {}\n\n get count(): number {\n return this.s.rows.length\n }\n\n get outerRadius(): number {\n return Math.max(0, Math.min(this.box.width, this.box.height) / 2 - EDGE)\n }\n\n get holeRadius(): number {\n return clamp(this.s.marks.innerRadius, 0, 0.9) * this.outerRadius\n }\n\n /** Radial depth of one ring. */\n get thickness(): number {\n const n = this.count\n if (n === 0) return 0\n return Math.max(0, (this.outerRadius - this.holeRadius - (n - 1) * Math.max(0, this.s.marks.ringGap)) / n)\n }\n\n /** Rim of ring `index`; ring 0 is the outermost. */\n ringOuter(index: number): number {\n return this.outerRadius - index * (this.thickness + Math.max(0, this.s.marks.ringGap))\n }\n\n ringMid(index: number): number {\n return this.ringOuter(index) - this.thickness / 2\n }\n\n get fractions(): number[] {\n return radialFractions(this.s.rows, this.s.max)\n }\n\n get startRad(): number {\n return this.s.marks.startAngle * DEG\n }\n\n get sweepRad(): number {\n return clamp(this.s.marks.sweep, 0, 360) * DEG\n }\n\n popOf(index: number): number {\n return clamp(this.s.pops[index] ?? 0, 0, 1)\n }\n\n get selectionStrength(): number {\n let strongest = 0\n for (let i = 0; i < this.count; i++) strongest = Math.max(strongest, this.popOf(i))\n return strongest\n }\n\n ringOpacity(index: number): number {\n return selectionFade(this.selectionStrength, this.s.dimOpacity, this.popOf(index))\n }\n\n /** How far ring `index`'s sweep has come at entrance progress `growth`: rings start in turn, outer first. */\n progress(index: number, growth: number): number {\n const total = 1 + STAGGER * Math.max(0, this.count - 1)\n return clamp(growth * total - index * STAGGER, 0, 1)\n }\n\n get labelFont(): number {\n return Math.max(1, Math.min(sizeOf(this.s.label, 28), this.thickness * 0.62))\n }\n\n /** Where ring `index`'s caption is pinned: just behind its start, reading away from the sweep. */\n labelAnchor(index: number): { at: Vector2; side: 1 | -1 } {\n const a = this.startRad\n const back = { x: -Math.cos(a), y: Math.sin(a) }\n const p = ringPoint(this.ringMid(index), a)\n const side: 1 | -1 = back.x <= 0 ? -1 : 1\n return { at: { x: p.x + back.x * LABEL_PAD, y: p.y + back.y * LABEL_PAD }, side }\n }\n\n get centerFont(): number {\n const { centerStyle, centerLabel, centerCaption } = this.s\n const wanted = sizeOf(centerStyle.labelStyle, 50)\n const hole = this.holeRadius\n if (hole <= 0) return wanted\n const ratio = sizeOf(centerStyle.captionStyle, 22) / Math.max(1, wanted)\n const glyphs = Math.max(1, centerLabel.length, centerCaption.length * ratio)\n return Math.min(wanted, (hole * 2 * CENTER_FIT) / (glyphs * GLYPH_RATIO))\n }\n}\n\n/**\n * An arc band between `rIn` and `rOut` over `a0`..`a1` (radians clockwise from\n * 12 o'clock), the ends squared or capped with half circles. A band shorter\n * than its caps is two overlapping caps, which fill as a pill.\n */\nexport function bandPath(a0: number, a1: number, rIn: number, rOut: number, round: boolean): PathBuilder {\n const path = new PathBuilder()\n const mid = (rIn + rOut) / 2\n const cap = (rOut - rIn) / 2\n const delta = round && mid > 0 ? Math.min(cap / mid, (a1 - a0) / 2) : 0\n const from = a0 + delta\n const to = a1 - delta\n const steps = Math.max(2, Math.ceil(Math.abs(to - from) / ARC_STEP))\n const arc = (r: number, x: number, y: number, first: boolean) => {\n for (let i = 0; i <= steps; i++) {\n const p = ringPoint(r, x + ((y - x) * i) / steps)\n if (first && i === 0) path.moveTo(p.x, p.y)\n else path.lineTo(p.x, p.y)\n }\n }\n // The end at `angle` rounded through a tip `direction` along the sweep.\n const semicircle = (angle: number, direction: 1 | -1, fromOuter: boolean) => {\n const centre = ringPoint(mid, angle)\n const radial = { x: Math.sin(angle), y: Math.cos(angle) }\n const tangent = { x: Math.cos(angle) * direction, y: -Math.sin(angle) * direction }\n const turns = 12\n for (let i = 1; i < turns; i++) {\n const phi = (Math.PI * i) / turns\n const r = fromOuter ? Math.cos(phi) : -Math.cos(phi)\n path.lineTo(centre.x + cap * (radial.x * r + tangent.x * Math.sin(phi)), centre.y + cap * (radial.y * r + tangent.y * Math.sin(phi)))\n }\n }\n arc(rOut, from, to, true)\n if (round) semicircle(to, 1, true)\n arc(rIn, to, from, false)\n if (round) semicircle(from, -1, false)\n path.close()\n return path\n}\n\nconst NO_BOX = { width: 0, height: 0 }\n\nfunction labelBlocks(s: RingsState & { readonly labelReveal: number; readonly centerReveal: number }) {\n const blocks: Node2D[] = []\n const geometry = () => new RadialGeometry(s, (blocks[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? NO_BOX)\n const side = geometry().labelAnchor(0).side\n\n s.rows.forEach((_, index) => {\n const anchor = () => geometry().labelAnchor(index).at\n blocks.push(\n new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n align: side > 0 ? \"centerLeft\" : \"centerRight\",\n opacity: () => (s.ringLabels ? clamp(s.labelReveal, 0, 1) * geometry().ringOpacity(index) : 0),\n centerLeft: side > 0 ? anchor : undefined,\n centerRight: side > 0 ? undefined : anchor,\n children: [\n new Text({\n ...textOps.styleProps(s.label),\n text: () => s.rows[index]?.category ?? \"\",\n textAlign: side > 0 ? \"left\" : \"right\",\n fontSize: () => geometry().labelFont,\n }),\n ],\n })\n )\n })\n\n if (s.centerLabel !== \"\" || s.centerCaption !== \"\") {\n const children: Node2D[] = []\n if (s.centerLabel !== \"\") {\n children.push(new Text({ ...textOps.styleProps(s.centerStyle.labelStyle), text: s.centerLabel, fontSize: () => geometry().centerFont }))\n }\n if (s.centerCaption !== \"\") {\n children.push(\n new Text({\n ...textOps.styleProps(s.centerStyle.captionStyle),\n text: s.centerCaption,\n fontSize: () => geometry().centerFont * (sizeOf(s.centerStyle.captionStyle, 22) / sizeOf(s.centerStyle.labelStyle, 50)),\n })\n )\n }\n blocks.push(\n new Rect({\n flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: \"center\",\n opacity: () => clamp(s.centerReveal, 0, 1),\n center: { x: 0, y: 0 },\n children,\n })\n )\n }\n return asParts(blocks)\n}\n\n/**\n * The rings: row `i` is the `i`-th ring from the outside, an arc from\n * `startAngle` whose sweep is `value / max` of the full sweep, over an\n * optional full-sweep track. A selected ring thickens and the rest dims.\n */\nexport const RadialRings = component2d({\n key: \"radialRings\",\n state: {\n rows: fresh<RadialRow[]>(() => []),\n max: 0,\n marks: Types.custom<RadialMarks>(DEFAULT_MARKS),\n label: Types.custom<TextStyle>(DEFAULT_LABEL),\n centerStyle: Types.custom<CenterConfig>(DEFAULT_CENTER),\n /** The empty-ring colour drawn under each arc. */\n trackFill: Types.fill(\"white/15\"),\n ringLabels: Types.boolean(false),\n /** Entrance progress `[0, 1]`: rings sweep in turn. */\n growth: 1,\n labelReveal: 1,\n pops: fresh<number[]>(() => []),\n dimOpacity: 0.25,\n centerLabel: \"\",\n centerCaption: \"\",\n centerReveal: 1,\n },\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => labelBlocks(state),\n load: (loader, state) => loadPaints(loader, { fills: [...ringFills(state.rows), fillOps.resolve(state.trackFill)] }),\n commands: {\n /** Sweep the rings in, outer first. */\n sweep: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ growth: ease(t) }))\n },\n /** Fade the ring captions up. */\n revealLabels: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ labelReveal: ease(t) }))\n },\n /** Fade the centre summary up. */\n revealCenter: (timeline, state) => {\n const ease = frameEase(timeline)\n const from = clamp(state.centerReveal, 0, 1)\n return timeline.animate((t) => ({ centerReveal: from + (1 - from) * ease(t) }))\n },\n },\n render: (ctx, state) => {\n const g = new RadialGeometry(state, ctx.size)\n const thickness = g.thickness\n if (g.outerRadius > 0 && g.count > 0 && thickness > 0) {\n const half = g.outerRadius + EDGE + 1\n const frame = { centerBounds: [-half, -half, half, half] as [number, number, number, number], width: half * 2, height: half * 2 }\n const fills = ringFills(state.rows)\n const fractions = g.fractions\n const a0 = g.startRad\n const round = state.marks.roundCaps\n // Selected rings last, so a thickened ring is never under its neighbour.\n const order = [...state.rows.keys()].sort((a, b) => g.popOf(a) - g.popOf(b))\n for (const i of order) {\n const grow = thickness * GROW * g.popOf(i)\n const rOut = g.ringOuter(i) + grow\n const rIn = g.ringOuter(i) - thickness - grow\n const opacity = g.ringOpacity(i)\n if (state.marks.track && g.sweepRad > 0) {\n ctx.draw(new Graphics2D().path(bandPath(a0, a0 + g.sweepRad, rIn, rOut, round).toPathState(frame)).fill(state.trackFill).opacity(opacity))\n }\n const span = g.sweepRad * fractions[i] * g.progress(i, state.growth)\n if (span <= 1e-4) continue\n ctx.draw(new Graphics2D().path(bandPath(a0, a0 + span, rIn, rOut, round).toPathState(frame)).fill(fills[i] ?? []).opacity(opacity))\n }\n }\n ctx.children()\n },\n})\n\nexport type RadialRings = NodeOf<typeof RadialRings>\n"]}
@@ -0,0 +1,73 @@
1
+ import { type DataRecord, type TextStyle, type Fill as _Fill, type FillResolved as _FillResolved } from "@motionscript/sdk";
2
+ import { type ChartStyleBase } from "../kit/style.js";
3
+ import type { LegendConfig } from "../kit/shared.js";
4
+ import type { CenterConfig, PieSlice } from "../kit/polar.js";
5
+ /** One ring: a row's caption, magnitude and paint. The same shape as a pie's slice, so the legend is shared. */
6
+ export type RadialRow = PieSlice;
7
+ export declare function resolveRadialRows(data: readonly DataRecord[], category: string, value: string, style: Pick<ChartStyleBase, "palette">): RadialRow[];
8
+ /** A row's plotted value: negatives and non-finite values count as 0. */
9
+ export declare function rowValue(row: RadialRow): number;
10
+ /** What a full sweep stands for: `max` when it is positive, else the largest value (1 when every value is 0). */
11
+ export declare function radialMax(rows: readonly RadialRow[], max: number): number;
12
+ /** The share of a full sweep each row fills, `[0, 1]`. */
13
+ export declare function radialFractions(rows: readonly RadialRow[], max: number): number[];
14
+ /** The ring geometry and look, from `style`. */
15
+ export interface RadialMarks {
16
+ innerRadius: number;
17
+ ringGap: number;
18
+ /** Degrees of a full ring. */
19
+ sweep: number;
20
+ /** Degrees clockwise from 12 o'clock. */
21
+ startAngle: number;
22
+ track: boolean;
23
+ roundCaps: boolean;
24
+ }
25
+ export interface RadialStyle extends ChartStyleBase {
26
+ readonly innerRadius: number;
27
+ readonly ringGap: number;
28
+ readonly sweep: number;
29
+ readonly startAngle: number;
30
+ readonly track: _FillResolved[];
31
+ readonly roundCaps: boolean;
32
+ readonly ringLabels: boolean;
33
+ }
34
+ export declare const RADIAL_STYLE_GROUPS: {
35
+ colors: {
36
+ palette: import("@motionscript/sdk").Type<_Fill[], _FillResolved[][], false>;
37
+ foreground: import("@motionscript/sdk").Type<_Fill, _FillResolved[], false>;
38
+ muted: import("@motionscript/sdk").Type<_Fill, _FillResolved[], false>;
39
+ background: import("@motionscript/sdk").Type<_Fill, _FillResolved[], false>;
40
+ };
41
+ text: {
42
+ fontFamily: import("@motionscript/sdk").Type<string, string, false>;
43
+ fontSize: import("@motionscript/sdk").Type<number, number, false>;
44
+ fontWeight: import("@motionscript/sdk").Type<number, number, false>;
45
+ };
46
+ marks: {
47
+ /** Where the innermost ring starts, as a fraction of the outer radius. */
48
+ innerRadius: import("@motionscript/sdk").Type<number, number, false>;
49
+ /** Space between neighbouring rings, px. */
50
+ ringGap: import("@motionscript/sdk").Type<number, number, false>;
51
+ /** Degrees a full ring spans: 360 a circle, 270 a gauge-like open ring. */
52
+ sweep: import("@motionscript/sdk").Type<number, number, false>;
53
+ /** Degrees clockwise from 12 o'clock where every ring begins. */
54
+ startAngle: import("@motionscript/sdk").Type<number, number, false>;
55
+ /** The full ring under each arc; empty draws none. */
56
+ track: import("@motionscript/sdk").Type<_Fill, _FillResolved[], false>;
57
+ /** Round the ends of the arcs and tracks. */
58
+ roundCaps: import("@motionscript/sdk").Type<boolean, boolean, false>;
59
+ };
60
+ labels: {
61
+ /** The category written at each ring's start, in the muted colour. */
62
+ ringLabels: import("@motionscript/sdk").Type<boolean, boolean, false>;
63
+ };
64
+ legend: {
65
+ legend: import("@motionscript/sdk").Type<"right" | "top" | "bottom" | "none", "right" | "top" | "bottom" | "none", false>;
66
+ swatch: import("@motionscript/sdk").Type<"square" | "circle" | "line", "square" | "circle" | "line", false>;
67
+ };
68
+ };
69
+ export declare function marksOf(style: RadialStyle): RadialMarks;
70
+ export declare function legendOf(style: RadialStyle): LegendConfig;
71
+ export declare function ringLabelOf(style: RadialStyle): TextStyle;
72
+ export declare function centerOf(style: RadialStyle): CenterConfig;
73
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/radial-chart/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,IAAI,IAAI,KAAK,EAAE,KAAK,YAAY,IAAI,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEzI,OAAO,EAA0D,KAAK,cAAc,EAAe,MAAM,cAAc,CAAA;AACvH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE1D,gHAAgH;AAChH,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAA;AAEhC,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG,SAAS,EAAE,CAEnJ;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAE/C;AAED,iHAAiH;AACjH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAKzE;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjF;AAED,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAY,SAAQ,cAAc;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,CAAA;IAC/B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;CAC7B;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;QAI5B,0EAA0E;;QAE1E,4CAA4C;;QAE5C,2EAA2E;;QAE3E,iEAAiE;;QAEjE,sDAAsD;;QAEtD,6CAA6C;;;;QAI7C,sEAAsE;;;;;;;CAIzE,CAAA;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CASvD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAEzD;AAMD,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAEzD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAWzD"}
@@ -0,0 +1,79 @@
1
+ import { Types, clamp } from "@motionscript/sdk";
2
+ import { CHART_COLORS, CHART_LEGEND, CHART_TEXT, legendConfigOf, paletteFill } from "../kit/style.js";
3
+ import { num, text } from "../kit/shared.js";
4
+ export function resolveRadialRows(data, category, value, style) {
5
+ return data.map((row, i) => ({ category: text(row, category), value: num(row, value), fill: paletteFill(style, i) }));
6
+ }
7
+ /** A row's plotted value: negatives and non-finite values count as 0. */
8
+ export function rowValue(row) {
9
+ return row.value > 0 && Number.isFinite(row.value) ? row.value : 0;
10
+ }
11
+ /** What a full sweep stands for: `max` when it is positive, else the largest value (1 when every value is 0). */
12
+ export function radialMax(rows, max) {
13
+ if (max > 0 && Number.isFinite(max))
14
+ return max;
15
+ let largest = 0;
16
+ for (const row of rows)
17
+ largest = Math.max(largest, rowValue(row));
18
+ return largest > 0 ? largest : 1;
19
+ }
20
+ /** The share of a full sweep each row fills, `[0, 1]`. */
21
+ export function radialFractions(rows, max) {
22
+ const top = radialMax(rows, max);
23
+ return rows.map((row) => clamp(rowValue(row) / top, 0, 1));
24
+ }
25
+ export const RADIAL_STYLE_GROUPS = {
26
+ colors: CHART_COLORS,
27
+ text: CHART_TEXT,
28
+ marks: {
29
+ /** Where the innermost ring starts, as a fraction of the outer radius. */
30
+ innerRadius: Types.number(0.3, { min: 0, max: 0.9, step: 0.05 }),
31
+ /** Space between neighbouring rings, px. */
32
+ ringGap: Types.number(8, { min: 0, max: 60, step: 1, unit: "px" }),
33
+ /** Degrees a full ring spans: 360 a circle, 270 a gauge-like open ring. */
34
+ sweep: Types.number(360, { min: 30, max: 360, step: 5, unit: "deg" }),
35
+ /** Degrees clockwise from 12 o'clock where every ring begins. */
36
+ startAngle: Types.number(0, { min: -360, max: 360, step: 5, unit: "deg" }),
37
+ /** The full ring under each arc; empty draws none. */
38
+ track: Types.fill("white/10"),
39
+ /** Round the ends of the arcs and tracks. */
40
+ roundCaps: Types.boolean(true),
41
+ },
42
+ labels: {
43
+ /** The category written at each ring's start, in the muted colour. */
44
+ ringLabels: Types.boolean(false),
45
+ },
46
+ legend: CHART_LEGEND,
47
+ };
48
+ export function marksOf(style) {
49
+ return {
50
+ innerRadius: style.innerRadius,
51
+ ringGap: style.ringGap,
52
+ sweep: style.sweep,
53
+ startAngle: style.startAngle,
54
+ track: style.track.length > 0,
55
+ roundCaps: style.roundCaps,
56
+ };
57
+ }
58
+ export function legendOf(style) {
59
+ return legendConfigOf(style);
60
+ }
61
+ function face(style) {
62
+ return style.fontFamily ? { fontFamily: style.fontFamily } : {};
63
+ }
64
+ export function ringLabelOf(style) {
65
+ return { ...face(style), fontSize: style.fontSize, fontWeight: style.fontWeight, textAlign: "center", fill: style.muted };
66
+ }
67
+ export function centerOf(style) {
68
+ return {
69
+ labelStyle: {
70
+ ...face(style),
71
+ fontSize: Math.round(style.fontSize * 1.8),
72
+ fontWeight: Math.min(900, style.fontWeight + 300),
73
+ textAlign: "center",
74
+ fill: style.foreground,
75
+ },
76
+ captionStyle: { ...face(style), fontSize: Math.round(style.fontSize * 0.8), fontWeight: style.fontWeight, textAlign: "center", fill: style.muted },
77
+ };
78
+ }
79
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/radial-chart/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { Types, clamp, type DataRecord, type TextStyle, type Fill as _Fill, type FillResolved as _FillResolved } from \"@motionscript/sdk\"\n\nimport { CHART_COLORS, CHART_LEGEND, CHART_TEXT, legendConfigOf, type ChartStyleBase, paletteFill } from \"../kit/style\"\nimport type { LegendConfig } from \"../kit/shared\"\nimport { num, text } from \"../kit/shared\"\nimport type { CenterConfig, PieSlice } from \"../kit/polar\"\n\n/** One ring: a row's caption, magnitude and paint. The same shape as a pie's slice, so the legend is shared. */\nexport type RadialRow = PieSlice\n\nexport function resolveRadialRows(data: readonly DataRecord[], category: string, value: string, style: Pick<ChartStyleBase, \"palette\">): RadialRow[] {\n return data.map((row, i) => ({ category: text(row, category), value: num(row, value), fill: paletteFill(style, i) }))\n}\n\n/** A row's plotted value: negatives and non-finite values count as 0. */\nexport function rowValue(row: RadialRow): number {\n return row.value > 0 && Number.isFinite(row.value) ? row.value : 0\n}\n\n/** What a full sweep stands for: `max` when it is positive, else the largest value (1 when every value is 0). */\nexport function radialMax(rows: readonly RadialRow[], max: number): number {\n if (max > 0 && Number.isFinite(max)) return max\n let largest = 0\n for (const row of rows) largest = Math.max(largest, rowValue(row))\n return largest > 0 ? largest : 1\n}\n\n/** The share of a full sweep each row fills, `[0, 1]`. */\nexport function radialFractions(rows: readonly RadialRow[], max: number): number[] {\n const top = radialMax(rows, max)\n return rows.map((row) => clamp(rowValue(row) / top, 0, 1))\n}\n\n/** The ring geometry and look, from `style`. */\nexport interface RadialMarks {\n innerRadius: number\n ringGap: number\n /** Degrees of a full ring. */\n sweep: number\n /** Degrees clockwise from 12 o'clock. */\n startAngle: number\n track: boolean\n roundCaps: boolean\n}\n\nexport interface RadialStyle extends ChartStyleBase {\n readonly innerRadius: number\n readonly ringGap: number\n readonly sweep: number\n readonly startAngle: number\n readonly track: _FillResolved[]\n readonly roundCaps: boolean\n readonly ringLabels: boolean\n}\n\nexport const RADIAL_STYLE_GROUPS = {\n colors: CHART_COLORS,\n text: CHART_TEXT,\n marks: {\n /** Where the innermost ring starts, as a fraction of the outer radius. */\n innerRadius: Types.number(0.3, { min: 0, max: 0.9, step: 0.05 }),\n /** Space between neighbouring rings, px. */\n ringGap: Types.number(8, { min: 0, max: 60, step: 1, unit: \"px\" }),\n /** Degrees a full ring spans: 360 a circle, 270 a gauge-like open ring. */\n sweep: Types.number(360, { min: 30, max: 360, step: 5, unit: \"deg\" }),\n /** Degrees clockwise from 12 o'clock where every ring begins. */\n startAngle: Types.number(0, { min: -360, max: 360, step: 5, unit: \"deg\" }),\n /** The full ring under each arc; empty draws none. */\n track: Types.fill(\"white/10\"),\n /** Round the ends of the arcs and tracks. */\n roundCaps: Types.boolean(true),\n },\n labels: {\n /** The category written at each ring's start, in the muted colour. */\n ringLabels: Types.boolean(false),\n },\n legend: CHART_LEGEND,\n}\n\nexport function marksOf(style: RadialStyle): RadialMarks {\n return {\n innerRadius: style.innerRadius,\n ringGap: style.ringGap,\n sweep: style.sweep,\n startAngle: style.startAngle,\n track: style.track.length > 0,\n roundCaps: style.roundCaps,\n }\n}\n\nexport function legendOf(style: RadialStyle): LegendConfig {\n return legendConfigOf(style)\n}\n\nfunction face(style: RadialStyle): Pick<TextStyle, \"fontFamily\"> {\n return style.fontFamily ? { fontFamily: style.fontFamily } : {}\n}\n\nexport function ringLabelOf(style: RadialStyle): TextStyle {\n return { ...face(style), fontSize: style.fontSize, fontWeight: style.fontWeight, textAlign: \"center\", fill: style.muted }\n}\n\nexport function centerOf(style: RadialStyle): CenterConfig {\n return {\n labelStyle: {\n ...face(style),\n fontSize: Math.round(style.fontSize * 1.8),\n fontWeight: Math.min(900, style.fontWeight + 300),\n textAlign: \"center\",\n fill: style.foreground,\n },\n captionStyle: { ...face(style), fontSize: Math.round(style.fontSize * 0.8), fontWeight: style.fontWeight, textAlign: \"center\", fill: style.muted },\n }\n}\n"]}