@invinite-org/chartlang-adapter-kit 1.2.1 → 1.4.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 (153) hide show
  1. package/CHANGELOG.md +157 -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/capabilities/capabilities.d.ts +9 -7
  20. package/dist/capabilities/capabilities.d.ts.map +1 -1
  21. package/dist/capabilities/capabilities.js +14 -8
  22. package/dist/capabilities/capabilities.js.map +1 -1
  23. package/dist/geometry/_lib/arrowhead.d.ts +18 -0
  24. package/dist/geometry/_lib/arrowhead.d.ts.map +1 -0
  25. package/dist/geometry/_lib/arrowhead.js +38 -0
  26. package/dist/geometry/_lib/arrowhead.js.map +1 -0
  27. package/dist/geometry/_lib/bezier.d.ts +57 -0
  28. package/dist/geometry/_lib/bezier.d.ts.map +1 -0
  29. package/dist/geometry/_lib/bezier.js +84 -0
  30. package/dist/geometry/_lib/bezier.js.map +1 -0
  31. package/dist/geometry/_lib/chevron.d.ts +29 -0
  32. package/dist/geometry/_lib/chevron.d.ts.map +1 -0
  33. package/dist/geometry/_lib/chevron.js +37 -0
  34. package/dist/geometry/_lib/chevron.js.map +1 -0
  35. package/dist/geometry/_lib/dash.d.ts +30 -0
  36. package/dist/geometry/_lib/dash.d.ts.map +1 -0
  37. package/dist/geometry/_lib/dash.js +40 -0
  38. package/dist/geometry/_lib/dash.js.map +1 -0
  39. package/dist/geometry/_lib/fibLevels.d.ts +36 -0
  40. package/dist/geometry/_lib/fibLevels.d.ts.map +1 -0
  41. package/dist/geometry/_lib/fibLevels.js +44 -0
  42. package/dist/geometry/_lib/fibLevels.js.map +1 -0
  43. package/dist/geometry/_lib/gannLevels.d.ts +54 -0
  44. package/dist/geometry/_lib/gannLevels.d.ts.map +1 -0
  45. package/dist/geometry/_lib/gannLevels.js +88 -0
  46. package/dist/geometry/_lib/gannLevels.js.map +1 -0
  47. package/dist/geometry/_lib/lineExtend.d.ts +31 -0
  48. package/dist/geometry/_lib/lineExtend.d.ts.map +1 -0
  49. package/dist/geometry/_lib/lineExtend.js +48 -0
  50. package/dist/geometry/_lib/lineExtend.js.map +1 -0
  51. package/dist/geometry/_lib/namedPolyline.d.ts +25 -0
  52. package/dist/geometry/_lib/namedPolyline.d.ts.map +1 -0
  53. package/dist/geometry/_lib/namedPolyline.js +64 -0
  54. package/dist/geometry/_lib/namedPolyline.js.map +1 -0
  55. package/dist/geometry/_lib/pitchforkGeom.d.ts +46 -0
  56. package/dist/geometry/_lib/pitchforkGeom.d.ts.map +1 -0
  57. package/dist/geometry/_lib/pitchforkGeom.js +70 -0
  58. package/dist/geometry/_lib/pitchforkGeom.js.map +1 -0
  59. package/dist/geometry/_lib/shapeStyle.d.ts +21 -0
  60. package/dist/geometry/_lib/shapeStyle.d.ts.map +1 -0
  61. package/dist/geometry/_lib/shapeStyle.js +41 -0
  62. package/dist/geometry/_lib/shapeStyle.js.map +1 -0
  63. package/dist/geometry/_lib/strokeStyle.d.ts +34 -0
  64. package/dist/geometry/_lib/strokeStyle.d.ts.map +1 -0
  65. package/dist/geometry/_lib/strokeStyle.js +26 -0
  66. package/dist/geometry/_lib/strokeStyle.js.map +1 -0
  67. package/dist/geometry/_lib/textStyle.d.ts +70 -0
  68. package/dist/geometry/_lib/textStyle.d.ts.map +1 -0
  69. package/dist/geometry/_lib/textStyle.js +78 -0
  70. package/dist/geometry/_lib/textStyle.js.map +1 -0
  71. package/dist/geometry/decompose.d.ts +28 -0
  72. package/dist/geometry/decompose.d.ts.map +1 -0
  73. package/dist/geometry/decompose.js +176 -0
  74. package/dist/geometry/decompose.js.map +1 -0
  75. package/dist/geometry/index.d.ts +4 -0
  76. package/dist/geometry/index.d.ts.map +1 -0
  77. package/dist/geometry/index.js +5 -0
  78. package/dist/geometry/index.js.map +1 -0
  79. package/dist/geometry/kinds/annotations.d.ts +77 -0
  80. package/dist/geometry/kinds/annotations.d.ts.map +1 -0
  81. package/dist/geometry/kinds/annotations.js +219 -0
  82. package/dist/geometry/kinds/annotations.js.map +1 -0
  83. package/dist/geometry/kinds/boxes.d.ts +116 -0
  84. package/dist/geometry/kinds/boxes.d.ts.map +1 -0
  85. package/dist/geometry/kinds/boxes.js +285 -0
  86. package/dist/geometry/kinds/boxes.js.map +1 -0
  87. package/dist/geometry/kinds/channels.d.ts +72 -0
  88. package/dist/geometry/kinds/channels.d.ts.map +1 -0
  89. package/dist/geometry/kinds/channels.js +148 -0
  90. package/dist/geometry/kinds/channels.js.map +1 -0
  91. package/dist/geometry/kinds/containers.d.ts +54 -0
  92. package/dist/geometry/kinds/containers.d.ts.map +1 -0
  93. package/dist/geometry/kinds/containers.js +268 -0
  94. package/dist/geometry/kinds/containers.js.map +1 -0
  95. package/dist/geometry/kinds/curves.d.ts +53 -0
  96. package/dist/geometry/kinds/curves.d.ts.map +1 -0
  97. package/dist/geometry/kinds/curves.js +110 -0
  98. package/dist/geometry/kinds/curves.js.map +1 -0
  99. package/dist/geometry/kinds/cycles.d.ts +52 -0
  100. package/dist/geometry/kinds/cycles.d.ts.map +1 -0
  101. package/dist/geometry/kinds/cycles.js +158 -0
  102. package/dist/geometry/kinds/cycles.js.map +1 -0
  103. package/dist/geometry/kinds/elliott.d.ts +73 -0
  104. package/dist/geometry/kinds/elliott.d.ts.map +1 -0
  105. package/dist/geometry/kinds/elliott.js +116 -0
  106. package/dist/geometry/kinds/elliott.js.map +1 -0
  107. package/dist/geometry/kinds/fibonacci.d.ts +166 -0
  108. package/dist/geometry/kinds/fibonacci.d.ts.map +1 -0
  109. package/dist/geometry/kinds/fibonacci.js +458 -0
  110. package/dist/geometry/kinds/fibonacci.js.map +1 -0
  111. package/dist/geometry/kinds/freehand.d.ts +53 -0
  112. package/dist/geometry/kinds/freehand.d.ts.map +1 -0
  113. package/dist/geometry/kinds/freehand.js +115 -0
  114. package/dist/geometry/kinds/freehand.js.map +1 -0
  115. package/dist/geometry/kinds/gann.d.ts +63 -0
  116. package/dist/geometry/kinds/gann.d.ts.map +1 -0
  117. package/dist/geometry/kinds/gann.js +153 -0
  118. package/dist/geometry/kinds/gann.js.map +1 -0
  119. package/dist/geometry/kinds/lines.d.ts +90 -0
  120. package/dist/geometry/kinds/lines.d.ts.map +1 -0
  121. package/dist/geometry/kinds/lines.js +201 -0
  122. package/dist/geometry/kinds/lines.js.map +1 -0
  123. package/dist/geometry/kinds/marker.d.ts +21 -0
  124. package/dist/geometry/kinds/marker.d.ts.map +1 -0
  125. package/dist/geometry/kinds/marker.js +47 -0
  126. package/dist/geometry/kinds/marker.js.map +1 -0
  127. package/dist/geometry/kinds/patterns.d.ts +85 -0
  128. package/dist/geometry/kinds/patterns.d.ts.map +1 -0
  129. package/dist/geometry/kinds/patterns.js +133 -0
  130. package/dist/geometry/kinds/patterns.js.map +1 -0
  131. package/dist/geometry/kinds/pitchforks.d.ts +36 -0
  132. package/dist/geometry/kinds/pitchforks.d.ts.map +1 -0
  133. package/dist/geometry/kinds/pitchforks.js +109 -0
  134. package/dist/geometry/kinds/pitchforks.js.map +1 -0
  135. package/dist/geometry/project.d.ts +50 -0
  136. package/dist/geometry/project.d.ts.map +1 -0
  137. package/dist/geometry/project.js +62 -0
  138. package/dist/geometry/project.js.map +1 -0
  139. package/dist/geometry/types.d.ts +146 -0
  140. package/dist/geometry/types.d.ts.map +1 -0
  141. package/dist/geometry/types.js +4 -0
  142. package/dist/geometry/types.js.map +1 -0
  143. package/dist/index.d.ts +2 -0
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +1 -0
  146. package/dist/index.js.map +1 -1
  147. package/dist/types.d.ts +54 -0
  148. package/dist/types.d.ts.map +1 -1
  149. package/dist/types.js.map +1 -1
  150. package/dist/validation/validateEmission.d.ts.map +1 -1
  151. package/dist/validation/validateEmission.js +47 -0
  152. package/dist/validation/validateEmission.js.map +1 -1
  153. package/package.json +9 -2
