@invinite-org/chartlang-adapter-kit 1.3.0 → 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 (143) hide show
  1. package/CHANGELOG.md +52 -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 +2 -0
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +1 -0
  142. package/dist/index.js.map +1 -1
  143. package/package.json +8 -1
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Canonical Fibonacci level ratios shared by every fib decomposer
3
+ * (`fibRetracement`, `fibTrendExtension`, `fibChannel`, `fibSpeedFan`,
4
+ * `fibSpeedArcs`, …). Frozen so per-kind decomposers cannot accidentally
5
+ * mutate the shared array. Source: invinite's
6
+ * `src/components/trading-chart/tools/fib-*` tools.
7
+ *
8
+ * Order is monotonic. Both the 0 and 1 endpoints are included so a
9
+ * retracement decomposer that needs the bracket lines (the 0% and 100%
10
+ * anchor levels) doesn't have to splice them in.
11
+ *
12
+ * @since 1.3
13
+ * @stable
14
+ * @example
15
+ * for (const level of FIB_LEVELS) {
16
+ * // Stroke a horizontal line at `from.price + level * (to.price - from.price)`.
17
+ * void level;
18
+ * }
19
+ */
20
+ export declare const FIB_LEVELS: ReadonlyArray<number>;
21
+ /**
22
+ * Format a Fibonacci ratio for display alongside its level line.
23
+ * Integer ratios (`0`, `1`, `2`) render with one decimal place (`"0.0"`,
24
+ * `"1.0"`, `"2.0"`); fractional ratios render with three (`"0.236"`,
25
+ * `"0.618"`, `"1.272"`). Matches the Pine-style label convention used
26
+ * by the invinite fib tools.
27
+ *
28
+ * @since 1.3
29
+ * @stable
30
+ * @example
31
+ * formatLevel(0); // "0.0"
32
+ * formatLevel(0.618); // "0.618"
33
+ * formatLevel(1.272); // "1.272"
34
+ */
35
+ export declare function formatLevel(level: number): string;
36
+ //# sourceMappingURL=fibLevels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fibLevels.d.ts","sourceRoot":"","sources":["../../../src/geometry/_lib/fibLevels.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,MAAM,CAI3C,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,44 @@
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
+ * Canonical Fibonacci level ratios shared by every fib decomposer
5
+ * (`fibRetracement`, `fibTrendExtension`, `fibChannel`, `fibSpeedFan`,
6
+ * `fibSpeedArcs`, …). Frozen so per-kind decomposers cannot accidentally
7
+ * mutate the shared array. Source: invinite's
8
+ * `src/components/trading-chart/tools/fib-*` tools.
9
+ *
10
+ * Order is monotonic. Both the 0 and 1 endpoints are included so a
11
+ * retracement decomposer that needs the bracket lines (the 0% and 100%
12
+ * anchor levels) doesn't have to splice them in.
13
+ *
14
+ * @since 1.3
15
+ * @stable
16
+ * @example
17
+ * for (const level of FIB_LEVELS) {
18
+ * // Stroke a horizontal line at `from.price + level * (to.price - from.price)`.
19
+ * void level;
20
+ * }
21
+ */
22
+ export const FIB_LEVELS = Object.freeze([
23
+ 0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.272,
24
+ // biome-ignore lint/suspicious/noApproximativeNumericConstant: canonical fib ratio, not √2.
25
+ 1.414, 1.618, 2.0, 2.618, 4.236,
26
+ ]);
27
+ /**
28
+ * Format a Fibonacci ratio for display alongside its level line.
29
+ * Integer ratios (`0`, `1`, `2`) render with one decimal place (`"0.0"`,
30
+ * `"1.0"`, `"2.0"`); fractional ratios render with three (`"0.236"`,
31
+ * `"0.618"`, `"1.272"`). Matches the Pine-style label convention used
32
+ * by the invinite fib tools.
33
+ *
34
+ * @since 1.3
35
+ * @stable
36
+ * @example
37
+ * formatLevel(0); // "0.0"
38
+ * formatLevel(0.618); // "0.618"
39
+ * formatLevel(1.272); // "1.272"
40
+ */
41
+ export function formatLevel(level) {
42
+ return level === Math.floor(level) ? level.toFixed(1) : level.toFixed(3);
43
+ }
44
+ //# sourceMappingURL=fibLevels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fibLevels.js","sourceRoot":"","sources":["../../../src/geometry/_lib/fibLevels.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,UAAU,GAA0B,MAAM,CAAC,MAAM,CAAC;IAC3D,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK;IAC5C,4FAA4F;IAC5F,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK;CAClC,CAAC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACrC,OAAO,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC7E,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/**\n * Canonical Fibonacci level ratios shared by every fib decomposer\n * (`fibRetracement`, `fibTrendExtension`, `fibChannel`, `fibSpeedFan`,\n * `fibSpeedArcs`, …). Frozen so per-kind decomposers cannot accidentally\n * mutate the shared array. Source: invinite's\n * `src/components/trading-chart/tools/fib-*` tools.\n *\n * Order is monotonic. Both the 0 and 1 endpoints are included so a\n * retracement decomposer that needs the bracket lines (the 0% and 100%\n * anchor levels) doesn't have to splice them in.\n *\n * @since 1.3\n * @stable\n * @example\n * for (const level of FIB_LEVELS) {\n * // Stroke a horizontal line at `from.price + level * (to.price - from.price)`.\n * void level;\n * }\n */\nexport const FIB_LEVELS: ReadonlyArray<number> = Object.freeze([\n 0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.272,\n // biome-ignore lint/suspicious/noApproximativeNumericConstant: canonical fib ratio, not √2.\n 1.414, 1.618, 2.0, 2.618, 4.236,\n]);\n\n/**\n * Format a Fibonacci ratio for display alongside its level line.\n * Integer ratios (`0`, `1`, `2`) render with one decimal place (`\"0.0\"`,\n * `\"1.0\"`, `\"2.0\"`); fractional ratios render with three (`\"0.236\"`,\n * `\"0.618\"`, `\"1.272\"`). Matches the Pine-style label convention used\n * by the invinite fib tools.\n *\n * @since 1.3\n * @stable\n * @example\n * formatLevel(0); // \"0.0\"\n * formatLevel(0.618); // \"0.618\"\n * formatLevel(1.272); // \"1.272\"\n */\nexport function formatLevel(level: number): string {\n return level === Math.floor(level) ? level.toFixed(1) : level.toFixed(3);\n}\n"]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Canonical Gann box subdivision ratios. Each box decomposer emits one
3
+ * horizontal + one vertical stroke at each ratio (including 0 and 1.0
4
+ * for the outer rectangle). The 1/4 subdivisions match the upstream
5
+ * invinite default and are the pinned Phase-3 wire shape.
6
+ *
7
+ * @since 1.3
8
+ * @stable
9
+ * @example
10
+ * import { GANN_LEVELS } from "./gannLevels.js";
11
+ * for (const level of GANN_LEVELS) void level;
12
+ */
13
+ export declare const GANN_LEVELS: ReadonlyArray<number>;
14
+ /**
15
+ * Canonical Gann fan slope ratios. Each entry is the slope multiplier
16
+ * applied to the (a→b) direction vector — the 1×1 ray points at b, 1×2
17
+ * doubles the slope, 2×1 halves it, etc. The 9-entry tuple matches the
18
+ * upstream invinite default `FibGannLevel` set.
19
+ *
20
+ * @since 1.3
21
+ * @stable
22
+ * @example
23
+ * import { GANN_FAN_RATIOS } from "./gannLevels.js";
24
+ * for (const r of GANN_FAN_RATIOS) void r;
25
+ */
26
+ export declare const GANN_FAN_RATIOS: ReadonlyArray<number>;
27
+ /**
28
+ * Human-readable label for each {@link GANN_FAN_RATIOS} entry. Order
29
+ * matches the ratios array so a decomposer can co-index them. Ratios
30
+ * greater than 1 render as `"1x<n>"`; ratios less than 1 render as
31
+ * `"<n>x1"`; 1×1 is the identity.
32
+ *
33
+ * @since 1.3
34
+ * @stable
35
+ * @example
36
+ * import { GANN_FAN_LABELS } from "./gannLevels.js";
37
+ * for (const label of GANN_FAN_LABELS) void label;
38
+ */
39
+ export declare const GANN_FAN_LABELS: ReadonlyArray<string>;
40
+ /**
41
+ * Map a fan ratio to its kebab label. Ratios `>= 1` render as
42
+ * `"1x<n>"`; ratios `< 1` render as `"<n>x1"` where `n = round(1 /
43
+ * ratio)`. Used when fan labels are enabled.
44
+ *
45
+ * @since 1.3
46
+ * @stable
47
+ * @example
48
+ * import { formatGannRatio } from "./gannLevels.js";
49
+ * formatGannRatio(1); // "1x1"
50
+ * formatGannRatio(2); // "1x2"
51
+ * formatGannRatio(0.5); // "2x1"
52
+ */
53
+ export declare function formatGannRatio(ratio: number): string;
54
+ //# sourceMappingURL=gannLevels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gannLevels.d.ts","sourceRoot":"","sources":["../../../src/geometry/_lib/gannLevels.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,MAAM,CAA0C,CAAC;AAEzF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,MAAM,CAUhD,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,MAAM,CAUhD,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD"}
@@ -0,0 +1,88 @@
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 box subdivision + fan-slope tables moved verbatim from the
5
+ // canvas2d adapter's renderer helper
6
+ // examples/canvas2d-adapter/src/render/draw/gannLevels.ts.
7
+ // The originating subdivision + slope conventions are invinite's
8
+ // gann-box / gann-fan tools (commit
9
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
10
+ // MIT for chartlang.
11
+ /**
12
+ * Canonical Gann box subdivision ratios. Each box decomposer emits one
13
+ * horizontal + one vertical stroke at each ratio (including 0 and 1.0
14
+ * for the outer rectangle). The 1/4 subdivisions match the upstream
15
+ * invinite default and are the pinned Phase-3 wire shape.
16
+ *
17
+ * @since 1.3
18
+ * @stable
19
+ * @example
20
+ * import { GANN_LEVELS } from "./gannLevels.js";
21
+ * for (const level of GANN_LEVELS) void level;
22
+ */
23
+ export const GANN_LEVELS = Object.freeze([0, 0.25, 0.5, 0.75, 1]);
24
+ /**
25
+ * Canonical Gann fan slope ratios. Each entry is the slope multiplier
26
+ * applied to the (a→b) direction vector — the 1×1 ray points at b, 1×2
27
+ * doubles the slope, 2×1 halves it, etc. The 9-entry tuple matches the
28
+ * upstream invinite default `FibGannLevel` set.
29
+ *
30
+ * @since 1.3
31
+ * @stable
32
+ * @example
33
+ * import { GANN_FAN_RATIOS } from "./gannLevels.js";
34
+ * for (const r of GANN_FAN_RATIOS) void r;
35
+ */
36
+ export const GANN_FAN_RATIOS = Object.freeze([
37
+ 1,
38
+ 2,
39
+ 3,
40
+ 0.5,
41
+ 1 / 3,
42
+ 4,
43
+ 0.25,
44
+ 8,
45
+ 0.125,
46
+ ]);
47
+ /**
48
+ * Human-readable label for each {@link GANN_FAN_RATIOS} entry. Order
49
+ * matches the ratios array so a decomposer can co-index them. Ratios
50
+ * greater than 1 render as `"1x<n>"`; ratios less than 1 render as
51
+ * `"<n>x1"`; 1×1 is the identity.
52
+ *
53
+ * @since 1.3
54
+ * @stable
55
+ * @example
56
+ * import { GANN_FAN_LABELS } from "./gannLevels.js";
57
+ * for (const label of GANN_FAN_LABELS) void label;
58
+ */
59
+ export const GANN_FAN_LABELS = Object.freeze([
60
+ "1x1",
61
+ "1x2",
62
+ "1x3",
63
+ "2x1",
64
+ "3x1",
65
+ "1x4",
66
+ "4x1",
67
+ "1x8",
68
+ "8x1",
69
+ ]);
70
+ /**
71
+ * Map a fan ratio to its kebab label. Ratios `>= 1` render as
72
+ * `"1x<n>"`; ratios `< 1` render as `"<n>x1"` where `n = round(1 /
73
+ * ratio)`. Used when fan labels are enabled.
74
+ *
75
+ * @since 1.3
76
+ * @stable
77
+ * @example
78
+ * import { formatGannRatio } from "./gannLevels.js";
79
+ * formatGannRatio(1); // "1x1"
80
+ * formatGannRatio(2); // "1x2"
81
+ * formatGannRatio(0.5); // "2x1"
82
+ */
83
+ export function formatGannRatio(ratio) {
84
+ if (ratio >= 1)
85
+ return `1x${Math.round(ratio)}`;
86
+ return `${Math.round(1 / ratio)}x1`;
87
+ }
88
+ //# sourceMappingURL=gannLevels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gannLevels.js","sourceRoot":"","sources":["../../../src/geometry/_lib/gannLevels.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,kEAAkE;AAClE,qCAAqC;AACrC,6DAA6D;AAC7D,iEAAiE;AACjE,oCAAoC;AACpC,qEAAqE;AACrE,qBAAqB;AAErB;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,WAAW,GAA0B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAEzF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,eAAe,GAA0B,MAAM,CAAC,MAAM,CAAC;IAChE,CAAC;IACD,CAAC;IACD,CAAC;IACD,GAAG;IACH,CAAC,GAAG,CAAC;IACL,CAAC;IACD,IAAI;IACJ,CAAC;IACD,KAAK;CACR,CAAC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,eAAe,GAA0B,MAAM,CAAC,MAAM,CAAC;IAChE,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;CACR,CAAC,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IACzC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;IAChD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;AACxC,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// Gann box subdivision + fan-slope tables moved verbatim from the\n// canvas2d adapter's renderer helper\n// examples/canvas2d-adapter/src/render/draw/gannLevels.ts.\n// The originating subdivision + slope conventions are invinite's\n// gann-box / gann-fan tools (commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed\n// MIT for chartlang.\n\n/**\n * Canonical Gann box subdivision ratios. Each box decomposer emits one\n * horizontal + one vertical stroke at each ratio (including 0 and 1.0\n * for the outer rectangle). The 1/4 subdivisions match the upstream\n * invinite default and are the pinned Phase-3 wire shape.\n *\n * @since 1.3\n * @stable\n * @example\n * import { GANN_LEVELS } from \"./gannLevels.js\";\n * for (const level of GANN_LEVELS) void level;\n */\nexport const GANN_LEVELS: ReadonlyArray<number> = Object.freeze([0, 0.25, 0.5, 0.75, 1]);\n\n/**\n * Canonical Gann fan slope ratios. Each entry is the slope multiplier\n * applied to the (a→b) direction vector — the 1×1 ray points at b, 1×2\n * doubles the slope, 2×1 halves it, etc. The 9-entry tuple matches the\n * upstream invinite default `FibGannLevel` set.\n *\n * @since 1.3\n * @stable\n * @example\n * import { GANN_FAN_RATIOS } from \"./gannLevels.js\";\n * for (const r of GANN_FAN_RATIOS) void r;\n */\nexport const GANN_FAN_RATIOS: ReadonlyArray<number> = Object.freeze([\n 1,\n 2,\n 3,\n 0.5,\n 1 / 3,\n 4,\n 0.25,\n 8,\n 0.125,\n]);\n\n/**\n * Human-readable label for each {@link GANN_FAN_RATIOS} entry. Order\n * matches the ratios array so a decomposer can co-index them. Ratios\n * greater than 1 render as `\"1x<n>\"`; ratios less than 1 render as\n * `\"<n>x1\"`; 1×1 is the identity.\n *\n * @since 1.3\n * @stable\n * @example\n * import { GANN_FAN_LABELS } from \"./gannLevels.js\";\n * for (const label of GANN_FAN_LABELS) void label;\n */\nexport const GANN_FAN_LABELS: ReadonlyArray<string> = Object.freeze([\n \"1x1\",\n \"1x2\",\n \"1x3\",\n \"2x1\",\n \"3x1\",\n \"1x4\",\n \"4x1\",\n \"1x8\",\n \"8x1\",\n]);\n\n/**\n * Map a fan ratio to its kebab label. Ratios `>= 1` render as\n * `\"1x<n>\"`; ratios `< 1` render as `\"<n>x1\"` where `n = round(1 /\n * ratio)`. Used when fan labels are enabled.\n *\n * @since 1.3\n * @stable\n * @example\n * import { formatGannRatio } from \"./gannLevels.js\";\n * formatGannRatio(1); // \"1x1\"\n * formatGannRatio(2); // \"1x2\"\n * formatGannRatio(0.5); // \"2x1\"\n */\nexport function formatGannRatio(ratio: number): string {\n if (ratio >= 1) return `1x${Math.round(ratio)}`;\n return `${Math.round(1 / ratio)}x1`;\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import type { Point2, Viewport } from "../types.js";
2
+ /**
3
+ * Project the segment `(a, b)` to the viewport edges in the directions
4
+ * `opts.extendLeft` / `opts.extendRight`. Used by the `line`
5
+ * decomposer (when its `LineDrawStyle` flags are set) and by
6
+ * `horizontal-ray` (always extends right).
7
+ *
8
+ * The projection walks the parametric line `p(t) = a + t·(b − a)` and
9
+ * solves for the `t` that hits `x = 0` (left edge) or
10
+ * `x = view.pxWidth` (right edge). A purely vertical segment
11
+ * (`dx === 0`) cannot intersect the x-edges, so the segment is returned
12
+ * unchanged (the stroke clips at the viewport boundary).
13
+ *
14
+ * @since 1.3
15
+ * @stable
16
+ * @example
17
+ * declare const view: Viewport;
18
+ * const a: Point2 = { x: 100, y: 100 };
19
+ * const b: Point2 = { x: 200, y: 200 };
20
+ * const seg = extendLineSegment(a, b, { extendRight: true }, view);
21
+ * // seg.from === a; seg.to.x === view.pxWidth
22
+ * void seg;
23
+ */
24
+ export declare function extendLineSegment(a: Point2, b: Point2, opts: {
25
+ readonly extendLeft?: boolean | undefined;
26
+ readonly extendRight?: boolean | undefined;
27
+ }, view: Viewport): {
28
+ readonly from: Point2;
29
+ readonly to: Point2;
30
+ };
31
+ //# sourceMappingURL=lineExtend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lineExtend.d.ts","sourceRoot":"","sources":["../../../src/geometry/_lib/lineExtend.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAC7B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,IAAI,EAAE;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,EAC/F,IAAI,EAAE,QAAQ,GACf;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAkBhD"}
@@ -0,0 +1,48 @@
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
+ // Geometry ported from
5
+ // invinite/src/components/trading-chart/tools/lib/geometry.ts
6
+ // (`extendSegmentPx`), commit
7
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite.
8
+ // Re-licensed MIT for chartlang.
9
+ /**
10
+ * Project the segment `(a, b)` to the viewport edges in the directions
11
+ * `opts.extendLeft` / `opts.extendRight`. Used by the `line`
12
+ * decomposer (when its `LineDrawStyle` flags are set) and by
13
+ * `horizontal-ray` (always extends right).
14
+ *
15
+ * The projection walks the parametric line `p(t) = a + t·(b − a)` and
16
+ * solves for the `t` that hits `x = 0` (left edge) or
17
+ * `x = view.pxWidth` (right edge). A purely vertical segment
18
+ * (`dx === 0`) cannot intersect the x-edges, so the segment is returned
19
+ * unchanged (the stroke clips at the viewport boundary).
20
+ *
21
+ * @since 1.3
22
+ * @stable
23
+ * @example
24
+ * declare const view: Viewport;
25
+ * const a: Point2 = { x: 100, y: 100 };
26
+ * const b: Point2 = { x: 200, y: 200 };
27
+ * const seg = extendLineSegment(a, b, { extendRight: true }, view);
28
+ * // seg.from === a; seg.to.x === view.pxWidth
29
+ * void seg;
30
+ */
31
+ export function extendLineSegment(a, b, opts, view) {
32
+ const dx = b.x - a.x;
33
+ const dy = b.y - a.y;
34
+ if (dx === 0)
35
+ return { from: a, to: b };
36
+ let from = a;
37
+ let to = b;
38
+ if (opts.extendLeft === true) {
39
+ const t = -a.x / dx;
40
+ from = { x: 0, y: a.y + t * dy };
41
+ }
42
+ if (opts.extendRight === true) {
43
+ const t = (view.pxWidth - b.x) / dx;
44
+ to = { x: view.pxWidth, y: b.y + t * dy };
45
+ }
46
+ return { from, to };
47
+ }
48
+ //# sourceMappingURL=lineExtend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lineExtend.js","sourceRoot":"","sources":["../../../src/geometry/_lib/lineExtend.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,uBAAuB;AACvB,gEAAgE;AAChE,kCAAkC;AAClC,4DAA4D;AAC5D,iCAAiC;AAIjC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,iBAAiB,CAC7B,CAAS,EACT,CAAS,EACT,IAA+F,EAC/F,IAAc;IAEd,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAExC,IAAI,IAAI,GAAW,CAAC,CAAC;IACrB,IAAI,EAAE,GAAW,CAAC,CAAC;IAEnB,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QACpB,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC;IACrC,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QACpC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC;IAC9C,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AACxB,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// Geometry ported from\n// invinite/src/components/trading-chart/tools/lib/geometry.ts\n// (`extendSegmentPx`), commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite.\n// Re-licensed MIT for chartlang.\n\nimport type { Point2, Viewport } from \"../types.js\";\n\n/**\n * Project the segment `(a, b)` to the viewport edges in the directions\n * `opts.extendLeft` / `opts.extendRight`. Used by the `line`\n * decomposer (when its `LineDrawStyle` flags are set) and by\n * `horizontal-ray` (always extends right).\n *\n * The projection walks the parametric line `p(t) = a + t·(b − a)` and\n * solves for the `t` that hits `x = 0` (left edge) or\n * `x = view.pxWidth` (right edge). A purely vertical segment\n * (`dx === 0`) cannot intersect the x-edges, so the segment is returned\n * unchanged (the stroke clips at the viewport boundary).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const view: Viewport;\n * const a: Point2 = { x: 100, y: 100 };\n * const b: Point2 = { x: 200, y: 200 };\n * const seg = extendLineSegment(a, b, { extendRight: true }, view);\n * // seg.from === a; seg.to.x === view.pxWidth\n * void seg;\n */\nexport function extendLineSegment(\n a: Point2,\n b: Point2,\n opts: { readonly extendLeft?: boolean | undefined; readonly extendRight?: boolean | undefined },\n view: Viewport,\n): { readonly from: Point2; readonly to: Point2 } {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n if (dx === 0) return { from: a, to: b };\n\n let from: Point2 = a;\n let to: Point2 = b;\n\n if (opts.extendLeft === true) {\n const t = -a.x / dx;\n from = { x: 0, y: a.y + t * dy };\n }\n if (opts.extendRight === true) {\n const t = (view.pxWidth - b.x) / dx;\n to = { x: view.pxWidth, y: b.y + t * dy };\n }\n\n return { from, to };\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import type { LineDrawStyle } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Point2 } from "../types.js";
3
+ /**
4
+ * Reduce an open labelled polyline to `DrawPrimitive[]`: one open
5
+ * `polyline` through `points` plus one `text` primitive per vertex,
6
+ * each label `LABEL_OFFSET_PX = 6 px` above its anchor with
7
+ * `align: "center"` / `baseline: "bottom"`. Consumed by the harmonic
8
+ * pattern decomposers (Tasks 2–3). Returns `[]` when `points` is empty.
9
+ *
10
+ * `points.length === labels.length` is a structural contract: every
11
+ * vertex carries exactly one label (X / A / B / C / D / S / H / …).
12
+ *
13
+ * @since 1.3
14
+ * @stable
15
+ * @example
16
+ * const prims = namedPolylinePrimitives(
17
+ * [{ x: 0, y: 0 }, { x: 10, y: 5 }],
18
+ * ["X", "A"],
19
+ * {},
20
+ * );
21
+ * // prims[0].kind === "polyline"; prims[1].kind === "text"
22
+ * void prims;
23
+ */
24
+ export declare function namedPolylinePrimitives(points: ReadonlyArray<Point2>, labels: ReadonlyArray<string>, style: LineDrawStyle): ReadonlyArray<DrawPrimitive>;
25
+ //# sourceMappingURL=namedPolyline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"namedPolyline.d.ts","sourceRoot":"","sources":["../../../src/geometry/_lib/namedPolyline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQzD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACnC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAC7B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAC7B,KAAK,EAAE,aAAa,GACrB,aAAa,CAAC,aAAa,CAAC,CAyB9B"}
@@ -0,0 +1,64 @@
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
+ // Open-polyline + per-vertex label composition mirrors invinite's
5
+ // pattern tools' `renderConnectedLegs` + `renderPointLabel` helpers:
6
+ // invinite/src/components/trading-chart/tools/xabcd-pattern-tool.ts
7
+ // invinite/src/components/trading-chart/tools/abcd-pattern-tool.ts
8
+ // (and the other 3 pattern tools), commit
9
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite.
10
+ // Re-licensed MIT for chartlang.
11
+ import { dashPattern } from "./dash.js";
12
+ const DEFAULT_COLOR = "#f59e0b";
13
+ const DEFAULT_LINE_WIDTH = 1;
14
+ const LABEL_FONT = "11px sans-serif";
15
+ const LABEL_OFFSET_PX = 6;
16
+ /**
17
+ * Reduce an open labelled polyline to `DrawPrimitive[]`: one open
18
+ * `polyline` through `points` plus one `text` primitive per vertex,
19
+ * each label `LABEL_OFFSET_PX = 6 px` above its anchor with
20
+ * `align: "center"` / `baseline: "bottom"`. Consumed by the harmonic
21
+ * pattern decomposers (Tasks 2–3). Returns `[]` when `points` is empty.
22
+ *
23
+ * `points.length === labels.length` is a structural contract: every
24
+ * vertex carries exactly one label (X / A / B / C / D / S / H / …).
25
+ *
26
+ * @since 1.3
27
+ * @stable
28
+ * @example
29
+ * const prims = namedPolylinePrimitives(
30
+ * [{ x: 0, y: 0 }, { x: 10, y: 5 }],
31
+ * ["X", "A"],
32
+ * {},
33
+ * );
34
+ * // prims[0].kind === "polyline"; prims[1].kind === "text"
35
+ * void prims;
36
+ */
37
+ export function namedPolylinePrimitives(points, labels, style) {
38
+ if (points.length === 0)
39
+ return [];
40
+ const color = style.color ?? DEFAULT_COLOR;
41
+ const lineWidth = style.lineWidth ?? DEFAULT_LINE_WIDTH;
42
+ const out = [
43
+ {
44
+ kind: "polyline",
45
+ points,
46
+ closed: false,
47
+ stroke: { color, width: lineWidth, dash: dashPattern("solid") },
48
+ },
49
+ ];
50
+ for (let i = 0; i < points.length; i++) {
51
+ out.push({
52
+ kind: "text",
53
+ x: points[i].x,
54
+ y: points[i].y - LABEL_OFFSET_PX,
55
+ text: labels[i],
56
+ color,
57
+ font: LABEL_FONT,
58
+ align: "center",
59
+ baseline: "bottom",
60
+ });
61
+ }
62
+ return out;
63
+ }
64
+ //# sourceMappingURL=namedPolyline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"namedPolyline.js","sourceRoot":"","sources":["../../../src/geometry/_lib/namedPolyline.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,kEAAkE;AAClE,qEAAqE;AACrE,sEAAsE;AACtE,qEAAqE;AACrE,4CAA4C;AAC5C,0DAA0D;AAC1D,iCAAiC;AAKjC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,aAAa,GAAG,SAAS,CAAC;AAChC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,MAAM,UAAU,GAAG,iBAAiB,CAAC;AACrC,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,uBAAuB,CACnC,MAA6B,EAC7B,MAA6B,EAC7B,KAAoB;IAEpB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,aAAa,CAAC;IAC3C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACxD,MAAM,GAAG,GAAoB;QACzB;YACI,IAAI,EAAE,UAAU;YAChB,MAAM;YACN,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE;SAClE;KACJ,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,GAAG,CAAC,IAAI,CAAC;YACL,IAAI,EAAE,MAAM;YACZ,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACd,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,eAAe;YAChC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;YACf,KAAK;YACL,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,QAAQ;YACf,QAAQ,EAAE,QAAQ;SACrB,CAAC,CAAC;IACP,CAAC;IACD,OAAO,GAAG,CAAC;AACf,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// Open-polyline + per-vertex label composition mirrors invinite's\n// pattern tools' `renderConnectedLegs` + `renderPointLabel` helpers:\n// invinite/src/components/trading-chart/tools/xabcd-pattern-tool.ts\n// invinite/src/components/trading-chart/tools/abcd-pattern-tool.ts\n// (and the other 3 pattern tools), commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite.\n// Re-licensed MIT for chartlang.\n\nimport type { LineDrawStyle } from \"@invinite-org/chartlang-core\";\n\nimport type { DrawPrimitive, Point2 } from \"../types.js\";\nimport { dashPattern } from \"./dash.js\";\n\nconst DEFAULT_COLOR = \"#f59e0b\";\nconst DEFAULT_LINE_WIDTH = 1;\nconst LABEL_FONT = \"11px sans-serif\";\nconst LABEL_OFFSET_PX = 6;\n\n/**\n * Reduce an open labelled polyline to `DrawPrimitive[]`: one open\n * `polyline` through `points` plus one `text` primitive per vertex,\n * each label `LABEL_OFFSET_PX = 6 px` above its anchor with\n * `align: \"center\"` / `baseline: \"bottom\"`. Consumed by the harmonic\n * pattern decomposers (Tasks 2–3). Returns `[]` when `points` is empty.\n *\n * `points.length === labels.length` is a structural contract: every\n * vertex carries exactly one label (X / A / B / C / D / S / H / …).\n *\n * @since 1.3\n * @stable\n * @example\n * const prims = namedPolylinePrimitives(\n * [{ x: 0, y: 0 }, { x: 10, y: 5 }],\n * [\"X\", \"A\"],\n * {},\n * );\n * // prims[0].kind === \"polyline\"; prims[1].kind === \"text\"\n * void prims;\n */\nexport function namedPolylinePrimitives(\n points: ReadonlyArray<Point2>,\n labels: ReadonlyArray<string>,\n style: LineDrawStyle,\n): ReadonlyArray<DrawPrimitive> {\n if (points.length === 0) return [];\n const color = style.color ?? DEFAULT_COLOR;\n const lineWidth = style.lineWidth ?? DEFAULT_LINE_WIDTH;\n const out: DrawPrimitive[] = [\n {\n kind: \"polyline\",\n points,\n closed: false,\n stroke: { color, width: lineWidth, dash: dashPattern(\"solid\") },\n },\n ];\n for (let i = 0; i < points.length; i++) {\n out.push({\n kind: \"text\",\n x: points[i].x,\n y: points[i].y - LABEL_OFFSET_PX,\n text: labels[i],\n color,\n font: LABEL_FONT,\n align: \"center\",\n baseline: \"bottom\",\n });\n }\n return out;\n}\n"]}
@@ -0,0 +1,46 @@
1
+ import type { PitchforkState } from "@invinite-org/chartlang-core";
2
+ import type { Point2 } from "../types.js";
3
+ type PitchforkVariant = PitchforkState["variant"];
4
+ /**
5
+ * Per-variant median-origin point — the first endpoint of the
6
+ * pitchfork's median rail in pixel space. A decomposer draws three
7
+ * lines: the median (origin → target → +extension) and two parallel
8
+ * handles through `b` and `c` offset by the same `target - origin`
9
+ * vector.
10
+ *
11
+ * Variants:
12
+ * - `standard` — origin = `a`; target = `mid(b, c)`.
13
+ * - `schiff` — origin = `(a.x, mid(a.y, midBC.y))`; target = `mid(b, c)`.
14
+ * - `modifiedSchiff` — origin = `mid(a, b)`; target = `mid(b, c)`.
15
+ * - `inside` — origin = `mid(b, c)`; target = `midBC + (c - midAB)`.
16
+ *
17
+ * @since 1.3
18
+ * @stable
19
+ * @example
20
+ * import { medianOriginFor } from "./pitchforkGeom.js";
21
+ * const origin = medianOriginFor(
22
+ * "standard",
23
+ * { x: 0, y: 0 }, { x: 10, y: 10 }, { x: 20, y: 0 },
24
+ * );
25
+ * void origin;
26
+ */
27
+ export declare function medianOriginFor(variant: PitchforkVariant, a: Point2, b: Point2, c: Point2): Point2;
28
+ /**
29
+ * Per-variant median-target point — the second endpoint of the
30
+ * pitchfork's median rail. A decomposer uses it to compute the
31
+ * extension vector `target - origin` reused for the two parallel handle
32
+ * rails.
33
+ *
34
+ * @since 1.3
35
+ * @stable
36
+ * @example
37
+ * import { medianTargetFor } from "./pitchforkGeom.js";
38
+ * const target = medianTargetFor(
39
+ * "standard",
40
+ * { x: 0, y: 0 }, { x: 10, y: 10 }, { x: 20, y: 0 },
41
+ * );
42
+ * void target;
43
+ */
44
+ export declare function medianTargetFor(variant: PitchforkVariant, a: Point2, b: Point2, c: Point2): Point2;
45
+ export {};
46
+ //# sourceMappingURL=pitchforkGeom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pitchforkGeom.d.ts","sourceRoot":"","sources":["../../../src/geometry/_lib/pitchforkGeom.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,KAAK,gBAAgB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAC3B,OAAO,EAAE,gBAAgB,EACzB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACV,MAAM,CAYR;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC3B,OAAO,EAAE,gBAAgB,EACzB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACV,MAAM,CAOR"}
@@ -0,0 +1,70 @@
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
+ // Median-origin + median-target formulas moved verbatim from the
5
+ // canvas2d adapter's renderer helper
6
+ // examples/canvas2d-adapter/src/render/draw/pitchforkGeom.ts.
7
+ // The originating math is invinite's pitchfork-geometry lib (commit
8
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
9
+ // MIT for chartlang.
10
+ /**
11
+ * Per-variant median-origin point — the first endpoint of the
12
+ * pitchfork's median rail in pixel space. A decomposer draws three
13
+ * lines: the median (origin → target → +extension) and two parallel
14
+ * handles through `b` and `c` offset by the same `target - origin`
15
+ * vector.
16
+ *
17
+ * Variants:
18
+ * - `standard` — origin = `a`; target = `mid(b, c)`.
19
+ * - `schiff` — origin = `(a.x, mid(a.y, midBC.y))`; target = `mid(b, c)`.
20
+ * - `modifiedSchiff` — origin = `mid(a, b)`; target = `mid(b, c)`.
21
+ * - `inside` — origin = `mid(b, c)`; target = `midBC + (c - midAB)`.
22
+ *
23
+ * @since 1.3
24
+ * @stable
25
+ * @example
26
+ * import { medianOriginFor } from "./pitchforkGeom.js";
27
+ * const origin = medianOriginFor(
28
+ * "standard",
29
+ * { x: 0, y: 0 }, { x: 10, y: 10 }, { x: 20, y: 0 },
30
+ * );
31
+ * void origin;
32
+ */
33
+ export function medianOriginFor(variant, a, b, c) {
34
+ const midBC = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };
35
+ if (variant === "schiff") {
36
+ return { x: a.x, y: (a.y + midBC.y) / 2 };
37
+ }
38
+ if (variant === "modifiedSchiff") {
39
+ return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
40
+ }
41
+ if (variant === "inside") {
42
+ return midBC;
43
+ }
44
+ return a;
45
+ }
46
+ /**
47
+ * Per-variant median-target point — the second endpoint of the
48
+ * pitchfork's median rail. A decomposer uses it to compute the
49
+ * extension vector `target - origin` reused for the two parallel handle
50
+ * rails.
51
+ *
52
+ * @since 1.3
53
+ * @stable
54
+ * @example
55
+ * import { medianTargetFor } from "./pitchforkGeom.js";
56
+ * const target = medianTargetFor(
57
+ * "standard",
58
+ * { x: 0, y: 0 }, { x: 10, y: 10 }, { x: 20, y: 0 },
59
+ * );
60
+ * void target;
61
+ */
62
+ export function medianTargetFor(variant, a, b, c) {
63
+ const midBC = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };
64
+ if (variant === "inside") {
65
+ const midAB = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
66
+ return { x: midBC.x + (c.x - midAB.x), y: midBC.y + (c.y - midAB.y) };
67
+ }
68
+ return midBC;
69
+ }
70
+ //# sourceMappingURL=pitchforkGeom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pitchforkGeom.js","sourceRoot":"","sources":["../../../src/geometry/_lib/pitchforkGeom.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,iEAAiE;AACjE,qCAAqC;AACrC,gEAAgE;AAChE,oEAAoE;AACpE,qEAAqE;AACrE,qBAAqB;AAQrB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,eAAe,CAC3B,OAAyB,EACzB,CAAS,EACT,CAAS,EACT,CAAS;IAET,MAAM,KAAK,GAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACjE,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,KAAK,gBAAgB,EAAE,CAAC;QAC/B,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACtD,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAC3B,OAAyB,EACzB,CAAS,EACT,CAAS,EACT,CAAS;IAET,MAAM,KAAK,GAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACjE,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,KAAK,GAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,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// Median-origin + median-target formulas moved verbatim from the\n// canvas2d adapter's renderer helper\n// examples/canvas2d-adapter/src/render/draw/pitchforkGeom.ts.\n// The originating math is invinite's pitchfork-geometry lib (commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed\n// MIT for chartlang.\n\nimport type { PitchforkState } from \"@invinite-org/chartlang-core\";\n\nimport type { Point2 } from \"../types.js\";\n\ntype PitchforkVariant = PitchforkState[\"variant\"];\n\n/**\n * Per-variant median-origin point — the first endpoint of the\n * pitchfork's median rail in pixel space. A decomposer draws three\n * lines: the median (origin → target → +extension) and two parallel\n * handles through `b` and `c` offset by the same `target - origin`\n * vector.\n *\n * Variants:\n * - `standard` — origin = `a`; target = `mid(b, c)`.\n * - `schiff` — origin = `(a.x, mid(a.y, midBC.y))`; target = `mid(b, c)`.\n * - `modifiedSchiff` — origin = `mid(a, b)`; target = `mid(b, c)`.\n * - `inside` — origin = `mid(b, c)`; target = `midBC + (c - midAB)`.\n *\n * @since 1.3\n * @stable\n * @example\n * import { medianOriginFor } from \"./pitchforkGeom.js\";\n * const origin = medianOriginFor(\n * \"standard\",\n * { x: 0, y: 0 }, { x: 10, y: 10 }, { x: 20, y: 0 },\n * );\n * void origin;\n */\nexport function medianOriginFor(\n variant: PitchforkVariant,\n a: Point2,\n b: Point2,\n c: Point2,\n): Point2 {\n const midBC: Point2 = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };\n if (variant === \"schiff\") {\n return { x: a.x, y: (a.y + midBC.y) / 2 };\n }\n if (variant === \"modifiedSchiff\") {\n return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n }\n if (variant === \"inside\") {\n return midBC;\n }\n return a;\n}\n\n/**\n * Per-variant median-target point — the second endpoint of the\n * pitchfork's median rail. A decomposer uses it to compute the\n * extension vector `target - origin` reused for the two parallel handle\n * rails.\n *\n * @since 1.3\n * @stable\n * @example\n * import { medianTargetFor } from \"./pitchforkGeom.js\";\n * const target = medianTargetFor(\n * \"standard\",\n * { x: 0, y: 0 }, { x: 10, y: 10 }, { x: 20, y: 0 },\n * );\n * void target;\n */\nexport function medianTargetFor(\n variant: PitchforkVariant,\n a: Point2,\n b: Point2,\n c: Point2,\n): Point2 {\n const midBC: Point2 = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };\n if (variant === \"inside\") {\n const midAB: Point2 = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n return { x: midBC.x + (c.x - midAB.x), y: midBC.y + (c.y - midAB.y) };\n }\n return midBC;\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { ShapeStyle } from "@invinite-org/chartlang-core";
2
+ import type { FillStyle, StrokeStyle } from "../types.js";
3
+ /**
4
+ * Resolved {@link ShapeStyle} → IR styles. `stroke` is always present
5
+ * (defaults `"#000000"` / `1` / solid); `fill` is present only when the
6
+ * source `style.fill` is set, with `alpha` defaulting to `1`. The
7
+ * box / shape decomposers map this straight onto a `polyline` / `arc`
8
+ * primitive's `stroke` / `fill`. Pure — no `ctx`.
9
+ *
10
+ * @since 1.3
11
+ * @stable
12
+ * @example
13
+ * const r = resolveShapeStyle({ stroke: "#3b82f6", fill: "#dbeafe", fillAlpha: 0.4 });
14
+ * // r.stroke.color === "#3b82f6"; r.fill?.alpha === 0.4
15
+ * void r;
16
+ */
17
+ export declare function resolveShapeStyle(style: ShapeStyle): {
18
+ readonly stroke: StrokeStyle;
19
+ readonly fill?: FillStyle;
20
+ };
21
+ //# sourceMappingURL=shapeStyle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shapeStyle.d.ts","sourceRoot":"","sources":["../../../src/geometry/_lib/shapeStyle.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE/D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO1D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG;IAClD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;CAC7B,CAaA"}