@invinite-org/chartlang-adapter-kit 1.3.0 → 1.5.0

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 (155) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +7 -0
  3. package/dist/canvas/index.d.ts +5 -0
  4. package/dist/canvas/index.d.ts.map +1 -0
  5. package/dist/canvas/index.js +5 -0
  6. package/dist/canvas/index.js.map +1 -0
  7. package/dist/canvas/mockContext.d.ts +168 -0
  8. package/dist/canvas/mockContext.d.ts.map +1 -0
  9. package/dist/canvas/mockContext.js +198 -0
  10. package/dist/canvas/mockContext.js.map +1 -0
  11. package/dist/canvas/paintPrimitive.d.ts +35 -0
  12. package/dist/canvas/paintPrimitive.d.ts.map +1 -0
  13. package/dist/canvas/paintPrimitive.js +171 -0
  14. package/dist/canvas/paintPrimitive.js.map +1 -0
  15. package/dist/canvas/renderCtx.d.ts +40 -0
  16. package/dist/canvas/renderCtx.d.ts.map +1 -0
  17. package/dist/canvas/renderCtx.js +4 -0
  18. package/dist/canvas/renderCtx.js.map +1 -0
  19. package/dist/geometry/_lib/arrowhead.d.ts +18 -0
  20. package/dist/geometry/_lib/arrowhead.d.ts.map +1 -0
  21. package/dist/geometry/_lib/arrowhead.js +38 -0
  22. package/dist/geometry/_lib/arrowhead.js.map +1 -0
  23. package/dist/geometry/_lib/bezier.d.ts +57 -0
  24. package/dist/geometry/_lib/bezier.d.ts.map +1 -0
  25. package/dist/geometry/_lib/bezier.js +84 -0
  26. package/dist/geometry/_lib/bezier.js.map +1 -0
  27. package/dist/geometry/_lib/chevron.d.ts +29 -0
  28. package/dist/geometry/_lib/chevron.d.ts.map +1 -0
  29. package/dist/geometry/_lib/chevron.js +37 -0
  30. package/dist/geometry/_lib/chevron.js.map +1 -0
  31. package/dist/geometry/_lib/dash.d.ts +30 -0
  32. package/dist/geometry/_lib/dash.d.ts.map +1 -0
  33. package/dist/geometry/_lib/dash.js +40 -0
  34. package/dist/geometry/_lib/dash.js.map +1 -0
  35. package/dist/geometry/_lib/fibLevels.d.ts +36 -0
  36. package/dist/geometry/_lib/fibLevels.d.ts.map +1 -0
  37. package/dist/geometry/_lib/fibLevels.js +44 -0
  38. package/dist/geometry/_lib/fibLevels.js.map +1 -0
  39. package/dist/geometry/_lib/gannLevels.d.ts +54 -0
  40. package/dist/geometry/_lib/gannLevels.d.ts.map +1 -0
  41. package/dist/geometry/_lib/gannLevels.js +88 -0
  42. package/dist/geometry/_lib/gannLevels.js.map +1 -0
  43. package/dist/geometry/_lib/lineExtend.d.ts +31 -0
  44. package/dist/geometry/_lib/lineExtend.d.ts.map +1 -0
  45. package/dist/geometry/_lib/lineExtend.js +48 -0
  46. package/dist/geometry/_lib/lineExtend.js.map +1 -0
  47. package/dist/geometry/_lib/namedPolyline.d.ts +25 -0
  48. package/dist/geometry/_lib/namedPolyline.d.ts.map +1 -0
  49. package/dist/geometry/_lib/namedPolyline.js +64 -0
  50. package/dist/geometry/_lib/namedPolyline.js.map +1 -0
  51. package/dist/geometry/_lib/pitchforkGeom.d.ts +46 -0
  52. package/dist/geometry/_lib/pitchforkGeom.d.ts.map +1 -0
  53. package/dist/geometry/_lib/pitchforkGeom.js +70 -0
  54. package/dist/geometry/_lib/pitchforkGeom.js.map +1 -0
  55. package/dist/geometry/_lib/shapeStyle.d.ts +21 -0
  56. package/dist/geometry/_lib/shapeStyle.d.ts.map +1 -0
  57. package/dist/geometry/_lib/shapeStyle.js +41 -0
  58. package/dist/geometry/_lib/shapeStyle.js.map +1 -0
  59. package/dist/geometry/_lib/strokeStyle.d.ts +34 -0
  60. package/dist/geometry/_lib/strokeStyle.d.ts.map +1 -0
  61. package/dist/geometry/_lib/strokeStyle.js +26 -0
  62. package/dist/geometry/_lib/strokeStyle.js.map +1 -0
  63. package/dist/geometry/_lib/textStyle.d.ts +70 -0
  64. package/dist/geometry/_lib/textStyle.d.ts.map +1 -0
  65. package/dist/geometry/_lib/textStyle.js +78 -0
  66. package/dist/geometry/_lib/textStyle.js.map +1 -0
  67. package/dist/geometry/decompose.d.ts +28 -0
  68. package/dist/geometry/decompose.d.ts.map +1 -0
  69. package/dist/geometry/decompose.js +176 -0
  70. package/dist/geometry/decompose.js.map +1 -0
  71. package/dist/geometry/index.d.ts +4 -0
  72. package/dist/geometry/index.d.ts.map +1 -0
  73. package/dist/geometry/index.js +5 -0
  74. package/dist/geometry/index.js.map +1 -0
  75. package/dist/geometry/kinds/annotations.d.ts +77 -0
  76. package/dist/geometry/kinds/annotations.d.ts.map +1 -0
  77. package/dist/geometry/kinds/annotations.js +219 -0
  78. package/dist/geometry/kinds/annotations.js.map +1 -0
  79. package/dist/geometry/kinds/boxes.d.ts +116 -0
  80. package/dist/geometry/kinds/boxes.d.ts.map +1 -0
  81. package/dist/geometry/kinds/boxes.js +285 -0
  82. package/dist/geometry/kinds/boxes.js.map +1 -0
  83. package/dist/geometry/kinds/channels.d.ts +72 -0
  84. package/dist/geometry/kinds/channels.d.ts.map +1 -0
  85. package/dist/geometry/kinds/channels.js +148 -0
  86. package/dist/geometry/kinds/channels.js.map +1 -0
  87. package/dist/geometry/kinds/containers.d.ts +54 -0
  88. package/dist/geometry/kinds/containers.d.ts.map +1 -0
  89. package/dist/geometry/kinds/containers.js +268 -0
  90. package/dist/geometry/kinds/containers.js.map +1 -0
  91. package/dist/geometry/kinds/curves.d.ts +53 -0
  92. package/dist/geometry/kinds/curves.d.ts.map +1 -0
  93. package/dist/geometry/kinds/curves.js +110 -0
  94. package/dist/geometry/kinds/curves.js.map +1 -0
  95. package/dist/geometry/kinds/cycles.d.ts +52 -0
  96. package/dist/geometry/kinds/cycles.d.ts.map +1 -0
  97. package/dist/geometry/kinds/cycles.js +158 -0
  98. package/dist/geometry/kinds/cycles.js.map +1 -0
  99. package/dist/geometry/kinds/elliott.d.ts +73 -0
  100. package/dist/geometry/kinds/elliott.d.ts.map +1 -0
  101. package/dist/geometry/kinds/elliott.js +116 -0
  102. package/dist/geometry/kinds/elliott.js.map +1 -0
  103. package/dist/geometry/kinds/fibonacci.d.ts +166 -0
  104. package/dist/geometry/kinds/fibonacci.d.ts.map +1 -0
  105. package/dist/geometry/kinds/fibonacci.js +458 -0
  106. package/dist/geometry/kinds/fibonacci.js.map +1 -0
  107. package/dist/geometry/kinds/freehand.d.ts +53 -0
  108. package/dist/geometry/kinds/freehand.d.ts.map +1 -0
  109. package/dist/geometry/kinds/freehand.js +115 -0
  110. package/dist/geometry/kinds/freehand.js.map +1 -0
  111. package/dist/geometry/kinds/gann.d.ts +63 -0
  112. package/dist/geometry/kinds/gann.d.ts.map +1 -0
  113. package/dist/geometry/kinds/gann.js +153 -0
  114. package/dist/geometry/kinds/gann.js.map +1 -0
  115. package/dist/geometry/kinds/lines.d.ts +90 -0
  116. package/dist/geometry/kinds/lines.d.ts.map +1 -0
  117. package/dist/geometry/kinds/lines.js +201 -0
  118. package/dist/geometry/kinds/lines.js.map +1 -0
  119. package/dist/geometry/kinds/marker.d.ts +21 -0
  120. package/dist/geometry/kinds/marker.d.ts.map +1 -0
  121. package/dist/geometry/kinds/marker.js +47 -0
  122. package/dist/geometry/kinds/marker.js.map +1 -0
  123. package/dist/geometry/kinds/patterns.d.ts +85 -0
  124. package/dist/geometry/kinds/patterns.d.ts.map +1 -0
  125. package/dist/geometry/kinds/patterns.js +133 -0
  126. package/dist/geometry/kinds/patterns.js.map +1 -0
  127. package/dist/geometry/kinds/pitchforks.d.ts +36 -0
  128. package/dist/geometry/kinds/pitchforks.d.ts.map +1 -0
  129. package/dist/geometry/kinds/pitchforks.js +109 -0
  130. package/dist/geometry/kinds/pitchforks.js.map +1 -0
  131. package/dist/geometry/project.d.ts +50 -0
  132. package/dist/geometry/project.d.ts.map +1 -0
  133. package/dist/geometry/project.js +62 -0
  134. package/dist/geometry/project.js.map +1 -0
  135. package/dist/geometry/types.d.ts +146 -0
  136. package/dist/geometry/types.d.ts.map +1 -0
  137. package/dist/geometry/types.js +4 -0
  138. package/dist/geometry/types.js.map +1 -0
  139. package/dist/index.d.ts +4 -0
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +2 -0
  142. package/dist/index.js.map +1 -1
  143. package/dist/interaction/domWiring.d.ts +90 -0
  144. package/dist/interaction/domWiring.d.ts.map +1 -0
  145. package/dist/interaction/domWiring.js +113 -0
  146. package/dist/interaction/domWiring.js.map +1 -0
  147. package/dist/interaction/index.d.ts +5 -0
  148. package/dist/interaction/index.d.ts.map +1 -0
  149. package/dist/interaction/index.js +5 -0
  150. package/dist/interaction/index.js.map +1 -0
  151. package/dist/interaction/viewController.d.ts +120 -0
  152. package/dist/interaction/viewController.d.ts.map +1 -0
  153. package/dist/interaction/viewController.js +112 -0
  154. package/dist/interaction/viewController.js.map +1 -0
  155. package/package.json +8 -1
