@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,115 @@
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
+ // Freehand geometry moved from the canvas2d adapter's per-kind renderers
5
+ // examples/canvas2d-adapter/src/render/draw/{pen,highlighter,brush}.ts.
6
+ // The originating math is invinite's pen / highlighter / brush tools
7
+ // (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite);
8
+ // re-licensed MIT for chartlang.
9
+ import { dashPattern } from "../_lib/dash.js";
10
+ import { worldPointToPixel } from "../project.js";
11
+ const DEFAULT_COLOR = "#000000";
12
+ const DEFAULT_LINE_WIDTH = 1;
13
+ /**
14
+ * Stroke width of a `highlighter` freehand stroke — fixed at 6 px to
15
+ * match invinite's chunky-highlighter appearance (the `HighlighterStyle`
16
+ * type carries no `lineWidth` field), matching the canvas2d source.
17
+ */
18
+ const HIGHLIGHTER_LINE_WIDTH = 6;
19
+ /**
20
+ * Fill opacity of a `brush` freehand region — fully opaque, mirroring
21
+ * the canvas2d source's `ctx.fill()` at the default `globalAlpha = 1`.
22
+ */
23
+ const BRUSH_FILL_ALPHA = 1;
24
+ /**
25
+ * Decompose a `pen` drawing — a freehand stroke as one open polyline
26
+ * through the projected anchors, stroke-only with a `LineDrawStyle`.
27
+ *
28
+ * @since 1.3
29
+ * @stable
30
+ * @example
31
+ * declare const s: PenState;
32
+ * declare const v: Viewport;
33
+ * const prims = decomposePen(s, v);
34
+ * // prims[0].kind === "polyline"; prims[0].closed === false
35
+ * void prims;
36
+ */
37
+ export function decomposePen(state, view) {
38
+ return [
39
+ {
40
+ kind: "polyline",
41
+ points: state.anchors.map((p) => worldPointToPixel(p, view)),
42
+ closed: false,
43
+ stroke: {
44
+ color: state.style.color ?? DEFAULT_COLOR,
45
+ width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,
46
+ dash: dashPattern(state.style.lineStyle ?? "solid"),
47
+ },
48
+ },
49
+ ];
50
+ }
51
+ /**
52
+ * Decompose a `highlighter` drawing — a thick translucent freehand
53
+ * stroke as one open polyline. The translucency rides on the IR
54
+ * `StrokeStyle.alpha` (set to `style.alpha`); the painter brackets the
55
+ * `stroke()` in `globalAlpha`, scoping it to this drawing only — exactly
56
+ * the canvas2d source's `globalAlpha` bracket. Width is the fixed
57
+ * {@link HIGHLIGHTER_LINE_WIDTH}; both `color` and `alpha` are required
58
+ * by `HighlighterStyle`.
59
+ *
60
+ * @since 1.3
61
+ * @stable
62
+ * @example
63
+ * declare const s: HighlighterState;
64
+ * declare const v: Viewport;
65
+ * const prims = decomposeHighlighter(s, v);
66
+ * // prims[0].kind === "polyline"; prims[0].stroke?.alpha is set
67
+ * void prims;
68
+ */
69
+ export function decomposeHighlighter(state, view) {
70
+ return [
71
+ {
72
+ kind: "polyline",
73
+ points: state.anchors.map((p) => worldPointToPixel(p, view)),
74
+ closed: false,
75
+ stroke: {
76
+ color: state.style.color,
77
+ width: HIGHLIGHTER_LINE_WIDTH,
78
+ dash: dashPattern("solid"),
79
+ alpha: state.style.alpha,
80
+ },
81
+ },
82
+ ];
83
+ }
84
+ /**
85
+ * Decompose a `brush` drawing — a freehand region as one closed polyline
86
+ * carrying both a `fill` (`style.fill` at full opacity) and a `stroke`
87
+ * (`style.stroke`, width 1). The painter fills before stroking, so the
88
+ * outline draws on top of the filled region. Both colours are required
89
+ * by `BrushStyle`.
90
+ *
91
+ * @since 1.3
92
+ * @stable
93
+ * @example
94
+ * declare const s: BrushState;
95
+ * declare const v: Viewport;
96
+ * const prims = decomposeBrush(s, v);
97
+ * // prims[0].kind === "polyline"; prims[0].closed === true
98
+ * void prims;
99
+ */
100
+ export function decomposeBrush(state, view) {
101
+ return [
102
+ {
103
+ kind: "polyline",
104
+ points: state.anchors.map((p) => worldPointToPixel(p, view)),
105
+ closed: true,
106
+ stroke: {
107
+ color: state.style.stroke,
108
+ width: DEFAULT_LINE_WIDTH,
109
+ dash: dashPattern("solid"),
110
+ },
111
+ fill: { color: state.style.fill, alpha: BRUSH_FILL_ALPHA },
112
+ },
113
+ ];
114
+ }
115
+ //# sourceMappingURL=freehand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"freehand.js","sourceRoot":"","sources":["../../../src/geometry/kinds/freehand.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,qEAAqE;AACrE,iEAAiE;AACjE,iCAAiC;AAIjC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGlD,MAAM,aAAa,GAAG,SAAS,CAAC;AAChC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAEjC;;;GAGG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAAC,KAAe,EAAE,IAAc;IACxD,OAAO;QACH;YACI,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YAC5D,MAAM,EAAE,KAAK;YACb,MAAM,EAAE;gBACJ,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,aAAa;gBACzC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,kBAAkB;gBAClD,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,OAAO,CAAC;aACtD;SACJ;KACJ,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAChC,KAAuB,EACvB,IAAc;IAEd,OAAO;QACH;YACI,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YAC5D,MAAM,EAAE,KAAK;YACb,MAAM,EAAE;gBACJ,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK;gBACxB,KAAK,EAAE,sBAAsB;gBAC7B,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC;gBAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK;aAC3B;SACJ;KACJ,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,KAAiB,EAAE,IAAc;IAC5D,OAAO;QACH;YACI,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YAC5D,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE;gBACJ,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;gBACzB,KAAK,EAAE,kBAAkB;gBACzB,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC;aAC7B;YACD,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE;SAC7D;KACJ,CAAC;AACN,CAAC","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// Freehand geometry moved from the canvas2d adapter's per-kind renderers\n// examples/canvas2d-adapter/src/render/draw/{pen,highlighter,brush}.ts.\n// The originating math is invinite's pen / highlighter / brush tools\n// (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite);\n// re-licensed MIT for chartlang.\n\nimport type { BrushState, HighlighterState, PenState } from \"@invinite-org/chartlang-core\";\n\nimport { dashPattern } from \"../_lib/dash.js\";\nimport { worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Viewport } from \"../types.js\";\n\nconst DEFAULT_COLOR = \"#000000\";\nconst DEFAULT_LINE_WIDTH = 1;\n\n/**\n * Stroke width of a `highlighter` freehand stroke — fixed at 6 px to\n * match invinite's chunky-highlighter appearance (the `HighlighterStyle`\n * type carries no `lineWidth` field), matching the canvas2d source.\n */\nconst HIGHLIGHTER_LINE_WIDTH = 6;\n\n/**\n * Fill opacity of a `brush` freehand region — fully opaque, mirroring\n * the canvas2d source's `ctx.fill()` at the default `globalAlpha = 1`.\n */\nconst BRUSH_FILL_ALPHA = 1;\n\n/**\n * Decompose a `pen` drawing — a freehand stroke as one open polyline\n * through the projected anchors, stroke-only with a `LineDrawStyle`.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: PenState;\n * declare const v: Viewport;\n * const prims = decomposePen(s, v);\n * // prims[0].kind === \"polyline\"; prims[0].closed === false\n * void prims;\n */\nexport function decomposePen(state: PenState, view: Viewport): ReadonlyArray<DrawPrimitive> {\n return [\n {\n kind: \"polyline\",\n points: state.anchors.map((p) => worldPointToPixel(p, view)),\n closed: false,\n stroke: {\n color: state.style.color ?? DEFAULT_COLOR,\n width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n dash: dashPattern(state.style.lineStyle ?? \"solid\"),\n },\n },\n ];\n}\n\n/**\n * Decompose a `highlighter` drawing — a thick translucent freehand\n * stroke as one open polyline. The translucency rides on the IR\n * `StrokeStyle.alpha` (set to `style.alpha`); the painter brackets the\n * `stroke()` in `globalAlpha`, scoping it to this drawing only — exactly\n * the canvas2d source's `globalAlpha` bracket. Width is the fixed\n * {@link HIGHLIGHTER_LINE_WIDTH}; both `color` and `alpha` are required\n * by `HighlighterStyle`.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: HighlighterState;\n * declare const v: Viewport;\n * const prims = decomposeHighlighter(s, v);\n * // prims[0].kind === \"polyline\"; prims[0].stroke?.alpha is set\n * void prims;\n */\nexport function decomposeHighlighter(\n state: HighlighterState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n return [\n {\n kind: \"polyline\",\n points: state.anchors.map((p) => worldPointToPixel(p, view)),\n closed: false,\n stroke: {\n color: state.style.color,\n width: HIGHLIGHTER_LINE_WIDTH,\n dash: dashPattern(\"solid\"),\n alpha: state.style.alpha,\n },\n },\n ];\n}\n\n/**\n * Decompose a `brush` drawing — a freehand region as one closed polyline\n * carrying both a `fill` (`style.fill` at full opacity) and a `stroke`\n * (`style.stroke`, width 1). The painter fills before stroking, so the\n * outline draws on top of the filled region. Both colours are required\n * by `BrushStyle`.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: BrushState;\n * declare const v: Viewport;\n * const prims = decomposeBrush(s, v);\n * // prims[0].kind === \"polyline\"; prims[0].closed === true\n * void prims;\n */\nexport function decomposeBrush(state: BrushState, view: Viewport): ReadonlyArray<DrawPrimitive> {\n return [\n {\n kind: \"polyline\",\n points: state.anchors.map((p) => worldPointToPixel(p, view)),\n closed: true,\n stroke: {\n color: state.style.stroke,\n width: DEFAULT_LINE_WIDTH,\n dash: dashPattern(\"solid\"),\n },\n fill: { color: state.style.fill, alpha: BRUSH_FILL_ALPHA },\n },\n ];\n}\n"]}
@@ -0,0 +1,63 @@
1
+ import type { GannBoxState, GannFanState, GannSquareFixedState, GannSquareState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose a `gann-box` drawing — a ratio grid spanning the bounding
5
+ * rectangle of the two world anchors, one horizontal + one vertical
6
+ * line at each {@link GANN_LEVELS} entry (5 + 5 = 10 polylines for the
7
+ * default 1/4 subdivisions, including the outer rectangle at 0 and 1.0).
8
+ *
9
+ * @since 1.3
10
+ * @stable
11
+ * @example
12
+ * declare const s: GannBoxState;
13
+ * declare const v: Viewport;
14
+ * const prims = decomposeGannBox(s, v);
15
+ * void prims;
16
+ */
17
+ export declare function decomposeGannBox(state: GannBoxState, view: Viewport): ReadonlyArray<DrawPrimitive>;
18
+ /**
19
+ * Decompose a `gann-square-fixed` drawing — an `80×80` pixel square
20
+ * anchored at `anchor`, subdivided by {@link GANN_LEVELS}. The fixed
21
+ * pixel side mirrors the canvas2d source's deterministic constant.
22
+ *
23
+ * @since 1.3
24
+ * @stable
25
+ * @example
26
+ * declare const s: GannSquareFixedState;
27
+ * declare const v: Viewport;
28
+ * const prims = decomposeGannSquareFixed(s, v);
29
+ * void prims;
30
+ */
31
+ export declare function decomposeGannSquareFixed(state: GannSquareFixedState, view: Viewport): ReadonlyArray<DrawPrimitive>;
32
+ /**
33
+ * Decompose a `gann-square` drawing — a square anchored at `anchors[0]`
34
+ * with side `max(|dx|, |dy|)` in pixel space (the Gann-1×1 default),
35
+ * extended in the direction of `anchors[1]`. Subdivisions follow
36
+ * {@link GANN_LEVELS}.
37
+ *
38
+ * @since 1.3
39
+ * @stable
40
+ * @example
41
+ * declare const s: GannSquareState;
42
+ * declare const v: Viewport;
43
+ * const prims = decomposeGannSquare(s, v);
44
+ * void prims;
45
+ */
46
+ export declare function decomposeGannSquare(state: GannSquareState, view: Viewport): ReadonlyArray<DrawPrimitive>;
47
+ /**
48
+ * Decompose a `gann-fan` drawing — 9 rays from `anchors[0]`, each with
49
+ * direction `(dx, ratio · dy)` where `(dx, dy)` is the `anchors[0] →
50
+ * anchors[1]` vector and `ratio` cycles through {@link GANN_FAN_RATIOS}.
51
+ * Rays extend to `max(pxWidth, pxHeight) · 2` so they exit the viewport;
52
+ * a zero-magnitude ray is skipped (matching the source `continue`).
53
+ *
54
+ * @since 1.3
55
+ * @stable
56
+ * @example
57
+ * declare const s: GannFanState;
58
+ * declare const v: Viewport;
59
+ * const prims = decomposeGannFan(s, v);
60
+ * void prims;
61
+ */
62
+ export declare function decomposeGannFan(state: GannFanState, view: Viewport): ReadonlyArray<DrawPrimitive>;
63
+ //# sourceMappingURL=gann.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gann.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/gann.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACR,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,eAAe,EAClB,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAgD3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAW9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAU9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAkB9B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAwB9B"}
@@ -0,0 +1,153 @@
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
+ // Gann geometry moved from the canvas2d adapter's per-kind renderers
5
+ // examples/canvas2d-adapter/src/render/draw/{gannBox,gannSquareFixed,
6
+ // gannSquare,gannFan}.ts.
7
+ // The originating math is invinite's gann-box / gann-square /
8
+ // gann-fan tools (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02,
9
+ // © Invinite); re-licensed MIT for chartlang.
10
+ import { SOLID_DASH } from "../_lib/dash.js";
11
+ import { GANN_FAN_RATIOS, GANN_LEVELS } from "../_lib/gannLevels.js";
12
+ import { worldPointToPixel } from "../project.js";
13
+ const DEFAULT_COLOR = "#a855f7";
14
+ const DEFAULT_LINE_WIDTH = 1;
15
+ const SIDE_PX = 80;
16
+ /**
17
+ * Shared subdivision-grid builder for the three Gann box kinds — one
18
+ * horizontal + one vertical polyline at every {@link GANN_LEVELS} ratio
19
+ * across the `[left, right] × [top, bottom]` rectangle.
20
+ */
21
+ function gridPolylines(left, right, top, bottom, color, width) {
22
+ const stroke = { color, width, dash: SOLID_DASH };
23
+ const out = [];
24
+ for (const level of GANN_LEVELS) {
25
+ const y = top + level * (bottom - top);
26
+ out.push({
27
+ kind: "polyline",
28
+ points: [
29
+ { x: left, y },
30
+ { x: right, y },
31
+ ],
32
+ closed: false,
33
+ stroke,
34
+ });
35
+ }
36
+ for (const level of GANN_LEVELS) {
37
+ const x = left + level * (right - left);
38
+ out.push({
39
+ kind: "polyline",
40
+ points: [
41
+ { x, y: top },
42
+ { x, y: bottom },
43
+ ],
44
+ closed: false,
45
+ stroke,
46
+ });
47
+ }
48
+ return out;
49
+ }
50
+ /**
51
+ * Decompose a `gann-box` drawing — a ratio grid spanning the bounding
52
+ * rectangle of the two world anchors, one horizontal + one vertical
53
+ * line at each {@link GANN_LEVELS} entry (5 + 5 = 10 polylines for the
54
+ * default 1/4 subdivisions, including the outer rectangle at 0 and 1.0).
55
+ *
56
+ * @since 1.3
57
+ * @stable
58
+ * @example
59
+ * declare const s: GannBoxState;
60
+ * declare const v: Viewport;
61
+ * const prims = decomposeGannBox(s, v);
62
+ * void prims;
63
+ */
64
+ export function decomposeGannBox(state, view) {
65
+ const a = worldPointToPixel(state.anchors[0], view);
66
+ const b = worldPointToPixel(state.anchors[1], view);
67
+ return gridPolylines(Math.min(a.x, b.x), Math.max(a.x, b.x), Math.min(a.y, b.y), Math.max(a.y, b.y), state.style.color ?? DEFAULT_COLOR, state.style.lineWidth ?? DEFAULT_LINE_WIDTH);
68
+ }
69
+ /**
70
+ * Decompose a `gann-square-fixed` drawing — an `80×80` pixel square
71
+ * anchored at `anchor`, subdivided by {@link GANN_LEVELS}. The fixed
72
+ * pixel side mirrors the canvas2d source's deterministic constant.
73
+ *
74
+ * @since 1.3
75
+ * @stable
76
+ * @example
77
+ * declare const s: GannSquareFixedState;
78
+ * declare const v: Viewport;
79
+ * const prims = decomposeGannSquareFixed(s, v);
80
+ * void prims;
81
+ */
82
+ export function decomposeGannSquareFixed(state, view) {
83
+ const origin = worldPointToPixel(state.anchor, view);
84
+ return gridPolylines(origin.x, origin.x + SIDE_PX, origin.y, origin.y + SIDE_PX, state.style.color ?? DEFAULT_COLOR, state.style.lineWidth ?? DEFAULT_LINE_WIDTH);
85
+ }
86
+ /**
87
+ * Decompose a `gann-square` drawing — a square anchored at `anchors[0]`
88
+ * with side `max(|dx|, |dy|)` in pixel space (the Gann-1×1 default),
89
+ * extended in the direction of `anchors[1]`. Subdivisions follow
90
+ * {@link GANN_LEVELS}.
91
+ *
92
+ * @since 1.3
93
+ * @stable
94
+ * @example
95
+ * declare const s: GannSquareState;
96
+ * declare const v: Viewport;
97
+ * const prims = decomposeGannSquare(s, v);
98
+ * void prims;
99
+ */
100
+ export function decomposeGannSquare(state, view) {
101
+ const a = worldPointToPixel(state.anchors[0], view);
102
+ const b = worldPointToPixel(state.anchors[1], view);
103
+ const side = Math.max(Math.abs(b.x - a.x), Math.abs(b.y - a.y));
104
+ const signX = b.x >= a.x ? 1 : -1;
105
+ const signY = b.y >= a.y ? 1 : -1;
106
+ const left = signX === 1 ? a.x : a.x - side;
107
+ const right = signX === 1 ? a.x + side : a.x;
108
+ const top = signY === 1 ? a.y : a.y - side;
109
+ const bottom = signY === 1 ? a.y + side : a.y;
110
+ return gridPolylines(left, right, top, bottom, state.style.color ?? DEFAULT_COLOR, state.style.lineWidth ?? DEFAULT_LINE_WIDTH);
111
+ }
112
+ /**
113
+ * Decompose a `gann-fan` drawing — 9 rays from `anchors[0]`, each with
114
+ * direction `(dx, ratio · dy)` where `(dx, dy)` is the `anchors[0] →
115
+ * anchors[1]` vector and `ratio` cycles through {@link GANN_FAN_RATIOS}.
116
+ * Rays extend to `max(pxWidth, pxHeight) · 2` so they exit the viewport;
117
+ * a zero-magnitude ray is skipped (matching the source `continue`).
118
+ *
119
+ * @since 1.3
120
+ * @stable
121
+ * @example
122
+ * declare const s: GannFanState;
123
+ * declare const v: Viewport;
124
+ * const prims = decomposeGannFan(s, v);
125
+ * void prims;
126
+ */
127
+ export function decomposeGannFan(state, view) {
128
+ const pivot = worldPointToPixel(state.anchors[0], view);
129
+ const ref = worldPointToPixel(state.anchors[1], view);
130
+ const dx = ref.x - pivot.x;
131
+ const dy = ref.y - pivot.y;
132
+ const color = state.style.color ?? DEFAULT_COLOR;
133
+ const width = state.style.lineWidth ?? DEFAULT_LINE_WIDTH;
134
+ const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;
135
+ const out = [];
136
+ for (const ratio of GANN_FAN_RATIOS) {
137
+ const rx = dx;
138
+ const ry = ratio * dy;
139
+ const mag = Math.hypot(rx, ry);
140
+ if (mag === 0)
141
+ continue;
142
+ const ux = rx / mag;
143
+ const uy = ry / mag;
144
+ out.push({
145
+ kind: "polyline",
146
+ points: [pivot, { x: pivot.x + ux * rayLength, y: pivot.y + uy * rayLength }],
147
+ closed: false,
148
+ stroke: { color, width, dash: SOLID_DASH },
149
+ });
150
+ }
151
+ return out;
152
+ }
153
+ //# sourceMappingURL=gann.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gann.js","sourceRoot":"","sources":["../../../src/geometry/kinds/gann.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// Gann geometry moved from the canvas2d adapter's per-kind renderers\n// examples/canvas2d-adapter/src/render/draw/{gannBox,gannSquareFixed,\n// gannSquare,gannFan}.ts.\n// The originating math is invinite's gann-box / gann-square /\n// gann-fan tools (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02,\n// © Invinite); re-licensed MIT for chartlang.\n\nimport type {\n GannBoxState,\n GannFanState,\n GannSquareFixedState,\n GannSquareState,\n} from \"@invinite-org/chartlang-core\";\n\nimport { SOLID_DASH } from \"../_lib/dash.js\";\nimport { GANN_FAN_RATIOS, GANN_LEVELS } from \"../_lib/gannLevels.js\";\nimport { worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Viewport } from \"../types.js\";\n\nconst DEFAULT_COLOR = \"#a855f7\";\nconst DEFAULT_LINE_WIDTH = 1;\nconst SIDE_PX = 80;\n\n/**\n * Shared subdivision-grid builder for the three Gann box kinds — one\n * horizontal + one vertical polyline at every {@link GANN_LEVELS} ratio\n * across the `[left, right] × [top, bottom]` rectangle.\n */\nfunction gridPolylines(\n left: number,\n right: number,\n top: number,\n bottom: number,\n color: string,\n width: number,\n): DrawPrimitive[] {\n const stroke = { color, width, dash: SOLID_DASH };\n const out: DrawPrimitive[] = [];\n for (const level of GANN_LEVELS) {\n const y = top + level * (bottom - top);\n out.push({\n kind: \"polyline\",\n points: [\n { x: left, y },\n { x: right, y },\n ],\n closed: false,\n stroke,\n });\n }\n for (const level of GANN_LEVELS) {\n const x = left + level * (right - left);\n out.push({\n kind: \"polyline\",\n points: [\n { x, y: top },\n { x, y: bottom },\n ],\n closed: false,\n stroke,\n });\n }\n return out;\n}\n\n/**\n * Decompose a `gann-box` drawing — a ratio grid spanning the bounding\n * rectangle of the two world anchors, one horizontal + one vertical\n * line at each {@link GANN_LEVELS} entry (5 + 5 = 10 polylines for the\n * default 1/4 subdivisions, including the outer rectangle at 0 and 1.0).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: GannBoxState;\n * declare const v: Viewport;\n * const prims = decomposeGannBox(s, v);\n * void prims;\n */\nexport function decomposeGannBox(\n state: GannBoxState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n return gridPolylines(\n Math.min(a.x, b.x),\n Math.max(a.x, b.x),\n Math.min(a.y, b.y),\n Math.max(a.y, b.y),\n state.style.color ?? DEFAULT_COLOR,\n state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n );\n}\n\n/**\n * Decompose a `gann-square-fixed` drawing — an `80×80` pixel square\n * anchored at `anchor`, subdivided by {@link GANN_LEVELS}. The fixed\n * pixel side mirrors the canvas2d source's deterministic constant.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: GannSquareFixedState;\n * declare const v: Viewport;\n * const prims = decomposeGannSquareFixed(s, v);\n * void prims;\n */\nexport function decomposeGannSquareFixed(\n state: GannSquareFixedState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const origin = worldPointToPixel(state.anchor, view);\n return gridPolylines(\n origin.x,\n origin.x + SIDE_PX,\n origin.y,\n origin.y + SIDE_PX,\n state.style.color ?? DEFAULT_COLOR,\n state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n );\n}\n\n/**\n * Decompose a `gann-square` drawing — a square anchored at `anchors[0]`\n * with side `max(|dx|, |dy|)` in pixel space (the Gann-1×1 default),\n * extended in the direction of `anchors[1]`. Subdivisions follow\n * {@link GANN_LEVELS}.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: GannSquareState;\n * declare const v: Viewport;\n * const prims = decomposeGannSquare(s, v);\n * void prims;\n */\nexport function decomposeGannSquare(\n state: GannSquareState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const side = Math.max(Math.abs(b.x - a.x), Math.abs(b.y - a.y));\n const signX = b.x >= a.x ? 1 : -1;\n const signY = b.y >= a.y ? 1 : -1;\n const left = signX === 1 ? a.x : a.x - side;\n const right = signX === 1 ? a.x + side : a.x;\n const top = signY === 1 ? a.y : a.y - side;\n const bottom = signY === 1 ? a.y + side : a.y;\n return gridPolylines(\n left,\n right,\n top,\n bottom,\n state.style.color ?? DEFAULT_COLOR,\n state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n );\n}\n\n/**\n * Decompose a `gann-fan` drawing — 9 rays from `anchors[0]`, each with\n * direction `(dx, ratio · dy)` where `(dx, dy)` is the `anchors[0] →\n * anchors[1]` vector and `ratio` cycles through {@link GANN_FAN_RATIOS}.\n * Rays extend to `max(pxWidth, pxHeight) · 2` so they exit the viewport;\n * a zero-magnitude ray is skipped (matching the source `continue`).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: GannFanState;\n * declare const v: Viewport;\n * const prims = decomposeGannFan(s, v);\n * void prims;\n */\nexport function decomposeGannFan(\n state: GannFanState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const pivot = worldPointToPixel(state.anchors[0], view);\n const ref = worldPointToPixel(state.anchors[1], view);\n const dx = ref.x - pivot.x;\n const dy = ref.y - pivot.y;\n const color = state.style.color ?? DEFAULT_COLOR;\n const width = state.style.lineWidth ?? DEFAULT_LINE_WIDTH;\n const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;\n const out: DrawPrimitive[] = [];\n for (const ratio of GANN_FAN_RATIOS) {\n const rx = dx;\n const ry = ratio * dy;\n const mag = Math.hypot(rx, ry);\n if (mag === 0) continue;\n const ux = rx / mag;\n const uy = ry / 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, dash: SOLID_DASH },\n });\n }\n return out;\n}\n"]}
@@ -0,0 +1,90 @@
1
+ import type { CrossLineState, HorizontalLineState, HorizontalRayState, LineState, TrendAngleState, VerticalLineState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose a `line` drawing — a single segment, optionally extended to
5
+ * the viewport edges via {@link extendLineSegment} (the `ray` /
6
+ * `extended-line` collapse).
7
+ *
8
+ * @since 1.3
9
+ * @stable
10
+ * @example
11
+ * declare const s: LineState;
12
+ * declare const v: Viewport;
13
+ * const prims = decomposeLine(s, v);
14
+ * // prims[0].kind === "polyline"
15
+ * void prims;
16
+ */
17
+ export declare function decomposeLine(state: LineState, view: Viewport): ReadonlyArray<DrawPrimitive>;
18
+ /**
19
+ * Decompose a `horizontal-line` drawing — a segment from `x = 0` to
20
+ * `x = view.pxWidth` at `priceToY(state.price)`.
21
+ *
22
+ * @since 1.3
23
+ * @stable
24
+ * @example
25
+ * declare const s: HorizontalLineState;
26
+ * declare const v: Viewport;
27
+ * const prims = decomposeHorizontalLine(s, v);
28
+ * void prims;
29
+ */
30
+ export declare function decomposeHorizontalLine(state: HorizontalLineState, view: Viewport): ReadonlyArray<DrawPrimitive>;
31
+ /**
32
+ * Decompose a `horizontal-ray` drawing — a segment from the projected
33
+ * anchor across the right edge at constant y.
34
+ *
35
+ * @since 1.3
36
+ * @stable
37
+ * @example
38
+ * declare const s: HorizontalRayState;
39
+ * declare const v: Viewport;
40
+ * const prims = decomposeHorizontalRay(s, v);
41
+ * void prims;
42
+ */
43
+ export declare function decomposeHorizontalRay(state: HorizontalRayState, view: Viewport): ReadonlyArray<DrawPrimitive>;
44
+ /**
45
+ * Decompose a `vertical-line` drawing — a segment from `y = 0` to
46
+ * `y = view.pxHeight` at `timeToX(state.time)`.
47
+ *
48
+ * @since 1.3
49
+ * @stable
50
+ * @example
51
+ * declare const s: VerticalLineState;
52
+ * declare const v: Viewport;
53
+ * const prims = decomposeVerticalLine(s, v);
54
+ * void prims;
55
+ */
56
+ export declare function decomposeVerticalLine(state: VerticalLineState, view: Viewport): ReadonlyArray<DrawPrimitive>;
57
+ /**
58
+ * Decompose a `cross-line` drawing — a horizontal segment and a
59
+ * vertical segment crossing at the projected anchor. Two polylines
60
+ * sharing one stroke style.
61
+ *
62
+ * @since 1.3
63
+ * @stable
64
+ * @example
65
+ * declare const s: CrossLineState;
66
+ * declare const v: Viewport;
67
+ * const prims = decomposeCrossLine(s, v);
68
+ * // prims.length === 2
69
+ * void prims;
70
+ */
71
+ export declare function decomposeCrossLine(state: CrossLineState, view: Viewport): ReadonlyArray<DrawPrimitive>;
72
+ /**
73
+ * Decompose a `trend-angle` drawing — the main segment, a small arc
74
+ * spanning the screen-space angle off the first anchor, and the angle
75
+ * label in degrees. The arc is solid regardless of `lineStyle` so it
76
+ * reads cleanly; the angle is measured in screen-pixel space with the
77
+ * canvas y-axis flipped (`-dy`) so a positive angle reads "upward to
78
+ * the right" (matching the source renderer's convention).
79
+ *
80
+ * @since 1.3
81
+ * @stable
82
+ * @example
83
+ * declare const s: TrendAngleState;
84
+ * declare const v: Viewport;
85
+ * const prims = decomposeTrendAngle(s, v);
86
+ * // prims[0].kind === "polyline"; prims[1].kind === "arc"; prims[2].kind === "text"
87
+ * void prims;
88
+ */
89
+ export declare function decomposeTrendAngle(state: TrendAngleState, view: Viewport): ReadonlyArray<DrawPrimitive>;
90
+ //# sourceMappingURL=lines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/lines.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACR,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,EACT,eAAe,EACf,iBAAiB,EACpB,MAAM,8BAA8B,CAAC;AAMtC,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAO3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC,aAAa,CAAC,CAU5F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAa9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAClC,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAU9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACjC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAa9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAuB9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CA8B9B"}
@@ -0,0 +1,201 @@
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 + extension geometry moved from the canvas2d adapter's
5
+ // per-kind line renderers
6
+ // examples/canvas2d-adapter/src/render/draw/{line,horizontalLine,
7
+ // horizontalRay,verticalLine,crossLine,trendAngle}.ts.
8
+ // The originating math is invinite's line / ray / extended-line /
9
+ // horizontal-line / horizontal-ray / vertical-line / cross-line /
10
+ // trend-angle tools (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02,
11
+ // © Invinite); re-licensed MIT for chartlang.
12
+ import { dashPattern } from "../_lib/dash.js";
13
+ import { extendLineSegment } from "../_lib/lineExtend.js";
14
+ import { strokeOf } from "../_lib/strokeStyle.js";
15
+ import { priceToY, timeToX, worldPointToPixel } from "../project.js";
16
+ const DEFAULT_COLOR = "#000000";
17
+ const ANGLE_ARC_RADIUS_PX = 24;
18
+ const ANGLE_TEXT_FONT = "12px sans-serif";
19
+ const ANGLE_TEXT_OFFSET_PX = 6;
20
+ /**
21
+ * Decompose a `line` drawing — a single segment, optionally extended to
22
+ * the viewport edges via {@link extendLineSegment} (the `ray` /
23
+ * `extended-line` collapse).
24
+ *
25
+ * @since 1.3
26
+ * @stable
27
+ * @example
28
+ * declare const s: LineState;
29
+ * declare const v: Viewport;
30
+ * const prims = decomposeLine(s, v);
31
+ * // prims[0].kind === "polyline"
32
+ * void prims;
33
+ */
34
+ export function decomposeLine(state, view) {
35
+ const a = worldPointToPixel(state.anchors[0], view);
36
+ const b = worldPointToPixel(state.anchors[1], view);
37
+ const { from, to } = extendLineSegment(a, b, { extendLeft: state.style.extendLeft, extendRight: state.style.extendRight }, view);
38
+ return [{ kind: "polyline", points: [from, to], closed: false, stroke: strokeOf(state.style) }];
39
+ }
40
+ /**
41
+ * Decompose a `horizontal-line` drawing — a segment from `x = 0` to
42
+ * `x = view.pxWidth` at `priceToY(state.price)`.
43
+ *
44
+ * @since 1.3
45
+ * @stable
46
+ * @example
47
+ * declare const s: HorizontalLineState;
48
+ * declare const v: Viewport;
49
+ * const prims = decomposeHorizontalLine(s, v);
50
+ * void prims;
51
+ */
52
+ export function decomposeHorizontalLine(state, view) {
53
+ const y = priceToY(state.price, view);
54
+ return [
55
+ {
56
+ kind: "polyline",
57
+ points: [
58
+ { x: 0, y },
59
+ { x: view.pxWidth, y },
60
+ ],
61
+ closed: false,
62
+ stroke: strokeOf(state.style),
63
+ },
64
+ ];
65
+ }
66
+ /**
67
+ * Decompose a `horizontal-ray` drawing — a segment from the projected
68
+ * anchor across the right edge at constant y.
69
+ *
70
+ * @since 1.3
71
+ * @stable
72
+ * @example
73
+ * declare const s: HorizontalRayState;
74
+ * declare const v: Viewport;
75
+ * const prims = decomposeHorizontalRay(s, v);
76
+ * void prims;
77
+ */
78
+ export function decomposeHorizontalRay(state, view) {
79
+ const origin = worldPointToPixel(state.anchor, view);
80
+ return [
81
+ {
82
+ kind: "polyline",
83
+ points: [origin, { x: view.pxWidth, y: origin.y }],
84
+ closed: false,
85
+ stroke: strokeOf(state.style),
86
+ },
87
+ ];
88
+ }
89
+ /**
90
+ * Decompose a `vertical-line` drawing — a segment from `y = 0` to
91
+ * `y = view.pxHeight` at `timeToX(state.time)`.
92
+ *
93
+ * @since 1.3
94
+ * @stable
95
+ * @example
96
+ * declare const s: VerticalLineState;
97
+ * declare const v: Viewport;
98
+ * const prims = decomposeVerticalLine(s, v);
99
+ * void prims;
100
+ */
101
+ export function decomposeVerticalLine(state, view) {
102
+ const x = timeToX(state.time, view);
103
+ return [
104
+ {
105
+ kind: "polyline",
106
+ points: [
107
+ { x, y: 0 },
108
+ { x, y: view.pxHeight },
109
+ ],
110
+ closed: false,
111
+ stroke: strokeOf(state.style),
112
+ },
113
+ ];
114
+ }
115
+ /**
116
+ * Decompose a `cross-line` drawing — a horizontal segment and a
117
+ * vertical segment crossing at the projected anchor. Two polylines
118
+ * sharing one stroke style.
119
+ *
120
+ * @since 1.3
121
+ * @stable
122
+ * @example
123
+ * declare const s: CrossLineState;
124
+ * declare const v: Viewport;
125
+ * const prims = decomposeCrossLine(s, v);
126
+ * // prims.length === 2
127
+ * void prims;
128
+ */
129
+ export function decomposeCrossLine(state, view) {
130
+ const p = worldPointToPixel(state.anchor, view);
131
+ const stroke = strokeOf(state.style);
132
+ return [
133
+ {
134
+ kind: "polyline",
135
+ points: [
136
+ { x: 0, y: p.y },
137
+ { x: view.pxWidth, y: p.y },
138
+ ],
139
+ closed: false,
140
+ stroke,
141
+ },
142
+ {
143
+ kind: "polyline",
144
+ points: [
145
+ { x: p.x, y: 0 },
146
+ { x: p.x, y: view.pxHeight },
147
+ ],
148
+ closed: false,
149
+ stroke,
150
+ },
151
+ ];
152
+ }
153
+ /**
154
+ * Decompose a `trend-angle` drawing — the main segment, a small arc
155
+ * spanning the screen-space angle off the first anchor, and the angle
156
+ * label in degrees. The arc is solid regardless of `lineStyle` so it
157
+ * reads cleanly; the angle is measured in screen-pixel space with the
158
+ * canvas y-axis flipped (`-dy`) so a positive angle reads "upward to
159
+ * the right" (matching the source renderer's convention).
160
+ *
161
+ * @since 1.3
162
+ * @stable
163
+ * @example
164
+ * declare const s: TrendAngleState;
165
+ * declare const v: Viewport;
166
+ * const prims = decomposeTrendAngle(s, v);
167
+ * // prims[0].kind === "polyline"; prims[1].kind === "arc"; prims[2].kind === "text"
168
+ * void prims;
169
+ */
170
+ export function decomposeTrendAngle(state, view) {
171
+ const a = worldPointToPixel(state.anchors[0], view);
172
+ const b = worldPointToPixel(state.anchors[1], view);
173
+ const color = state.style.color ?? DEFAULT_COLOR;
174
+ const stroke = strokeOf(state.style);
175
+ const angleRad = Math.atan2(-(b.y - a.y), b.x - a.x);
176
+ const degrees = (angleRad * 180) / Math.PI;
177
+ return [
178
+ { kind: "polyline", points: [a, b], closed: false, stroke },
179
+ {
180
+ kind: "arc",
181
+ cx: a.x,
182
+ cy: a.y,
183
+ r: ANGLE_ARC_RADIUS_PX,
184
+ start: -angleRad,
185
+ end: 0,
186
+ closed: false,
187
+ stroke: { color, width: stroke.width, dash: dashPattern("solid") },
188
+ },
189
+ {
190
+ kind: "text",
191
+ x: a.x + ANGLE_ARC_RADIUS_PX + ANGLE_TEXT_OFFSET_PX,
192
+ y: a.y,
193
+ text: `${degrees.toFixed(1)}°`,
194
+ color,
195
+ font: ANGLE_TEXT_FONT,
196
+ align: "left",
197
+ baseline: "middle",
198
+ },
199
+ ];
200
+ }
201
+ //# sourceMappingURL=lines.js.map