@@ -0,0 +1,116 @@
1
+ import type { CircleState, EllipseState, FillBetweenState, PathState, PolylineState, RectangleState, RotatedRectangleState, TriangleState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose a `rectangle` drawing — a closed 4-corner polygon derived
5
+ * from the axis-aligned bounding box of the two projected anchors.
6
+ *
7
+ * @since 1.3
8
+ * @stable
9
+ * @example
10
+ * declare const s: RectangleState;
11
+ * declare const v: Viewport;
12
+ * const prims = decomposeRectangle(s, v);
13
+ * // prims[0].kind === "polyline"; prims[0].closed === true
14
+ * void prims;
15
+ */
16
+ export declare function decomposeRectangle(state: RectangleState, view: Viewport): ReadonlyArray<DrawPrimitive>;
17
+ /**
18
+ * Decompose a `rotated-rectangle` drawing — a closed polygon through
19
+ * the four projected anchors (carried in stroke order, so no matrix
20
+ * math is needed).
21
+ *
22
+ * @since 1.3
23
+ * @stable
24
+ * @example
25
+ * declare const s: RotatedRectangleState;
26
+ * declare const v: Viewport;
27
+ * const prims = decomposeRotatedRectangle(s, v);
28
+ * void prims;
29
+ */
30
+ export declare function decomposeRotatedRectangle(state: RotatedRectangleState, view: Viewport): ReadonlyArray<DrawPrimitive>;
31
+ /**
32
+ * Decompose a `triangle` drawing — a closed 3-vertex polygon.
33
+ *
34
+ * @since 1.3
35
+ * @stable
36
+ * @example
37
+ * declare const s: TriangleState;
38
+ * declare const v: Viewport;
39
+ * const prims = decomposeTriangle(s, v);
40
+ * void prims;
41
+ */
42
+ export declare function decomposeTriangle(state: TriangleState, view: Viewport): ReadonlyArray<DrawPrimitive>;
43
+ /**
44
+ * Decompose a `polyline` drawing — a closed N-vertex polyline carrying
45
+ * a `LineDrawStyle` (no fill).
46
+ *
47
+ * @since 1.3
48
+ * @stable
49
+ * @example
50
+ * declare const s: PolylineState;
51
+ * declare const v: Viewport;
52
+ * const prims = decomposePolyline(s, v);
53
+ * // prims[0].closed === true
54
+ * void prims;
55
+ */
56
+ export declare function decomposePolyline(state: PolylineState, view: Viewport): ReadonlyArray<DrawPrimitive>;
57
+ /**
58
+ * Decompose a `circle` drawing — an arc whose radius is the projected
59
+ * pixel distance between the centre anchor and the radius anchor (so
60
+ * the stroke keeps the same apparent thickness across zoom changes).
61
+ *
62
+ * @since 1.3
63
+ * @stable
64
+ * @example
65
+ * declare const s: CircleState;
66
+ * declare const v: Viewport;
67
+ * const prims = decomposeCircle(s, v);
68
+ * // prims[0].kind === "arc"
69
+ * void prims;
70
+ */
71
+ export declare function decomposeCircle(state: CircleState, view: Viewport): ReadonlyArray<DrawPrimitive>;
72
+ /**
73
+ * Decompose an axis-aligned `ellipse` drawing — a closed
74
+ * {@link ELLIPSE_SEGMENTS}-segment polyline inscribed in the bounding
75
+ * box of the two anchors. Rotated ellipses are out of scope.
76
+ *
77
+ * @since 1.3
78
+ * @stable
79
+ * @example
80
+ * declare const s: EllipseState;
81
+ * declare const v: Viewport;
82
+ * const prims = decomposeEllipse(s, v);
83
+ * void prims;
84
+ */
85
+ export declare function decomposeEllipse(state: EllipseState, view: Viewport): ReadonlyArray<DrawPrimitive>;
86
+ /**
87
+ * Decompose a `path` drawing — an OPEN N-vertex polyline (unless
88
+ * `style.closed === true`) carrying a `PathOpts` style (no fill).
89
+ *
90
+ * @since 1.3
91
+ * @stable
92
+ * @example
93
+ * declare const s: PathState;
94
+ * declare const v: Viewport;
95
+ * const prims = decomposePath(s, v);
96
+ * void prims;
97
+ */
98
+ export declare function decomposePath(state: PathState, view: Viewport): ReadonlyArray<DrawPrimitive>;
99
+ /**
100
+ * Decompose a `fill-between` drawing — a closed filled polygon walking
101
+ * `edgeA` forward then `edgeB` in reverse. The optional outline strokes
102
+ * only when `style.color` is set; the band fills only when `style.fill`
103
+ * is set. A degenerate edge (`< 1` point) or a non-finite mapped anchor
104
+ * is a silent no-op (returns `[]`), matching the source renderer's
105
+ * warmup behaviour.
106
+ *
107
+ * @since 1.3
108
+ * @stable
109
+ * @example
110
+ * declare const s: FillBetweenState;
111
+ * declare const v: Viewport;
112
+ * const prims = decomposeFillBetween(s, v);
113
+ * void prims;
114
+ */
115
+ export declare function decomposeFillBetween(state: FillBetweenState, view: Viewport): ReadonlyArray<DrawPrimitive>;
116
+ //# sourceMappingURL=boxes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boxes.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/boxes.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACR,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,aAAa,EAChB,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EAAE,aAAa,EAAU,QAAQ,EAAE,MAAM,aAAa,CAAC;AAcnE;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAsB9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACrC,KAAK,EAAE,qBAAqB,EAC5B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAY9B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAY9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAc9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC,aAAa,CAAC,CAkBhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAsB9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC,aAAa,CAAC,CAc5F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAChC,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CA4B9B"}
@@ -0,0 +1,285 @@
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
+ // Stroke + fill geometry moved from the canvas2d adapter's per-kind
5
+ // shape renderers
6
+ // examples/canvas2d-adapter/src/render/draw/{rectangle,
7
+ // rotatedRectangle,triangle,polyline,circle,ellipse,path,
8
+ // fillBetween}.ts.
9
+ // The originating math is invinite's rectangle / rotated-rectangle /
10
+ // triangle / polyline / circle / ellipse / path tools (commit
11
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
12
+ // MIT for chartlang.
13
+ import { dashPattern } from "../_lib/dash.js";
14
+ import { resolveShapeStyle } from "../_lib/shapeStyle.js";
15
+ import { worldPointToPixel } from "../project.js";
16
+ const DEFAULT_LINE_WIDTH = 1;
17
+ const DEFAULT_FILL_ALPHA = 1;
18
+ const TWO_PI = Math.PI * 2;
19
+ /**
20
+ * Number of polyline segments used to approximate an `ellipse`. 64
21
+ * segments give visually smooth strokes at typical viewport widths
22
+ * (≈800 px) while keeping the primitive list bounded — matching the
23
+ * canvas2d source renderer exactly.
24
+ */
25
+ const ELLIPSE_SEGMENTS = 64;
26
+ /**
27
+ * Decompose a `rectangle` drawing — a closed 4-corner polygon derived
28
+ * from the axis-aligned bounding box of the two projected anchors.
29
+ *
30
+ * @since 1.3
31
+ * @stable
32
+ * @example
33
+ * declare const s: RectangleState;
34
+ * declare const v: Viewport;
35
+ * const prims = decomposeRectangle(s, v);
36
+ * // prims[0].kind === "polyline"; prims[0].closed === true
37
+ * void prims;
38
+ */
39
+ export function decomposeRectangle(state, view) {
40
+ const a = worldPointToPixel(state.anchors[0], view);
41
+ const b = worldPointToPixel(state.anchors[1], view);
42
+ const xMin = Math.min(a.x, b.x);
43
+ const xMax = Math.max(a.x, b.x);
44
+ const yMin = Math.min(a.y, b.y);
45
+ const yMax = Math.max(a.y, b.y);
46
+ const { stroke, fill } = resolveShapeStyle(state.style);
47
+ return [
48
+ {
49
+ kind: "polyline",
50
+ points: [
51
+ { x: xMin, y: yMin },
52
+ { x: xMax, y: yMin },
53
+ { x: xMax, y: yMax },
54
+ { x: xMin, y: yMax },
55
+ ],
56
+ closed: true,
57
+ stroke,
58
+ ...(fill === undefined ? {} : { fill }),
59
+ },
60
+ ];
61
+ }
62
+ /**
63
+ * Decompose a `rotated-rectangle` drawing — a closed polygon through
64
+ * the four projected anchors (carried in stroke order, so no matrix
65
+ * math is needed).
66
+ *
67
+ * @since 1.3
68
+ * @stable
69
+ * @example
70
+ * declare const s: RotatedRectangleState;
71
+ * declare const v: Viewport;
72
+ * const prims = decomposeRotatedRectangle(s, v);
73
+ * void prims;
74
+ */
75
+ export function decomposeRotatedRectangle(state, view) {
76
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
77
+ const { stroke, fill } = resolveShapeStyle(state.style);
78
+ return [
79
+ {
80
+ kind: "polyline",
81
+ points,
82
+ closed: true,
83
+ stroke,
84
+ ...(fill === undefined ? {} : { fill }),
85
+ },
86
+ ];
87
+ }
88
+ /**
89
+ * Decompose a `triangle` drawing — a closed 3-vertex polygon.
90
+ *
91
+ * @since 1.3
92
+ * @stable
93
+ * @example
94
+ * declare const s: TriangleState;
95
+ * declare const v: Viewport;
96
+ * const prims = decomposeTriangle(s, v);
97
+ * void prims;
98
+ */
99
+ export function decomposeTriangle(state, view) {
100
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
101
+ const { stroke, fill } = resolveShapeStyle(state.style);
102
+ return [
103
+ {
104
+ kind: "polyline",
105
+ points,
106
+ closed: true,
107
+ stroke,
108
+ ...(fill === undefined ? {} : { fill }),
109
+ },
110
+ ];
111
+ }
112
+ /**
113
+ * Decompose a `polyline` drawing — a closed N-vertex polyline carrying
114
+ * a `LineDrawStyle` (no fill).
115
+ *
116
+ * @since 1.3
117
+ * @stable
118
+ * @example
119
+ * declare const s: PolylineState;
120
+ * declare const v: Viewport;
121
+ * const prims = decomposePolyline(s, v);
122
+ * // prims[0].closed === true
123
+ * void prims;
124
+ */
125
+ export function decomposePolyline(state, view) {
126
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
127
+ return [
128
+ {
129
+ kind: "polyline",
130
+ points,
131
+ closed: true,
132
+ stroke: {
133
+ color: state.style.color ?? "#000000",
134
+ width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,
135
+ dash: dashPattern(state.style.lineStyle ?? "solid"),
136
+ },
137
+ },
138
+ ];
139
+ }
140
+ /**
141
+ * Decompose a `circle` drawing — an arc whose radius is the projected
142
+ * pixel distance between the centre anchor and the radius anchor (so
143
+ * the stroke keeps the same apparent thickness across zoom changes).
144
+ *
145
+ * @since 1.3
146
+ * @stable
147
+ * @example
148
+ * declare const s: CircleState;
149
+ * declare const v: Viewport;
150
+ * const prims = decomposeCircle(s, v);
151
+ * // prims[0].kind === "arc"
152
+ * void prims;
153
+ */
154
+ export function decomposeCircle(state, view) {
155
+ const centre = worldPointToPixel(state.anchors[0], view);
156
+ const edge = worldPointToPixel(state.anchors[1], view);
157
+ const radius = Math.hypot(edge.x - centre.x, edge.y - centre.y);
158
+ const { stroke, fill } = resolveShapeStyle(state.style);
159
+ return [
160
+ {
161
+ kind: "arc",
162
+ cx: centre.x,
163
+ cy: centre.y,
164
+ r: radius,
165
+ start: 0,
166
+ end: TWO_PI,
167
+ closed: true,
168
+ stroke,
169
+ ...(fill === undefined ? {} : { fill }),
170
+ },
171
+ ];
172
+ }
173
+ /**
174
+ * Decompose an axis-aligned `ellipse` drawing — a closed
175
+ * {@link ELLIPSE_SEGMENTS}-segment polyline inscribed in the bounding
176
+ * box of the two anchors. Rotated ellipses are out of scope.
177
+ *
178
+ * @since 1.3
179
+ * @stable
180
+ * @example
181
+ * declare const s: EllipseState;
182
+ * declare const v: Viewport;
183
+ * const prims = decomposeEllipse(s, v);
184
+ * void prims;
185
+ */
186
+ export function decomposeEllipse(state, view) {
187
+ const a = worldPointToPixel(state.anchors[0], view);
188
+ const b = worldPointToPixel(state.anchors[1], view);
189
+ const cx = (a.x + b.x) / 2;
190
+ const cy = (a.y + b.y) / 2;
191
+ const rx = Math.abs(b.x - a.x) / 2;
192
+ const ry = Math.abs(b.y - a.y) / 2;
193
+ const points = [{ x: cx + rx, y: cy }];
194
+ for (let i = 1; i < ELLIPSE_SEGMENTS; i++) {
195
+ const theta = (i / ELLIPSE_SEGMENTS) * TWO_PI;
196
+ points.push({ x: cx + rx * Math.cos(theta), y: cy + ry * Math.sin(theta) });
197
+ }
198
+ const { stroke, fill } = resolveShapeStyle(state.style);
199
+ return [
200
+ {
201
+ kind: "polyline",
202
+ points,
203
+ closed: true,
204
+ stroke,
205
+ ...(fill === undefined ? {} : { fill }),
206
+ },
207
+ ];
208
+ }
209
+ /**
210
+ * Decompose a `path` drawing — an OPEN N-vertex polyline (unless
211
+ * `style.closed === true`) carrying a `PathOpts` style (no fill).
212
+ *
213
+ * @since 1.3
214
+ * @stable
215
+ * @example
216
+ * declare const s: PathState;
217
+ * declare const v: Viewport;
218
+ * const prims = decomposePath(s, v);
219
+ * void prims;
220
+ */
221
+ export function decomposePath(state, view) {
222
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
223
+ return [
224
+ {
225
+ kind: "polyline",
226
+ points,
227
+ closed: state.style.closed === true,
228
+ stroke: {
229
+ color: state.style.color ?? "#000000",
230
+ width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,
231
+ dash: dashPattern(state.style.lineStyle ?? "solid"),
232
+ },
233
+ },
234
+ ];
235
+ }
236
+ /**
237
+ * Decompose a `fill-between` drawing — a closed filled polygon walking
238
+ * `edgeA` forward then `edgeB` in reverse. The optional outline strokes
239
+ * only when `style.color` is set; the band fills only when `style.fill`
240
+ * is set. A degenerate edge (`< 1` point) or a non-finite mapped anchor
241
+ * is a silent no-op (returns `[]`), matching the source renderer's
242
+ * warmup behaviour.
243
+ *
244
+ * @since 1.3
245
+ * @stable
246
+ * @example
247
+ * declare const s: FillBetweenState;
248
+ * declare const v: Viewport;
249
+ * const prims = decomposeFillBetween(s, v);
250
+ * void prims;
251
+ */
252
+ export function decomposeFillBetween(state, view) {
253
+ const a = state.edgeA.map((p) => worldPointToPixel(p, view));
254
+ const b = state.edgeB.map((p) => worldPointToPixel(p, view));
255
+ if (a.length < 1 || b.length < 1)
256
+ return [];
257
+ if (a.some((p) => !Number.isFinite(p.x) || !Number.isFinite(p.y)))
258
+ return [];
259
+ if (b.some((p) => !Number.isFinite(p.x) || !Number.isFinite(p.y)))
260
+ return [];
261
+ const points = [...a];
262
+ for (let i = b.length - 1; i >= 0; i--)
263
+ points.push(b[i]);
264
+ const { color, fill } = state.style;
265
+ return [
266
+ {
267
+ kind: "polyline",
268
+ points,
269
+ closed: true,
270
+ ...(color === undefined
271
+ ? {}
272
+ : {
273
+ stroke: {
274
+ color,
275
+ width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,
276
+ dash: dashPattern(state.style.lineStyle ?? "solid"),
277
+ },
278
+ }),
279
+ ...(fill === undefined
280
+ ? {}
281
+ : { fill: { color: fill, alpha: state.style.fillAlpha ?? DEFAULT_FILL_ALPHA } }),
282
+ },
283
+ ];
284
+ }
285
+ //# sourceMappingURL=boxes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boxes.js","sourceRoot":"","sources":["../../../src/geometry/kinds/boxes.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// Stroke + fill geometry moved from the canvas2d adapter's per-kind\n// shape renderers\n// examples/canvas2d-adapter/src/render/draw/{rectangle,\n// rotatedRectangle,triangle,polyline,circle,ellipse,path,\n// fillBetween}.ts.\n// The originating math is invinite's rectangle / rotated-rectangle /\n// triangle / polyline / circle / ellipse / path tools (commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed\n// MIT for chartlang.\n\nimport type {\n CircleState,\n EllipseState,\n FillBetweenState,\n PathState,\n PolylineState,\n RectangleState,\n RotatedRectangleState,\n TriangleState,\n} from \"@invinite-org/chartlang-core\";\n\nimport { dashPattern } from \"../_lib/dash.js\";\nimport { resolveShapeStyle } from \"../_lib/shapeStyle.js\";\nimport { worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Point2, Viewport } from \"../types.js\";\n\nconst DEFAULT_LINE_WIDTH = 1;\nconst DEFAULT_FILL_ALPHA = 1;\nconst TWO_PI = Math.PI * 2;\n\n/**\n * Number of polyline segments used to approximate an `ellipse`. 64\n * segments give visually smooth strokes at typical viewport widths\n * (≈800 px) while keeping the primitive list bounded — matching the\n * canvas2d source renderer exactly.\n */\nconst ELLIPSE_SEGMENTS = 64;\n\n/**\n * Decompose a `rectangle` drawing — a closed 4-corner polygon derived\n * from the axis-aligned bounding box of the two projected anchors.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: RectangleState;\n * declare const v: Viewport;\n * const prims = decomposeRectangle(s, v);\n * // prims[0].kind === \"polyline\"; prims[0].closed === true\n * void prims;\n */\nexport function decomposeRectangle(\n state: RectangleState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const xMin = Math.min(a.x, b.x);\n const xMax = Math.max(a.x, b.x);\n const yMin = Math.min(a.y, b.y);\n const yMax = Math.max(a.y, b.y);\n const { stroke, fill } = resolveShapeStyle(state.style);\n return [\n {\n kind: \"polyline\",\n points: [\n { x: xMin, y: yMin },\n { x: xMax, y: yMin },\n { x: xMax, y: yMax },\n { x: xMin, y: yMax },\n ],\n closed: true,\n stroke,\n ...(fill === undefined ? {} : { fill }),\n },\n ];\n}\n\n/**\n * Decompose a `rotated-rectangle` drawing — a closed polygon through\n * the four projected anchors (carried in stroke order, so no matrix\n * math is needed).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: RotatedRectangleState;\n * declare const v: Viewport;\n * const prims = decomposeRotatedRectangle(s, v);\n * void prims;\n */\nexport function decomposeRotatedRectangle(\n state: RotatedRectangleState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n const { stroke, fill } = resolveShapeStyle(state.style);\n return [\n {\n kind: \"polyline\",\n points,\n closed: true,\n stroke,\n ...(fill === undefined ? {} : { fill }),\n },\n ];\n}\n\n/**\n * Decompose a `triangle` drawing — a closed 3-vertex polygon.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: TriangleState;\n * declare const v: Viewport;\n * const prims = decomposeTriangle(s, v);\n * void prims;\n */\nexport function decomposeTriangle(\n state: TriangleState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n const { stroke, fill } = resolveShapeStyle(state.style);\n return [\n {\n kind: \"polyline\",\n points,\n closed: true,\n stroke,\n ...(fill === undefined ? {} : { fill }),\n },\n ];\n}\n\n/**\n * Decompose a `polyline` drawing — a closed N-vertex polyline carrying\n * a `LineDrawStyle` (no fill).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: PolylineState;\n * declare const v: Viewport;\n * const prims = decomposePolyline(s, v);\n * // prims[0].closed === true\n * void prims;\n */\nexport function decomposePolyline(\n state: PolylineState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n return [\n {\n kind: \"polyline\",\n points,\n closed: true,\n stroke: {\n color: state.style.color ?? \"#000000\",\n width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n dash: dashPattern(state.style.lineStyle ?? \"solid\"),\n },\n },\n ];\n}\n\n/**\n * Decompose a `circle` drawing — an arc whose radius is the projected\n * pixel distance between the centre anchor and the radius anchor (so\n * the stroke keeps the same apparent thickness across zoom changes).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: CircleState;\n * declare const v: Viewport;\n * const prims = decomposeCircle(s, v);\n * // prims[0].kind === \"arc\"\n * void prims;\n */\nexport function decomposeCircle(state: CircleState, view: Viewport): ReadonlyArray<DrawPrimitive> {\n const centre = worldPointToPixel(state.anchors[0], view);\n const edge = worldPointToPixel(state.anchors[1], view);\n const radius = Math.hypot(edge.x - centre.x, edge.y - centre.y);\n const { stroke, fill } = resolveShapeStyle(state.style);\n return [\n {\n kind: \"arc\",\n cx: centre.x,\n cy: centre.y,\n r: radius,\n start: 0,\n end: TWO_PI,\n closed: true,\n stroke,\n ...(fill === undefined ? {} : { fill }),\n },\n ];\n}\n\n/**\n * Decompose an axis-aligned `ellipse` drawing — a closed\n * {@link ELLIPSE_SEGMENTS}-segment polyline inscribed in the bounding\n * box of the two anchors. Rotated ellipses are out of scope.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: EllipseState;\n * declare const v: Viewport;\n * const prims = decomposeEllipse(s, v);\n * void prims;\n */\nexport function decomposeEllipse(\n state: EllipseState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const cx = (a.x + b.x) / 2;\n const cy = (a.y + b.y) / 2;\n const rx = Math.abs(b.x - a.x) / 2;\n const ry = Math.abs(b.y - a.y) / 2;\n const points: Point2[] = [{ x: cx + rx, y: cy }];\n for (let i = 1; i < ELLIPSE_SEGMENTS; i++) {\n const theta = (i / ELLIPSE_SEGMENTS) * TWO_PI;\n points.push({ x: cx + rx * Math.cos(theta), y: cy + ry * Math.sin(theta) });\n }\n const { stroke, fill } = resolveShapeStyle(state.style);\n return [\n {\n kind: \"polyline\",\n points,\n closed: true,\n stroke,\n ...(fill === undefined ? {} : { fill }),\n },\n ];\n}\n\n/**\n * Decompose a `path` drawing — an OPEN N-vertex polyline (unless\n * `style.closed === true`) carrying a `PathOpts` style (no fill).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: PathState;\n * declare const v: Viewport;\n * const prims = decomposePath(s, v);\n * void prims;\n */\nexport function decomposePath(state: PathState, view: Viewport): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n return [\n {\n kind: \"polyline\",\n points,\n closed: state.style.closed === true,\n stroke: {\n color: state.style.color ?? \"#000000\",\n width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n dash: dashPattern(state.style.lineStyle ?? \"solid\"),\n },\n },\n ];\n}\n\n/**\n * Decompose a `fill-between` drawing — a closed filled polygon walking\n * `edgeA` forward then `edgeB` in reverse. The optional outline strokes\n * only when `style.color` is set; the band fills only when `style.fill`\n * is set. A degenerate edge (`< 1` point) or a non-finite mapped anchor\n * is a silent no-op (returns `[]`), matching the source renderer's\n * warmup behaviour.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: FillBetweenState;\n * declare const v: Viewport;\n * const prims = decomposeFillBetween(s, v);\n * void prims;\n */\nexport function decomposeFillBetween(\n state: FillBetweenState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = state.edgeA.map((p) => worldPointToPixel(p, view));\n const b = state.edgeB.map((p) => worldPointToPixel(p, view));\n if (a.length < 1 || b.length < 1) return [];\n if (a.some((p) => !Number.isFinite(p.x) || !Number.isFinite(p.y))) return [];\n if (b.some((p) => !Number.isFinite(p.x) || !Number.isFinite(p.y))) return [];\n const points: Point2[] = [...a];\n for (let i = b.length - 1; i >= 0; i--) points.push(b[i]);\n const { color, fill } = state.style;\n return [\n {\n kind: \"polyline\",\n points,\n closed: true,\n ...(color === undefined\n ? {}\n : {\n stroke: {\n color,\n width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n dash: dashPattern(state.style.lineStyle ?? \"solid\"),\n },\n }),\n ...(fill === undefined\n ? {}\n : { fill: { color: fill, alpha: state.style.fillAlpha ?? DEFAULT_FILL_ALPHA } }),\n },\n ];\n}\n"]}
@@ -0,0 +1,72 @@
1
+ import type { DisjointChannelState, FlatTopBottomState, RegressionTrendState, TrendChannelState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose a `trend-channel` drawing — two parallel line segments. The
5
+ * primary rail runs `anchors[0]` → `anchors[1]`; the second rail is its
6
+ * translate by the offset vector `anchors[2] − anchors[0]`, keeping the
7
+ * pair strictly parallel. Stroke-only — `LineDrawStyle` carries no fill,
8
+ * so there is no inter-rail band (matching the canvas2d source).
9
+ *
10
+ * @since 1.3
11
+ * @stable
12
+ * @example
13
+ * declare const s: TrendChannelState;
14
+ * declare const v: Viewport;
15
+ * const prims = decomposeTrendChannel(s, v);
16
+ * // prims.length === 2
17
+ * void prims;
18
+ */
19
+ export declare function decomposeTrendChannel(state: TrendChannelState, view: Viewport): ReadonlyArray<DrawPrimitive>;
20
+ /**
21
+ * Decompose a `flat-top-bottom` drawing — two horizontal rails. The top
22
+ * rail sits at `max(anchors[0].price, anchors[2].price)` and the bottom
23
+ * at `min(...)`; both span the time range `anchors[0].time` →
24
+ * `anchors[1].time`. Stroke-only.
25
+ *
26
+ * @since 1.3
27
+ * @stable
28
+ * @example
29
+ * declare const s: FlatTopBottomState;
30
+ * declare const v: Viewport;
31
+ * const prims = decomposeFlatTopBottom(s, v);
32
+ * // prims.length === 2
33
+ * void prims;
34
+ */
35
+ export declare function decomposeFlatTopBottom(state: FlatTopBottomState, view: Viewport): ReadonlyArray<DrawPrimitive>;
36
+ /**
37
+ * Decompose a `disjoint-channel` drawing — two independent line segments
38
+ * with no shared geometry constraint: `anchors[0]` → `anchors[1]` and
39
+ * `anchors[2]` → `anchors[3]`. Stroke-only.
40
+ *
41
+ * @since 1.3
42
+ * @stable
43
+ * @example
44
+ * declare const s: DisjointChannelState;
45
+ * declare const v: Viewport;
46
+ * const prims = decomposeDisjointChannel(s, v);
47
+ * // prims.length === 2
48
+ * void prims;
49
+ */
50
+ export declare function decomposeDisjointChannel(state: DisjointChannelState, view: Viewport): ReadonlyArray<DrawPrimitive>;
51
+ /**
52
+ * Decompose a `regression-trend` drawing — a single placeholder line
53
+ * between the two anchors, colour `style.color ?? "#3b82f6"`, width 1,
54
+ * solid. The OLS fit + ±σ bands the `RegressionTrendOpts` flags
55
+ * (`source` / `stdevMultiplier` / `showUpperBand` / `showLowerBand`)
56
+ * describe require a bar buffer the `Viewport` does not expose and band
57
+ * anchors the 2-point `state.anchors` does not carry; the canvas2d
58
+ * source renders the same placeholder line, so this decomposer preserves
59
+ * that geometry. Those style fields are accepted but unused here —
60
+ * consumer adapters with a bar buffer can compute the fit themselves.
61
+ *
62
+ * @since 1.3
63
+ * @stable
64
+ * @example
65
+ * declare const s: RegressionTrendState;
66
+ * declare const v: Viewport;
67
+ * const prims = decomposeRegressionTrend(s, v);
68
+ * // prims.length === 1; prims[0].kind === "polyline"
69
+ * void prims;
70
+ */
71
+ export declare function decomposeRegressionTrend(state: RegressionTrendState, view: Viewport): ReadonlyArray<DrawPrimitive>;
72
+ //# sourceMappingURL=channels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/channels.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACR,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACpB,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAU3D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACjC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAmB9B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAClC,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAe9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAU9B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAe9B"}
@@ -0,0 +1,148 @@
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
+ // Channel geometry moved from the canvas2d adapter's per-kind renderers
5
+ // examples/canvas2d-adapter/src/render/draw/{trendChannel,
6
+ // flatTopBottom,disjointChannel,regressionTrend}.ts.
7
+ // The originating math is invinite's trend-channel / flat-top-bottom /
8
+ // disjoint-channel / regression-trend tools (commit
9
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
10
+ // MIT for chartlang.
11
+ import { dashPattern } from "../_lib/dash.js";
12
+ import { strokeOf } from "../_lib/strokeStyle.js";
13
+ import { worldPointToPixel } from "../project.js";
14
+ const DEFAULT_LINE_WIDTH = 1;
15
+ /**
16
+ * Default stroke colour of a `regression-trend` placeholder line —
17
+ * `#3b82f6`, matching the canvas2d source renderer.
18
+ */
19
+ const REGRESSION_DEFAULT_COLOR = "#3b82f6";
20
+ /**
21
+ * Decompose a `trend-channel` drawing — two parallel line segments. The
22
+ * primary rail runs `anchors[0]` → `anchors[1]`; the second rail is its
23
+ * translate by the offset vector `anchors[2] − anchors[0]`, keeping the
24
+ * pair strictly parallel. Stroke-only — `LineDrawStyle` carries no fill,
25
+ * so there is no inter-rail band (matching the canvas2d source).
26
+ *
27
+ * @since 1.3
28
+ * @stable
29
+ * @example
30
+ * declare const s: TrendChannelState;
31
+ * declare const v: Viewport;
32
+ * const prims = decomposeTrendChannel(s, v);
33
+ * // prims.length === 2
34
+ * void prims;
35
+ */
36
+ export function decomposeTrendChannel(state, view) {
37
+ const a = worldPointToPixel(state.anchors[0], view);
38
+ const b = worldPointToPixel(state.anchors[1], view);
39
+ const hook = worldPointToPixel(state.anchors[2], view);
40
+ const dx = hook.x - a.x;
41
+ const dy = hook.y - a.y;
42
+ const stroke = strokeOf(state.style);
43
+ return [
44
+ { kind: "polyline", points: [a, b], closed: false, stroke },
45
+ {
46
+ kind: "polyline",
47
+ points: [
48
+ { x: a.x + dx, y: a.y + dy },
49
+ { x: b.x + dx, y: b.y + dy },
50
+ ],
51
+ closed: false,
52
+ stroke,
53
+ },
54
+ ];
55
+ }
56
+ /**
57
+ * Decompose a `flat-top-bottom` drawing — two horizontal rails. The top
58
+ * rail sits at `max(anchors[0].price, anchors[2].price)` and the bottom
59
+ * at `min(...)`; both span the time range `anchors[0].time` →
60
+ * `anchors[1].time`. Stroke-only.
61
+ *
62
+ * @since 1.3
63
+ * @stable
64
+ * @example
65
+ * declare const s: FlatTopBottomState;
66
+ * declare const v: Viewport;
67
+ * const prims = decomposeFlatTopBottom(s, v);
68
+ * // prims.length === 2
69
+ * void prims;
70
+ */
71
+ export function decomposeFlatTopBottom(state, view) {
72
+ const p0 = state.anchors[0];
73
+ const p1 = state.anchors[1];
74
+ const p2 = state.anchors[2];
75
+ const topPrice = Math.max(p0.price, p2.price);
76
+ const bottomPrice = Math.min(p0.price, p2.price);
77
+ const topLeft = worldPointToPixel({ time: p0.time, price: topPrice }, view);
78
+ const topRight = worldPointToPixel({ time: p1.time, price: topPrice }, view);
79
+ const bottomLeft = worldPointToPixel({ time: p0.time, price: bottomPrice }, view);
80
+ const bottomRight = worldPointToPixel({ time: p1.time, price: bottomPrice }, view);
81
+ const stroke = strokeOf(state.style);
82
+ return [
83
+ { kind: "polyline", points: [topLeft, topRight], closed: false, stroke },
84
+ { kind: "polyline", points: [bottomLeft, bottomRight], closed: false, stroke },
85
+ ];
86
+ }
87
+ /**
88
+ * Decompose a `disjoint-channel` drawing — two independent line segments
89
+ * with no shared geometry constraint: `anchors[0]` → `anchors[1]` and
90
+ * `anchors[2]` → `anchors[3]`. Stroke-only.
91
+ *
92
+ * @since 1.3
93
+ * @stable
94
+ * @example
95
+ * declare const s: DisjointChannelState;
96
+ * declare const v: Viewport;
97
+ * const prims = decomposeDisjointChannel(s, v);
98
+ * // prims.length === 2
99
+ * void prims;
100
+ */
101
+ export function decomposeDisjointChannel(state, view) {
102
+ const a = worldPointToPixel(state.anchors[0], view);
103
+ const b = worldPointToPixel(state.anchors[1], view);
104
+ const c = worldPointToPixel(state.anchors[2], view);
105
+ const d = worldPointToPixel(state.anchors[3], view);
106
+ const stroke = strokeOf(state.style);
107
+ return [
108
+ { kind: "polyline", points: [a, b], closed: false, stroke },
109
+ { kind: "polyline", points: [c, d], closed: false, stroke },
110
+ ];
111
+ }
112
+ /**
113
+ * Decompose a `regression-trend` drawing — a single placeholder line
114
+ * between the two anchors, colour `style.color ?? "#3b82f6"`, width 1,
115
+ * solid. The OLS fit + ±σ bands the `RegressionTrendOpts` flags
116
+ * (`source` / `stdevMultiplier` / `showUpperBand` / `showLowerBand`)
117
+ * describe require a bar buffer the `Viewport` does not expose and band
118
+ * anchors the 2-point `state.anchors` does not carry; the canvas2d
119
+ * source renders the same placeholder line, so this decomposer preserves
120
+ * that geometry. Those style fields are accepted but unused here —
121
+ * consumer adapters with a bar buffer can compute the fit themselves.
122
+ *
123
+ * @since 1.3
124
+ * @stable
125
+ * @example
126
+ * declare const s: RegressionTrendState;
127
+ * declare const v: Viewport;
128
+ * const prims = decomposeRegressionTrend(s, v);
129
+ * // prims.length === 1; prims[0].kind === "polyline"
130
+ * void prims;
131
+ */
132
+ export function decomposeRegressionTrend(state, view) {
133
+ const a = worldPointToPixel(state.anchors[0], view);
134
+ const b = worldPointToPixel(state.anchors[1], view);
135
+ return [
136
+ {
137
+ kind: "polyline",
138
+ points: [a, b],
139
+ closed: false,
140
+ stroke: {
141
+ color: state.style.color ?? REGRESSION_DEFAULT_COLOR,
142
+ width: DEFAULT_LINE_WIDTH,
143
+ dash: dashPattern("solid"),
144
+ },
145
+ },
146
+ ];
147
+ }
148
+ //# sourceMappingURL=channels.js.map