@@ -0,0 +1,458 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ //
4
+ // Fibonacci geometry moved from the canvas2d adapter's per-kind
5
+ // renderers
6
+ // examples/canvas2d-adapter/src/render/draw/{fibRetracement,
7
+ // fibTrendExtension,fibChannel,fibTimeZone,fibWedge,fibSpeedFan,
8
+ // fibSpeedArcs,fibSpiral,fibCircles,fibTrendTime}.ts.
9
+ // The originating math is invinite's fib-* tools (commit
10
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
11
+ // MIT for chartlang.
12
+ import { sampleCubic } from "../_lib/bezier.js";
13
+ import { SOLID_DASH } from "../_lib/dash.js";
14
+ import { FIB_LEVELS, formatLevel } from "../_lib/fibLevels.js";
15
+ import { extendLineSegment } from "../_lib/lineExtend.js";
16
+ import { priceToY, timeToX, worldPointToPixel } from "../project.js";
17
+ const DEFAULT_COLOR = "#facc15";
18
+ const DEFAULT_LINE_WIDTH = 1;
19
+ const LABEL_FONT = "12px sans-serif";
20
+ const LABEL_OFFSET_PX = 4;
21
+ const LABEL_TOP_PX = 12;
22
+ const LABEL_OFFSET_FRACTION = 0.25;
23
+ const TAU = Math.PI * 2;
24
+ /**
25
+ * Build a level-line label `text` primitive. Shared by every fib
26
+ * decomposer so the font / colour / alignment convention lives once.
27
+ * Price- and radius-axis fibs use `baseline: "middle"`; time-axis fibs
28
+ * pass `baseline: "top"`.
29
+ */
30
+ function fibLabel(text, x, y, color, baseline) {
31
+ return { kind: "text", x, y, text, color, font: LABEL_FONT, align: "left", baseline };
32
+ }
33
+ /**
34
+ * Decompose a `fib-retracement` drawing — one horizontal level line per
35
+ * Fibonacci ratio between `anchors[0].price` and `anchors[1].price`,
36
+ * each honouring `style.extendLeft` / `style.extendRight` via
37
+ * {@link extendLineSegment}. `style.levels` overrides {@link FIB_LEVELS};
38
+ * `style.showLabels === true` appends a right-edge `formatLevel` label
39
+ * per rail.
40
+ *
41
+ * @since 1.3
42
+ * @stable
43
+ * @example
44
+ * declare const s: FibRetracementState;
45
+ * declare const v: Viewport;
46
+ * const prims = decomposeFibRetracement(s, v);
47
+ * void prims;
48
+ */
49
+ export function decomposeFibRetracement(state, view) {
50
+ const a = worldPointToPixel(state.anchors[0], view);
51
+ const b = worldPointToPixel(state.anchors[1], view);
52
+ const color = state.style.color ?? DEFAULT_COLOR;
53
+ const levels = state.style.levels ?? FIB_LEVELS;
54
+ const fromPrice = state.anchors[0].price;
55
+ const toPrice = state.anchors[1].price;
56
+ const out = [];
57
+ for (const level of levels) {
58
+ const levelPrice = fromPrice + level * (toPrice - fromPrice);
59
+ const levelY = priceToY(levelPrice, view);
60
+ const { from, to } = extendLineSegment({ x: a.x, y: levelY }, { x: b.x, y: levelY }, { extendLeft: state.style.extendLeft, extendRight: state.style.extendRight }, view);
61
+ out.push({
62
+ kind: "polyline",
63
+ points: [
64
+ { x: from.x, y: levelY },
65
+ { x: to.x, y: levelY },
66
+ ],
67
+ closed: false,
68
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
69
+ });
70
+ if (state.style.showLabels === true) {
71
+ out.push(fibLabel(formatLevel(level), to.x + LABEL_OFFSET_PX, levelY, color, "middle"));
72
+ }
73
+ }
74
+ return out;
75
+ }
76
+ /**
77
+ * Decompose a `fib-trend-extension` drawing — projects fib-ratio
78
+ * extensions from `anchors[2].price` using the A→B leg's price delta
79
+ * (`anchors[1].price − anchors[0].price`). Each projected price is one
80
+ * horizontal line from `timeToX(anchors[2].time)` rightward to the
81
+ * viewport edge. `style.levels` overrides {@link FIB_LEVELS};
82
+ * `showLabels` appends a label past the right edge.
83
+ *
84
+ * @since 1.3
85
+ * @stable
86
+ * @example
87
+ * declare const s: FibTrendExtensionState;
88
+ * declare const v: Viewport;
89
+ * const prims = decomposeFibTrendExtension(s, v);
90
+ * void prims;
91
+ */
92
+ export function decomposeFibTrendExtension(state, view) {
93
+ const [A, B, C] = state.anchors;
94
+ const color = state.style.color ?? DEFAULT_COLOR;
95
+ const levels = state.style.levels ?? FIB_LEVELS;
96
+ const priceDelta = B.price - A.price;
97
+ const startX = timeToX(C.time, view);
98
+ const endX = view.pxWidth;
99
+ const out = [];
100
+ for (const level of levels) {
101
+ const levelPrice = C.price + level * priceDelta;
102
+ const levelY = priceToY(levelPrice, view);
103
+ out.push({
104
+ kind: "polyline",
105
+ points: [
106
+ { x: startX, y: levelY },
107
+ { x: endX, y: levelY },
108
+ ],
109
+ closed: false,
110
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
111
+ });
112
+ if (state.style.showLabels === true) {
113
+ out.push(fibLabel(formatLevel(level), endX + LABEL_OFFSET_PX, levelY, color, "middle"));
114
+ }
115
+ }
116
+ return out;
117
+ }
118
+ /**
119
+ * Decompose a `fib-channel` drawing — one parallel rail per Fibonacci
120
+ * ratio: each rail translates the primary `(anchors[0], anchors[1])`
121
+ * line by `level · (anchors[2].y − anchors[0].y)` in pixel space.
122
+ * `style.levels` overrides {@link FIB_LEVELS}; `showLabels` appends a
123
+ * label at the rail's right endpoint.
124
+ *
125
+ * @since 1.3
126
+ * @stable
127
+ * @example
128
+ * declare const s: FibChannelState;
129
+ * declare const v: Viewport;
130
+ * const prims = decomposeFibChannel(s, v);
131
+ * void prims;
132
+ */
133
+ export function decomposeFibChannel(state, view) {
134
+ const a = worldPointToPixel(state.anchors[0], view);
135
+ const b = worldPointToPixel(state.anchors[1], view);
136
+ const c = worldPointToPixel(state.anchors[2], view);
137
+ const color = state.style.color ?? DEFAULT_COLOR;
138
+ const levels = state.style.levels ?? FIB_LEVELS;
139
+ const offsetUnit = c.y - a.y;
140
+ const out = [];
141
+ for (const level of levels) {
142
+ const offsetY = level * offsetUnit;
143
+ const fromY = a.y + offsetY;
144
+ const toY = b.y + offsetY;
145
+ out.push({
146
+ kind: "polyline",
147
+ points: [
148
+ { x: a.x, y: fromY },
149
+ { x: b.x, y: toY },
150
+ ],
151
+ closed: false,
152
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
153
+ });
154
+ if (state.style.showLabels === true) {
155
+ out.push(fibLabel(formatLevel(level), b.x + LABEL_OFFSET_PX, toY, color, "middle"));
156
+ }
157
+ }
158
+ return out;
159
+ }
160
+ /**
161
+ * Decompose a `fib-time-zone` drawing — vertical lines at fib-ratio
162
+ * spaced times `anchors[0].time + level · (anchors[1].time −
163
+ * anchors[0].time)`, each spanning the full viewport height.
164
+ * `style.levels` overrides {@link FIB_LEVELS}; `showLabels` appends a
165
+ * top-anchored label.
166
+ *
167
+ * @since 1.3
168
+ * @stable
169
+ * @example
170
+ * declare const s: FibTimeZoneState;
171
+ * declare const v: Viewport;
172
+ * const prims = decomposeFibTimeZone(s, v);
173
+ * void prims;
174
+ */
175
+ export function decomposeFibTimeZone(state, view) {
176
+ const [A, B] = state.anchors;
177
+ const color = state.style.color ?? DEFAULT_COLOR;
178
+ const levels = state.style.levels ?? FIB_LEVELS;
179
+ const timeDelta = B.time - A.time;
180
+ const out = [];
181
+ for (const level of levels) {
182
+ const tx = timeToX(A.time + level * timeDelta, view);
183
+ out.push({
184
+ kind: "polyline",
185
+ points: [
186
+ { x: tx, y: 0 },
187
+ { x: tx, y: view.pxHeight },
188
+ ],
189
+ closed: false,
190
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
191
+ });
192
+ if (state.style.showLabels === true) {
193
+ out.push(fibLabel(formatLevel(level), tx + LABEL_OFFSET_PX, LABEL_TOP_PX, color, "top"));
194
+ }
195
+ }
196
+ return out;
197
+ }
198
+ /**
199
+ * Decompose a `fib-trend-time` drawing — vertical lines at fib-spaced
200
+ * times anchored at `anchors[2]`: `t = C.time + level · (B.time −
201
+ * A.time)`. Mirrors `fib-time-zone` but uses the A→B leg as the
202
+ * time-delta unit projected from C. `showLabels` appends a top label.
203
+ *
204
+ * @since 1.3
205
+ * @stable
206
+ * @example
207
+ * declare const s: FibTrendTimeState;
208
+ * declare const v: Viewport;
209
+ * const prims = decomposeFibTrendTime(s, v);
210
+ * void prims;
211
+ */
212
+ export function decomposeFibTrendTime(state, view) {
213
+ const [A, B, C] = state.anchors;
214
+ const color = state.style.color ?? DEFAULT_COLOR;
215
+ const levels = state.style.levels ?? FIB_LEVELS;
216
+ const timeDelta = B.time - A.time;
217
+ const out = [];
218
+ for (const level of levels) {
219
+ const tx = timeToX(C.time + level * timeDelta, view);
220
+ out.push({
221
+ kind: "polyline",
222
+ points: [
223
+ { x: tx, y: 0 },
224
+ { x: tx, y: view.pxHeight },
225
+ ],
226
+ closed: false,
227
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
228
+ });
229
+ if (state.style.showLabels === true) {
230
+ out.push(fibLabel(formatLevel(level), tx + LABEL_OFFSET_PX, LABEL_TOP_PX, color, "top"));
231
+ }
232
+ }
233
+ return out;
234
+ }
235
+ /**
236
+ * Decompose a `fib-wedge` drawing — a fan of rays from `anchors[0]` (the
237
+ * pivot) at fib-ratio-interpolated angles between the pivot→`anchors[1]`
238
+ * and pivot→`anchors[2]` direction vectors. Ray length is
239
+ * `max(pxWidth, pxHeight) · 2` so strokes always exit the viewport; a
240
+ * degenerate (zero-magnitude) direction skips that level. `showLabels`
241
+ * appends a label a quarter of the way along each ray.
242
+ *
243
+ * @since 1.3
244
+ * @stable
245
+ * @example
246
+ * declare const s: FibWedgeState;
247
+ * declare const v: Viewport;
248
+ * const prims = decomposeFibWedge(s, v);
249
+ * void prims;
250
+ */
251
+ export function decomposeFibWedge(state, view) {
252
+ const pivot = worldPointToPixel(state.anchors[0], view);
253
+ const r1 = worldPointToPixel(state.anchors[1], view);
254
+ const r2 = worldPointToPixel(state.anchors[2], view);
255
+ const color = state.style.color ?? DEFAULT_COLOR;
256
+ const levels = state.style.levels ?? FIB_LEVELS;
257
+ const d1x = r1.x - pivot.x;
258
+ const d1y = r1.y - pivot.y;
259
+ const d2x = r2.x - pivot.x;
260
+ const d2y = r2.y - pivot.y;
261
+ const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;
262
+ const out = [];
263
+ for (const level of levels) {
264
+ const dx = d1x + level * (d2x - d1x);
265
+ const dy = d1y + level * (d2y - d1y);
266
+ const mag = Math.hypot(dx, dy);
267
+ if (mag === 0)
268
+ continue;
269
+ const ux = dx / mag;
270
+ const uy = dy / mag;
271
+ out.push({
272
+ kind: "polyline",
273
+ points: [pivot, { x: pivot.x + ux * rayLength, y: pivot.y + uy * rayLength }],
274
+ closed: false,
275
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
276
+ });
277
+ if (state.style.showLabels === true) {
278
+ out.push(fibLabel(formatLevel(level), pivot.x + ux * rayLength * LABEL_OFFSET_FRACTION, pivot.y + uy * rayLength * LABEL_OFFSET_FRACTION, color, "middle"));
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+ /**
284
+ * Decompose a `fib-speed-fan` drawing — a fan of rays from `anchors[0]`.
285
+ * Each ray scales the A→B y-delta by a fib ratio while keeping the
286
+ * x-delta constant. Ray length is `max(pxWidth, pxHeight) · 2`; a
287
+ * degenerate direction skips that level. `showLabels` appends a label a
288
+ * quarter of the way along each ray.
289
+ *
290
+ * @since 1.3
291
+ * @stable
292
+ * @example
293
+ * declare const s: FibSpeedFanState;
294
+ * declare const v: Viewport;
295
+ * const prims = decomposeFibSpeedFan(s, v);
296
+ * void prims;
297
+ */
298
+ export function decomposeFibSpeedFan(state, view) {
299
+ const from = worldPointToPixel(state.anchors[0], view);
300
+ const to = worldPointToPixel(state.anchors[1], view);
301
+ const color = state.style.color ?? DEFAULT_COLOR;
302
+ const levels = state.style.levels ?? FIB_LEVELS;
303
+ const dx = to.x - from.x;
304
+ const dy = to.y - from.y;
305
+ const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;
306
+ const out = [];
307
+ for (const level of levels) {
308
+ const rdy = level * dy;
309
+ const mag = Math.hypot(dx, rdy);
310
+ if (mag === 0)
311
+ continue;
312
+ const ux = dx / mag;
313
+ const uy = rdy / mag;
314
+ out.push({
315
+ kind: "polyline",
316
+ points: [from, { x: from.x + ux * rayLength, y: from.y + uy * rayLength }],
317
+ closed: false,
318
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
319
+ });
320
+ if (state.style.showLabels === true) {
321
+ out.push(fibLabel(formatLevel(level), from.x + ux * rayLength * LABEL_OFFSET_FRACTION, from.y + uy * rayLength * LABEL_OFFSET_FRACTION, color, "middle"));
322
+ }
323
+ }
324
+ return out;
325
+ }
326
+ /**
327
+ * Decompose a `fib-speed-arcs` drawing — concentric full circles centred
328
+ * at `anchors[0]` with radii `level · R₀`, `R₀ = |anchors[1] −
329
+ * anchors[0]|` in pixel space. Always-full-circle (half-disk variant
330
+ * deferred). `showLabels` appends a label to the right of each arc.
331
+ *
332
+ * @since 1.3
333
+ * @stable
334
+ * @example
335
+ * declare const s: FibSpeedArcsState;
336
+ * declare const v: Viewport;
337
+ * const prims = decomposeFibSpeedArcs(s, v);
338
+ * void prims;
339
+ */
340
+ export function decomposeFibSpeedArcs(state, view) {
341
+ const centre = worldPointToPixel(state.anchors[0], view);
342
+ const edge = worldPointToPixel(state.anchors[1], view);
343
+ const color = state.style.color ?? DEFAULT_COLOR;
344
+ const levels = state.style.levels ?? FIB_LEVELS;
345
+ const r0 = Math.hypot(edge.x - centre.x, edge.y - centre.y);
346
+ return concentricArcs(centre, r0, levels, color, state.style.showLabels === true);
347
+ }
348
+ /**
349
+ * Decompose a `fib-circles` drawing — concentric full circles centred at
350
+ * `anchors[0]` with radii `level · R₀`, `R₀ = |anchors[1] − anchors[0]|`
351
+ * (the radius-point distance) in pixel space. Uses fib ratios (not the
352
+ * integer Fibonacci sequence). `showLabels` appends a label to the right
353
+ * of each circle.
354
+ *
355
+ * @since 1.3
356
+ * @stable
357
+ * @example
358
+ * declare const s: FibCirclesState;
359
+ * declare const v: Viewport;
360
+ * const prims = decomposeFibCircles(s, v);
361
+ * void prims;
362
+ */
363
+ export function decomposeFibCircles(state, view) {
364
+ const centre = worldPointToPixel(state.anchors[0], view);
365
+ const radiusPoint = worldPointToPixel(state.anchors[1], view);
366
+ const color = state.style.color ?? DEFAULT_COLOR;
367
+ const levels = state.style.levels ?? FIB_LEVELS;
368
+ const r0 = Math.hypot(radiusPoint.x - centre.x, radiusPoint.y - centre.y);
369
+ return concentricArcs(centre, r0, levels, color, state.style.showLabels === true);
370
+ }
371
+ /**
372
+ * Shared concentric-arc builder for `fib-speed-arcs` / `fib-circles`:
373
+ * one full `arc` primitive per level at radius `level · r0`, plus an
374
+ * optional right-edge label. Matches the canvas2d source, which emits a
375
+ * (possibly zero-radius) arc per level without an early return.
376
+ */
377
+ function concentricArcs(centre, r0, levels, color, showLabels) {
378
+ const out = [];
379
+ for (const level of levels) {
380
+ const radius = level * r0;
381
+ out.push({
382
+ kind: "arc",
383
+ cx: centre.x,
384
+ cy: centre.y,
385
+ r: radius,
386
+ start: 0,
387
+ end: TAU,
388
+ closed: false,
389
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
390
+ });
391
+ if (showLabels) {
392
+ out.push(fibLabel(formatLevel(level), centre.x + radius + LABEL_OFFSET_PX, centre.y, color, "middle"));
393
+ }
394
+ }
395
+ return out;
396
+ }
397
+ // Number of quarter-turns sampled = 8 → 2 full rotations.
398
+ const SPIRAL_QUARTERS = 8;
399
+ // Samples per quarter for the cubic Bezier approximation.
400
+ const SPIRAL_SAMPLES_PER_QUARTER = 16;
401
+ // φ ≈ 1.618. Each quarter scales the spiral radius by 1/φ inward.
402
+ const PHI = (1 + Math.sqrt(5)) / 2;
403
+ // Classical Bezier-arc factor for a 90° quadrant: k = 4(√2 − 1)/3.
404
+ const SPIRAL_K = (4 * (Math.sqrt(2) - 1)) / 3;
405
+ /**
406
+ * Decompose a `fib-spiral` drawing — a chained cubic-Bezier
407
+ * approximation of a golden spiral as one open polyline. Each
408
+ * quarter-turn is one cubic Bezier with the classical `k ≈ 0.5523` arc
409
+ * factor; the radius shrinks by `1/φ` per quarter. Centre =
410
+ * `anchors[0]`; initial radius `|anchors[1] − anchors[0]|` in pixel
411
+ * space. A zero initial radius returns `[]` (matching the source's
412
+ * `if (r === 0) return`).
413
+ *
414
+ * @since 1.3
415
+ * @stable
416
+ * @example
417
+ * declare const s: FibSpiralState;
418
+ * declare const v: Viewport;
419
+ * const prims = decomposeFibSpiral(s, v);
420
+ * void prims;
421
+ */
422
+ export function decomposeFibSpiral(state, view) {
423
+ const centre = worldPointToPixel(state.anchors[0], view);
424
+ const edge = worldPointToPixel(state.anchors[1], view);
425
+ const color = state.style.color ?? DEFAULT_COLOR;
426
+ let r = Math.hypot(edge.x - centre.x, edge.y - centre.y);
427
+ if (r === 0)
428
+ return [];
429
+ const points = [{ x: centre.x + r, y: centre.y }];
430
+ for (let q = 0; q < SPIRAL_QUARTERS; q++) {
431
+ const baseAngle = q * (Math.PI / 2);
432
+ const r1 = r;
433
+ const r2 = r / PHI;
434
+ const cos0 = Math.cos(baseAngle);
435
+ const sin0 = Math.sin(baseAngle);
436
+ const cos1 = Math.cos(baseAngle + Math.PI / 2);
437
+ const sin1 = Math.sin(baseAngle + Math.PI / 2);
438
+ const p0 = { x: centre.x + r1 * cos0, y: centre.y + r1 * sin0 };
439
+ const p3 = { x: centre.x + r2 * cos1, y: centre.y + r2 * sin1 };
440
+ const p1 = { x: p0.x + SPIRAL_K * r1 * cos1, y: p0.y + SPIRAL_K * r1 * sin1 };
441
+ const p2 = { x: p3.x + SPIRAL_K * r2 * cos0, y: p3.y + SPIRAL_K * r2 * sin0 };
442
+ const samples = sampleCubic(p0, p1, p2, p3, SPIRAL_SAMPLES_PER_QUARTER);
443
+ // Skip the first sample (the previous quarter's endpoint / the
444
+ // initial start point) to avoid a duplicate vertex.
445
+ for (let i = 1; i < samples.length; i++)
446
+ points.push(samples[i]);
447
+ r = r2;
448
+ }
449
+ return [
450
+ {
451
+ kind: "polyline",
452
+ points,
453
+ closed: false,
454
+ stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },
455
+ },
456
+ ];
457
+ }
458
+ //# sourceMappingURL=fibonacci.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fibonacci.js","sourceRoot":"","sources":["../../../src/geometry/kinds/fibonacci.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Fibonacci geometry moved from the canvas2d adapter's per-kind\n// renderers\n// examples/canvas2d-adapter/src/render/draw/{fibRetracement,\n// fibTrendExtension,fibChannel,fibTimeZone,fibWedge,fibSpeedFan,\n// fibSpeedArcs,fibSpiral,fibCircles,fibTrendTime}.ts.\n// The originating math is invinite's fib-* tools (commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed\n// MIT for chartlang.\n\nimport type {\n FibChannelState,\n FibCirclesState,\n FibRetracementState,\n FibSpeedArcsState,\n FibSpeedFanState,\n FibSpiralState,\n FibTimeZoneState,\n FibTrendExtensionState,\n FibTrendTimeState,\n FibWedgeState,\n} from \"@invinite-org/chartlang-core\";\n\nimport { sampleCubic } from \"../_lib/bezier.js\";\nimport { SOLID_DASH } from \"../_lib/dash.js\";\nimport { FIB_LEVELS, formatLevel } from \"../_lib/fibLevels.js\";\nimport { extendLineSegment } from \"../_lib/lineExtend.js\";\nimport { priceToY, timeToX, worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Point2, Viewport } from \"../types.js\";\n\nconst DEFAULT_COLOR = \"#facc15\";\nconst DEFAULT_LINE_WIDTH = 1;\nconst LABEL_FONT = \"12px sans-serif\";\nconst LABEL_OFFSET_PX = 4;\nconst LABEL_TOP_PX = 12;\nconst LABEL_OFFSET_FRACTION = 0.25;\nconst TAU = Math.PI * 2;\n\n/**\n * Build a level-line label `text` primitive. Shared by every fib\n * decomposer so the font / colour / alignment convention lives once.\n * Price- and radius-axis fibs use `baseline: \"middle\"`; time-axis fibs\n * pass `baseline: \"top\"`.\n */\nfunction fibLabel(\n text: string,\n x: number,\n y: number,\n color: string,\n baseline: \"middle\" | \"top\",\n): DrawPrimitive {\n return { kind: \"text\", x, y, text, color, font: LABEL_FONT, align: \"left\", baseline };\n}\n\n/**\n * Decompose a `fib-retracement` drawing — one horizontal level line per\n * Fibonacci ratio between `anchors[0].price` and `anchors[1].price`,\n * each honouring `style.extendLeft` / `style.extendRight` via\n * {@link extendLineSegment}. `style.levels` overrides {@link FIB_LEVELS};\n * `style.showLabels === true` appends a right-edge `formatLevel` label\n * per rail.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibRetracementState;\n * declare const v: Viewport;\n * const prims = decomposeFibRetracement(s, v);\n * void prims;\n */\nexport function decomposeFibRetracement(\n state: FibRetracementState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const fromPrice = state.anchors[0].price;\n const toPrice = state.anchors[1].price;\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const levelPrice = fromPrice + level * (toPrice - fromPrice);\n const levelY = priceToY(levelPrice, view);\n const { from, to } = extendLineSegment(\n { x: a.x, y: levelY },\n { x: b.x, y: levelY },\n { extendLeft: state.style.extendLeft, extendRight: state.style.extendRight },\n view,\n );\n out.push({\n kind: \"polyline\",\n points: [\n { x: from.x, y: levelY },\n { x: to.x, y: levelY },\n ],\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (state.style.showLabels === true) {\n out.push(fibLabel(formatLevel(level), to.x + LABEL_OFFSET_PX, levelY, color, \"middle\"));\n }\n }\n return out;\n}\n\n/**\n * Decompose a `fib-trend-extension` drawing — projects fib-ratio\n * extensions from `anchors[2].price` using the A→B leg's price delta\n * (`anchors[1].price − anchors[0].price`). Each projected price is one\n * horizontal line from `timeToX(anchors[2].time)` rightward to the\n * viewport edge. `style.levels` overrides {@link FIB_LEVELS};\n * `showLabels` appends a label past the right edge.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibTrendExtensionState;\n * declare const v: Viewport;\n * const prims = decomposeFibTrendExtension(s, v);\n * void prims;\n */\nexport function decomposeFibTrendExtension(\n state: FibTrendExtensionState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const [A, B, C] = state.anchors;\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const priceDelta = B.price - A.price;\n const startX = timeToX(C.time, view);\n const endX = view.pxWidth;\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const levelPrice = C.price + level * priceDelta;\n const levelY = priceToY(levelPrice, view);\n out.push({\n kind: \"polyline\",\n points: [\n { x: startX, y: levelY },\n { x: endX, y: levelY },\n ],\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (state.style.showLabels === true) {\n out.push(fibLabel(formatLevel(level), endX + LABEL_OFFSET_PX, levelY, color, \"middle\"));\n }\n }\n return out;\n}\n\n/**\n * Decompose a `fib-channel` drawing — one parallel rail per Fibonacci\n * ratio: each rail translates the primary `(anchors[0], anchors[1])`\n * line by `level · (anchors[2].y − anchors[0].y)` in pixel space.\n * `style.levels` overrides {@link FIB_LEVELS}; `showLabels` appends a\n * label at the rail's right endpoint.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibChannelState;\n * declare const v: Viewport;\n * const prims = decomposeFibChannel(s, v);\n * void prims;\n */\nexport function decomposeFibChannel(\n state: FibChannelState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const c = worldPointToPixel(state.anchors[2], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const offsetUnit = c.y - a.y;\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const offsetY = level * offsetUnit;\n const fromY = a.y + offsetY;\n const toY = b.y + offsetY;\n out.push({\n kind: \"polyline\",\n points: [\n { x: a.x, y: fromY },\n { x: b.x, y: toY },\n ],\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (state.style.showLabels === true) {\n out.push(fibLabel(formatLevel(level), b.x + LABEL_OFFSET_PX, toY, color, \"middle\"));\n }\n }\n return out;\n}\n\n/**\n * Decompose a `fib-time-zone` drawing — vertical lines at fib-ratio\n * spaced times `anchors[0].time + level · (anchors[1].time −\n * anchors[0].time)`, each spanning the full viewport height.\n * `style.levels` overrides {@link FIB_LEVELS}; `showLabels` appends a\n * top-anchored label.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibTimeZoneState;\n * declare const v: Viewport;\n * const prims = decomposeFibTimeZone(s, v);\n * void prims;\n */\nexport function decomposeFibTimeZone(\n state: FibTimeZoneState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const [A, B] = state.anchors;\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const timeDelta = B.time - A.time;\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const tx = timeToX(A.time + level * timeDelta, view);\n out.push({\n kind: \"polyline\",\n points: [\n { x: tx, y: 0 },\n { x: tx, y: view.pxHeight },\n ],\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (state.style.showLabels === true) {\n out.push(\n fibLabel(formatLevel(level), tx + LABEL_OFFSET_PX, LABEL_TOP_PX, color, \"top\"),\n );\n }\n }\n return out;\n}\n\n/**\n * Decompose a `fib-trend-time` drawing — vertical lines at fib-spaced\n * times anchored at `anchors[2]`: `t = C.time + level · (B.time −\n * A.time)`. Mirrors `fib-time-zone` but uses the A→B leg as the\n * time-delta unit projected from C. `showLabels` appends a top label.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibTrendTimeState;\n * declare const v: Viewport;\n * const prims = decomposeFibTrendTime(s, v);\n * void prims;\n */\nexport function decomposeFibTrendTime(\n state: FibTrendTimeState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const [A, B, C] = state.anchors;\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const timeDelta = B.time - A.time;\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const tx = timeToX(C.time + level * timeDelta, view);\n out.push({\n kind: \"polyline\",\n points: [\n { x: tx, y: 0 },\n { x: tx, y: view.pxHeight },\n ],\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (state.style.showLabels === true) {\n out.push(\n fibLabel(formatLevel(level), tx + LABEL_OFFSET_PX, LABEL_TOP_PX, color, \"top\"),\n );\n }\n }\n return out;\n}\n\n/**\n * Decompose a `fib-wedge` drawing — a fan of rays from `anchors[0]` (the\n * pivot) at fib-ratio-interpolated angles between the pivot→`anchors[1]`\n * and pivot→`anchors[2]` direction vectors. Ray length is\n * `max(pxWidth, pxHeight) · 2` so strokes always exit the viewport; a\n * degenerate (zero-magnitude) direction skips that level. `showLabels`\n * appends a label a quarter of the way along each ray.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibWedgeState;\n * declare const v: Viewport;\n * const prims = decomposeFibWedge(s, v);\n * void prims;\n */\nexport function decomposeFibWedge(\n state: FibWedgeState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const pivot = worldPointToPixel(state.anchors[0], view);\n const r1 = worldPointToPixel(state.anchors[1], view);\n const r2 = worldPointToPixel(state.anchors[2], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const d1x = r1.x - pivot.x;\n const d1y = r1.y - pivot.y;\n const d2x = r2.x - pivot.x;\n const d2y = r2.y - pivot.y;\n const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const dx = d1x + level * (d2x - d1x);\n const dy = d1y + level * (d2y - d1y);\n const mag = Math.hypot(dx, dy);\n if (mag === 0) continue;\n const ux = dx / mag;\n const uy = dy / mag;\n out.push({\n kind: \"polyline\",\n points: [pivot, { x: pivot.x + ux * rayLength, y: pivot.y + uy * rayLength }],\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (state.style.showLabels === true) {\n out.push(\n fibLabel(\n formatLevel(level),\n pivot.x + ux * rayLength * LABEL_OFFSET_FRACTION,\n pivot.y + uy * rayLength * LABEL_OFFSET_FRACTION,\n color,\n \"middle\",\n ),\n );\n }\n }\n return out;\n}\n\n/**\n * Decompose a `fib-speed-fan` drawing — a fan of rays from `anchors[0]`.\n * Each ray scales the A→B y-delta by a fib ratio while keeping the\n * x-delta constant. Ray length is `max(pxWidth, pxHeight) · 2`; a\n * degenerate direction skips that level. `showLabels` appends a label a\n * quarter of the way along each ray.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibSpeedFanState;\n * declare const v: Viewport;\n * const prims = decomposeFibSpeedFan(s, v);\n * void prims;\n */\nexport function decomposeFibSpeedFan(\n state: FibSpeedFanState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const from = worldPointToPixel(state.anchors[0], view);\n const to = worldPointToPixel(state.anchors[1], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const dx = to.x - from.x;\n const dy = to.y - from.y;\n const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const rdy = level * dy;\n const mag = Math.hypot(dx, rdy);\n if (mag === 0) continue;\n const ux = dx / mag;\n const uy = rdy / mag;\n out.push({\n kind: \"polyline\",\n points: [from, { x: from.x + ux * rayLength, y: from.y + uy * rayLength }],\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (state.style.showLabels === true) {\n out.push(\n fibLabel(\n formatLevel(level),\n from.x + ux * rayLength * LABEL_OFFSET_FRACTION,\n from.y + uy * rayLength * LABEL_OFFSET_FRACTION,\n color,\n \"middle\",\n ),\n );\n }\n }\n return out;\n}\n\n/**\n * Decompose a `fib-speed-arcs` drawing — concentric full circles centred\n * at `anchors[0]` with radii `level · R₀`, `R₀ = |anchors[1] −\n * anchors[0]|` in pixel space. Always-full-circle (half-disk variant\n * deferred). `showLabels` appends a label to the right of each arc.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibSpeedArcsState;\n * declare const v: Viewport;\n * const prims = decomposeFibSpeedArcs(s, v);\n * void prims;\n */\nexport function decomposeFibSpeedArcs(\n state: FibSpeedArcsState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const centre = worldPointToPixel(state.anchors[0], view);\n const edge = worldPointToPixel(state.anchors[1], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const r0 = Math.hypot(edge.x - centre.x, edge.y - centre.y);\n return concentricArcs(centre, r0, levels, color, state.style.showLabels === true);\n}\n\n/**\n * Decompose a `fib-circles` drawing — concentric full circles centred at\n * `anchors[0]` with radii `level · R₀`, `R₀ = |anchors[1] − anchors[0]|`\n * (the radius-point distance) in pixel space. Uses fib ratios (not the\n * integer Fibonacci sequence). `showLabels` appends a label to the right\n * of each circle.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibCirclesState;\n * declare const v: Viewport;\n * const prims = decomposeFibCircles(s, v);\n * void prims;\n */\nexport function decomposeFibCircles(\n state: FibCirclesState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const centre = worldPointToPixel(state.anchors[0], view);\n const radiusPoint = worldPointToPixel(state.anchors[1], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n const levels = state.style.levels ?? FIB_LEVELS;\n const r0 = Math.hypot(radiusPoint.x - centre.x, radiusPoint.y - centre.y);\n return concentricArcs(centre, r0, levels, color, state.style.showLabels === true);\n}\n\n/**\n * Shared concentric-arc builder for `fib-speed-arcs` / `fib-circles`:\n * one full `arc` primitive per level at radius `level · r0`, plus an\n * optional right-edge label. Matches the canvas2d source, which emits a\n * (possibly zero-radius) arc per level without an early return.\n */\nfunction concentricArcs(\n centre: Point2,\n r0: number,\n levels: ReadonlyArray<number>,\n color: string,\n showLabels: boolean,\n): ReadonlyArray<DrawPrimitive> {\n const out: DrawPrimitive[] = [];\n for (const level of levels) {\n const radius = level * r0;\n out.push({\n kind: \"arc\",\n cx: centre.x,\n cy: centre.y,\n r: radius,\n start: 0,\n end: TAU,\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n });\n if (showLabels) {\n out.push(\n fibLabel(\n formatLevel(level),\n centre.x + radius + LABEL_OFFSET_PX,\n centre.y,\n color,\n \"middle\",\n ),\n );\n }\n }\n return out;\n}\n\n// Number of quarter-turns sampled = 8 → 2 full rotations.\nconst SPIRAL_QUARTERS = 8;\n// Samples per quarter for the cubic Bezier approximation.\nconst SPIRAL_SAMPLES_PER_QUARTER = 16;\n// φ ≈ 1.618. Each quarter scales the spiral radius by 1/φ inward.\nconst PHI = (1 + Math.sqrt(5)) / 2;\n// Classical Bezier-arc factor for a 90° quadrant: k = 4(√2 − 1)/3.\nconst SPIRAL_K = (4 * (Math.sqrt(2) - 1)) / 3;\n\n/**\n * Decompose a `fib-spiral` drawing — a chained cubic-Bezier\n * approximation of a golden spiral as one open polyline. Each\n * quarter-turn is one cubic Bezier with the classical `k ≈ 0.5523` arc\n * factor; the radius shrinks by `1/φ` per quarter. Centre =\n * `anchors[0]`; initial radius `|anchors[1] − anchors[0]|` in pixel\n * space. A zero initial radius returns `[]` (matching the source's\n * `if (r === 0) return`).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FibSpiralState;\n * declare const v: Viewport;\n * const prims = decomposeFibSpiral(s, v);\n * void prims;\n */\nexport function decomposeFibSpiral(\n state: FibSpiralState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const centre = worldPointToPixel(state.anchors[0], view);\n const edge = worldPointToPixel(state.anchors[1], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n let r = Math.hypot(edge.x - centre.x, edge.y - centre.y);\n if (r === 0) return [];\n const points: Point2[] = [{ x: centre.x + r, y: centre.y }];\n for (let q = 0; q < SPIRAL_QUARTERS; q++) {\n const baseAngle = q * (Math.PI / 2);\n const r1 = r;\n const r2 = r / PHI;\n const cos0 = Math.cos(baseAngle);\n const sin0 = Math.sin(baseAngle);\n const cos1 = Math.cos(baseAngle + Math.PI / 2);\n const sin1 = Math.sin(baseAngle + Math.PI / 2);\n const p0: Point2 = { x: centre.x + r1 * cos0, y: centre.y + r1 * sin0 };\n const p3: Point2 = { x: centre.x + r2 * cos1, y: centre.y + r2 * sin1 };\n const p1: Point2 = { x: p0.x + SPIRAL_K * r1 * cos1, y: p0.y + SPIRAL_K * r1 * sin1 };\n const p2: Point2 = { x: p3.x + SPIRAL_K * r2 * cos0, y: p3.y + SPIRAL_K * r2 * sin0 };\n const samples = sampleCubic(p0, p1, p2, p3, SPIRAL_SAMPLES_PER_QUARTER);\n // Skip the first sample (the previous quarter's endpoint / the\n // initial start point) to avoid a duplicate vertex.\n for (let i = 1; i < samples.length; i++) points.push(samples[i]);\n r = r2;\n }\n return [\n {\n kind: \"polyline\",\n points,\n closed: false,\n stroke: { color, width: DEFAULT_LINE_WIDTH, dash: SOLID_DASH },\n },\n ];\n}\n"]}
@@ -0,0 +1,53 @@
1
+ import type { BrushState, HighlighterState, PenState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose a `pen` drawing — a freehand stroke as one open polyline
5
+ * through the projected anchors, stroke-only with a `LineDrawStyle`.
6
+ *
7
+ * @since 1.3
8
+ * @stable
9
+ * @example
10
+ * declare const s: PenState;
11
+ * declare const v: Viewport;
12
+ * const prims = decomposePen(s, v);
13
+ * // prims[0].kind === "polyline"; prims[0].closed === false
14
+ * void prims;
15
+ */
16
+ export declare function decomposePen(state: PenState, view: Viewport): ReadonlyArray<DrawPrimitive>;
17
+ /**
18
+ * Decompose a `highlighter` drawing — a thick translucent freehand
19
+ * stroke as one open polyline. The translucency rides on the IR
20
+ * `StrokeStyle.alpha` (set to `style.alpha`); the painter brackets the
21
+ * `stroke()` in `globalAlpha`, scoping it to this drawing only — exactly
22
+ * the canvas2d source's `globalAlpha` bracket. Width is the fixed
23
+ * {@link HIGHLIGHTER_LINE_WIDTH}; both `color` and `alpha` are required
24
+ * by `HighlighterStyle`.
25
+ *
26
+ * @since 1.3
27
+ * @stable
28
+ * @example
29
+ * declare const s: HighlighterState;
30
+ * declare const v: Viewport;
31
+ * const prims = decomposeHighlighter(s, v);
32
+ * // prims[0].kind === "polyline"; prims[0].stroke?.alpha is set
33
+ * void prims;
34
+ */
35
+ export declare function decomposeHighlighter(state: HighlighterState, view: Viewport): ReadonlyArray<DrawPrimitive>;
36
+ /**
37
+ * Decompose a `brush` drawing — a freehand region as one closed polyline
38
+ * carrying both a `fill` (`style.fill` at full opacity) and a `stroke`
39
+ * (`style.stroke`, width 1). The painter fills before stroking, so the
40
+ * outline draws on top of the filled region. Both colours are required
41
+ * by `BrushStyle`.
42
+ *
43
+ * @since 1.3
44
+ * @stable
45
+ * @example
46
+ * declare const s: BrushState;
47
+ * declare const v: Viewport;
48
+ * const prims = decomposeBrush(s, v);
49
+ * // prims[0].kind === "polyline"; prims[0].closed === true
50
+ * void prims;
51
+ */
52
+ export declare function decomposeBrush(state: BrushState, view: Viewport): ReadonlyArray<DrawPrimitive>;
53
+ //# sourceMappingURL=freehand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"freehand.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/freehand.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAI3F,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAkB3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC,aAAa,CAAC,CAa1F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAChC,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAc9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC,aAAa,CAAC,CAc9F"}