@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 @@
1
+ {"version":3,"file":"lines.js","sourceRoot":"","sources":["../../../src/geometry/kinds/lines.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// Stroke + extension geometry moved from the canvas2d adapter's\n// per-kind line renderers\n// examples/canvas2d-adapter/src/render/draw/{line,horizontalLine,\n// horizontalRay,verticalLine,crossLine,trendAngle}.ts.\n// The originating math is invinite's line / ray / extended-line /\n// horizontal-line / horizontal-ray / vertical-line / cross-line /\n// trend-angle tools (commit 078f41fe2569d659d5aba726da8bcb5d3e2ced02,\n// © Invinite); re-licensed MIT for chartlang.\n\nimport type {\n CrossLineState,\n HorizontalLineState,\n HorizontalRayState,\n LineState,\n TrendAngleState,\n VerticalLineState,\n} from \"@invinite-org/chartlang-core\";\n\nimport { dashPattern } from \"../_lib/dash.js\";\nimport { extendLineSegment } from \"../_lib/lineExtend.js\";\nimport { strokeOf } from \"../_lib/strokeStyle.js\";\nimport { priceToY, timeToX, worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Viewport } from \"../types.js\";\n\nconst DEFAULT_COLOR = \"#000000\";\nconst ANGLE_ARC_RADIUS_PX = 24;\nconst ANGLE_TEXT_FONT = \"12px sans-serif\";\nconst ANGLE_TEXT_OFFSET_PX = 6;\n\n/**\n * Decompose a `line` drawing — a single segment, optionally extended to\n * the viewport edges via {@link extendLineSegment} (the `ray` /\n * `extended-line` collapse).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: LineState;\n * declare const v: Viewport;\n * const prims = decomposeLine(s, v);\n * // prims[0].kind === \"polyline\"\n * void prims;\n */\nexport function decomposeLine(state: LineState, view: Viewport): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const { from, to } = extendLineSegment(\n a,\n b,\n { extendLeft: state.style.extendLeft, extendRight: state.style.extendRight },\n view,\n );\n return [{ kind: \"polyline\", points: [from, to], closed: false, stroke: strokeOf(state.style) }];\n}\n\n/**\n * Decompose a `horizontal-line` drawing — a segment from `x = 0` to\n * `x = view.pxWidth` at `priceToY(state.price)`.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: HorizontalLineState;\n * declare const v: Viewport;\n * const prims = decomposeHorizontalLine(s, v);\n * void prims;\n */\nexport function decomposeHorizontalLine(\n state: HorizontalLineState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const y = priceToY(state.price, view);\n return [\n {\n kind: \"polyline\",\n points: [\n { x: 0, y },\n { x: view.pxWidth, y },\n ],\n closed: false,\n stroke: strokeOf(state.style),\n },\n ];\n}\n\n/**\n * Decompose a `horizontal-ray` drawing — a segment from the projected\n * anchor across the right edge at constant y.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: HorizontalRayState;\n * declare const v: Viewport;\n * const prims = decomposeHorizontalRay(s, v);\n * void prims;\n */\nexport function decomposeHorizontalRay(\n state: HorizontalRayState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const origin = worldPointToPixel(state.anchor, view);\n return [\n {\n kind: \"polyline\",\n points: [origin, { x: view.pxWidth, y: origin.y }],\n closed: false,\n stroke: strokeOf(state.style),\n },\n ];\n}\n\n/**\n * Decompose a `vertical-line` drawing — a segment from `y = 0` to\n * `y = view.pxHeight` at `timeToX(state.time)`.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: VerticalLineState;\n * declare const v: Viewport;\n * const prims = decomposeVerticalLine(s, v);\n * void prims;\n */\nexport function decomposeVerticalLine(\n state: VerticalLineState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const x = timeToX(state.time, view);\n return [\n {\n kind: \"polyline\",\n points: [\n { x, y: 0 },\n { x, y: view.pxHeight },\n ],\n closed: false,\n stroke: strokeOf(state.style),\n },\n ];\n}\n\n/**\n * Decompose a `cross-line` drawing — a horizontal segment and a\n * vertical segment crossing at the projected anchor. Two polylines\n * sharing one stroke style.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: CrossLineState;\n * declare const v: Viewport;\n * const prims = decomposeCrossLine(s, v);\n * // prims.length === 2\n * void prims;\n */\nexport function decomposeCrossLine(\n state: CrossLineState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const p = worldPointToPixel(state.anchor, view);\n const stroke = strokeOf(state.style);\n return [\n {\n kind: \"polyline\",\n points: [\n { x: 0, y: p.y },\n { x: view.pxWidth, y: p.y },\n ],\n closed: false,\n stroke,\n },\n {\n kind: \"polyline\",\n points: [\n { x: p.x, y: 0 },\n { x: p.x, y: view.pxHeight },\n ],\n closed: false,\n stroke,\n },\n ];\n}\n\n/**\n * Decompose a `trend-angle` drawing — the main segment, a small arc\n * spanning the screen-space angle off the first anchor, and the angle\n * label in degrees. The arc is solid regardless of `lineStyle` so it\n * reads cleanly; the angle is measured in screen-pixel space with the\n * canvas y-axis flipped (`-dy`) so a positive angle reads \"upward to\n * the right\" (matching the source renderer's convention).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: TrendAngleState;\n * declare const v: Viewport;\n * const prims = decomposeTrendAngle(s, v);\n * // prims[0].kind === \"polyline\"; prims[1].kind === \"arc\"; prims[2].kind === \"text\"\n * void prims;\n */\nexport function decomposeTrendAngle(\n state: TrendAngleState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const color = state.style.color ?? DEFAULT_COLOR;\n const stroke = strokeOf(state.style);\n const angleRad = Math.atan2(-(b.y - a.y), b.x - a.x);\n const degrees = (angleRad * 180) / Math.PI;\n return [\n { kind: \"polyline\", points: [a, b], closed: false, stroke },\n {\n kind: \"arc\",\n cx: a.x,\n cy: a.y,\n r: ANGLE_ARC_RADIUS_PX,\n start: -angleRad,\n end: 0,\n closed: false,\n stroke: { color, width: stroke.width, dash: dashPattern(\"solid\") },\n },\n {\n kind: \"text\",\n x: a.x + ANGLE_ARC_RADIUS_PX + ANGLE_TEXT_OFFSET_PX,\n y: a.y,\n text: `${degrees.toFixed(1)}°`,\n color,\n font: ANGLE_TEXT_FONT,\n align: \"left\",\n baseline: \"middle\",\n },\n ];\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { MarkerState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose a `marker` drawing. The reference adapter paints a marker's
5
+ * `text` label only (no glyph) — so this emits a single `text`
6
+ * primitive when `state.text` is a non-empty string, and `[]`
7
+ * otherwise. (The IR `marker` primitive exists for adapters / future
8
+ * kinds; no basic kind emits it.) `style.bgColor` is preserved on the
9
+ * IR `text` primitive but the canvas sink does not paint a background
10
+ * rect, matching the source renderer.
11
+ *
12
+ * @since 1.3
13
+ * @stable
14
+ * @example
15
+ * declare const s: MarkerState;
16
+ * declare const v: Viewport;
17
+ * const prims = decomposeMarker(s, v);
18
+ * void prims;
19
+ */
20
+ export declare function decomposeMarker(state: MarkerState, view: Viewport): ReadonlyArray<DrawPrimitive>;
21
+ //# sourceMappingURL=marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/marker.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAIhE,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC,aAAa,CAAC,CAiBhG"}
@@ -0,0 +1,47 @@
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
+ // Marker geometry moved from the canvas2d adapter's per-kind renderer
5
+ // examples/canvas2d-adapter/src/render/draw/marker.ts.
6
+ // The originating math is invinite's marker tool (commit
7
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
8
+ // MIT for chartlang.
9
+ import { resolveTextOpts } from "../_lib/textStyle.js";
10
+ import { worldPointToPixel } from "../project.js";
11
+ /**
12
+ * Decompose a `marker` drawing. The reference adapter paints a marker's
13
+ * `text` label only (no glyph) — so this emits a single `text`
14
+ * primitive when `state.text` is a non-empty string, and `[]`
15
+ * otherwise. (The IR `marker` primitive exists for adapters / future
16
+ * kinds; no basic kind emits it.) `style.bgColor` is preserved on the
17
+ * IR `text` primitive but the canvas sink does not paint a background
18
+ * rect, matching the source renderer.
19
+ *
20
+ * @since 1.3
21
+ * @stable
22
+ * @example
23
+ * declare const s: MarkerState;
24
+ * declare const v: Viewport;
25
+ * const prims = decomposeMarker(s, v);
26
+ * void prims;
27
+ */
28
+ export function decomposeMarker(state, view) {
29
+ if (state.text === undefined || state.text.length === 0)
30
+ return [];
31
+ const anchor = worldPointToPixel(state.anchor, view);
32
+ const resolved = resolveTextOpts(state.style);
33
+ return [
34
+ {
35
+ kind: "text",
36
+ x: anchor.x,
37
+ y: anchor.y,
38
+ text: state.text,
39
+ color: resolved.color,
40
+ font: resolved.font,
41
+ align: resolved.align,
42
+ baseline: resolved.baseline,
43
+ ...(state.style.bgColor === undefined ? {} : { bgColor: state.style.bgColor }),
44
+ },
45
+ ];
46
+ }
47
+ //# sourceMappingURL=marker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker.js","sourceRoot":"","sources":["../../../src/geometry/kinds/marker.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,sEAAsE;AACtE,yDAAyD;AACzD,yDAAyD;AACzD,qEAAqE;AACrE,qBAAqB;AAIrB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGlD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB,EAAE,IAAc;IAC9D,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO;QACH;YACI,IAAI,EAAE,MAAM;YACZ,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;SACjF;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// Marker geometry moved from the canvas2d adapter's per-kind renderer\n// examples/canvas2d-adapter/src/render/draw/marker.ts.\n// The originating math is invinite's marker tool (commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed\n// MIT for chartlang.\n\nimport type { MarkerState } from \"@invinite-org/chartlang-core\";\n\nimport { resolveTextOpts } from \"../_lib/textStyle.js\";\nimport { worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Viewport } from \"../types.js\";\n\n/**\n * Decompose a `marker` drawing. The reference adapter paints a marker's\n * `text` label only (no glyph) — so this emits a single `text`\n * primitive when `state.text` is a non-empty string, and `[]`\n * otherwise. (The IR `marker` primitive exists for adapters / future\n * kinds; no basic kind emits it.) `style.bgColor` is preserved on the\n * IR `text` primitive but the canvas sink does not paint a background\n * rect, matching the source renderer.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: MarkerState;\n * declare const v: Viewport;\n * const prims = decomposeMarker(s, v);\n * void prims;\n */\nexport function decomposeMarker(state: MarkerState, view: Viewport): ReadonlyArray<DrawPrimitive> {\n if (state.text === undefined || state.text.length === 0) return [];\n const anchor = worldPointToPixel(state.anchor, view);\n const resolved = resolveTextOpts(state.style);\n return [\n {\n kind: \"text\",\n x: anchor.x,\n y: anchor.y,\n text: state.text,\n color: resolved.color,\n font: resolved.font,\n align: resolved.align,\n baseline: resolved.baseline,\n ...(state.style.bgColor === undefined ? {} : { bgColor: state.style.bgColor }),\n },\n ];\n}\n"]}
@@ -0,0 +1,85 @@
1
+ import type { AbcdPatternState, CypherPatternState, HeadAndShouldersState, ThreeDrivesPatternState, TrianglePatternState, XabcdPatternState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose an `xabcd-pattern` drawing — a 4-leg labelled open polyline
5
+ * through the 5 anchors (X-A-B-C-D), one `text` per pivot.
6
+ *
7
+ * @since 1.3
8
+ * @stable
9
+ * @example
10
+ * declare const s: XabcdPatternState;
11
+ * declare const v: Viewport;
12
+ * const prims = decomposeXabcdPattern(s, v);
13
+ * void prims;
14
+ */
15
+ export declare function decomposeXabcdPattern(state: XabcdPatternState, view: Viewport): ReadonlyArray<DrawPrimitive>;
16
+ /**
17
+ * Decompose a `cypher-pattern` drawing — structurally identical to
18
+ * `xabcd-pattern`: a 4-leg labelled open polyline through the 5 anchors
19
+ * (X-A-B-C-D). The cypher fib-ratio invariants are a script-author
20
+ * concern, not a geometry one.
21
+ *
22
+ * @since 1.3
23
+ * @stable
24
+ * @example
25
+ * declare const s: CypherPatternState;
26
+ * declare const v: Viewport;
27
+ * const prims = decomposeCypherPattern(s, v);
28
+ * void prims;
29
+ */
30
+ export declare function decomposeCypherPattern(state: CypherPatternState, view: Viewport): ReadonlyArray<DrawPrimitive>;
31
+ /**
32
+ * Decompose a `head-and-shoulders` drawing — a 4-leg labelled open
33
+ * polyline through the 5 anchors (LS-LL-H-RL-RS) plus a neckline
34
+ * polyline between the two trough anchors (LL → RL).
35
+ *
36
+ * @since 1.3
37
+ * @stable
38
+ * @example
39
+ * declare const s: HeadAndShouldersState;
40
+ * declare const v: Viewport;
41
+ * const prims = decomposeHeadAndShoulders(s, v);
42
+ * void prims;
43
+ */
44
+ export declare function decomposeHeadAndShoulders(state: HeadAndShouldersState, view: Viewport): ReadonlyArray<DrawPrimitive>;
45
+ /**
46
+ * Decompose an `abcd-pattern` drawing — a 3-leg labelled open polyline
47
+ * through the 4 anchors (A-B-C-D).
48
+ *
49
+ * @since 1.3
50
+ * @stable
51
+ * @example
52
+ * declare const s: AbcdPatternState;
53
+ * declare const v: Viewport;
54
+ * const prims = decomposeAbcdPattern(s, v);
55
+ * void prims;
56
+ */
57
+ export declare function decomposeAbcdPattern(state: AbcdPatternState, view: Viewport): ReadonlyArray<DrawPrimitive>;
58
+ /**
59
+ * Decompose a `triangle-pattern` drawing — a 2-leg labelled open
60
+ * polyline through the 3 anchors (A-B-C). Distinct from `draw.triangle`:
61
+ * this is a `LineDrawStyle` harmonic outline, not a filled shape.
62
+ *
63
+ * @since 1.3
64
+ * @stable
65
+ * @example
66
+ * declare const s: TrianglePatternState;
67
+ * declare const v: Viewport;
68
+ * const prims = decomposeTrianglePattern(s, v);
69
+ * void prims;
70
+ */
71
+ export declare function decomposeTrianglePattern(state: TrianglePatternState, view: Viewport): ReadonlyArray<DrawPrimitive>;
72
+ /**
73
+ * Decompose a `three-drives-pattern` drawing — a 6-leg labelled open
74
+ * polyline through the 7 anchors (start → d1 → r1 → d2 → r2 → d3 → end).
75
+ *
76
+ * @since 1.3
77
+ * @stable
78
+ * @example
79
+ * declare const s: ThreeDrivesPatternState;
80
+ * declare const v: Viewport;
81
+ * const prims = decomposeThreeDrivesPattern(s, v);
82
+ * void prims;
83
+ */
84
+ export declare function decomposeThreeDrivesPattern(state: ThreeDrivesPatternState, view: Viewport): ReadonlyArray<DrawPrimitive>;
85
+ //# sourceMappingURL=patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/patterns.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACR,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACpB,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAW3D;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACjC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAG9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAClC,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAG9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACrC,KAAK,EAAE,qBAAqB,EAC5B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAc9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAChC,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAG9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAG9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CACvC,KAAK,EAAE,uBAAuB,EAC9B,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAG9B"}
@@ -0,0 +1,133 @@
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
+ // Harmonic-pattern geometry moved from the canvas2d adapter's per-kind
5
+ // renderers
6
+ // examples/canvas2d-adapter/src/render/draw/{xabcdPattern,
7
+ // cypherPattern,headAndShoulders,abcdPattern,trianglePattern,
8
+ // threeDrivesPattern}.ts.
9
+ // The originating math is invinite's pattern tools (commit
10
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
11
+ // MIT for chartlang.
12
+ import { dashPattern } from "../_lib/dash.js";
13
+ import { namedPolylinePrimitives } from "../_lib/namedPolyline.js";
14
+ import { worldPointToPixel } from "../project.js";
15
+ const XABCD_LABELS = ["X", "A", "B", "C", "D"];
16
+ const HEAD_AND_SHOULDERS_LABELS = ["LS", "LL", "H", "RL", "RS"];
17
+ const ABCD_LABELS = ["A", "B", "C", "D"];
18
+ const TRIANGLE_LABELS = ["A", "B", "C"];
19
+ const THREE_DRIVES_LABELS = ["S", "D1", "R1", "D2", "R2", "D3", "E"];
20
+ const HEAD_AND_SHOULDERS_DEFAULT_COLOR = "#f59e0b";
21
+ const NECKLINE_WIDTH = 1;
22
+ /**
23
+ * Decompose an `xabcd-pattern` drawing — a 4-leg labelled open polyline
24
+ * through the 5 anchors (X-A-B-C-D), one `text` per pivot.
25
+ *
26
+ * @since 1.3
27
+ * @stable
28
+ * @example
29
+ * declare const s: XabcdPatternState;
30
+ * declare const v: Viewport;
31
+ * const prims = decomposeXabcdPattern(s, v);
32
+ * void prims;
33
+ */
34
+ export function decomposeXabcdPattern(state, view) {
35
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
36
+ return namedPolylinePrimitives(points, XABCD_LABELS, state.style);
37
+ }
38
+ /**
39
+ * Decompose a `cypher-pattern` drawing — structurally identical to
40
+ * `xabcd-pattern`: a 4-leg labelled open polyline through the 5 anchors
41
+ * (X-A-B-C-D). The cypher fib-ratio invariants are a script-author
42
+ * concern, not a geometry one.
43
+ *
44
+ * @since 1.3
45
+ * @stable
46
+ * @example
47
+ * declare const s: CypherPatternState;
48
+ * declare const v: Viewport;
49
+ * const prims = decomposeCypherPattern(s, v);
50
+ * void prims;
51
+ */
52
+ export function decomposeCypherPattern(state, view) {
53
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
54
+ return namedPolylinePrimitives(points, XABCD_LABELS, state.style);
55
+ }
56
+ /**
57
+ * Decompose a `head-and-shoulders` drawing — a 4-leg labelled open
58
+ * polyline through the 5 anchors (LS-LL-H-RL-RS) plus a neckline
59
+ * polyline between the two trough anchors (LL → RL).
60
+ *
61
+ * @since 1.3
62
+ * @stable
63
+ * @example
64
+ * declare const s: HeadAndShouldersState;
65
+ * declare const v: Viewport;
66
+ * const prims = decomposeHeadAndShoulders(s, v);
67
+ * void prims;
68
+ */
69
+ export function decomposeHeadAndShoulders(state, view) {
70
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
71
+ const out = [...namedPolylinePrimitives(points, HEAD_AND_SHOULDERS_LABELS, state.style)];
72
+ out.push({
73
+ kind: "polyline",
74
+ points: [points[1], points[3]],
75
+ closed: false,
76
+ stroke: {
77
+ color: state.style.color ?? HEAD_AND_SHOULDERS_DEFAULT_COLOR,
78
+ width: NECKLINE_WIDTH,
79
+ dash: dashPattern("solid"),
80
+ },
81
+ });
82
+ return out;
83
+ }
84
+ /**
85
+ * Decompose an `abcd-pattern` drawing — a 3-leg labelled open polyline
86
+ * through the 4 anchors (A-B-C-D).
87
+ *
88
+ * @since 1.3
89
+ * @stable
90
+ * @example
91
+ * declare const s: AbcdPatternState;
92
+ * declare const v: Viewport;
93
+ * const prims = decomposeAbcdPattern(s, v);
94
+ * void prims;
95
+ */
96
+ export function decomposeAbcdPattern(state, view) {
97
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
98
+ return namedPolylinePrimitives(points, ABCD_LABELS, state.style);
99
+ }
100
+ /**
101
+ * Decompose a `triangle-pattern` drawing — a 2-leg labelled open
102
+ * polyline through the 3 anchors (A-B-C). Distinct from `draw.triangle`:
103
+ * this is a `LineDrawStyle` harmonic outline, not a filled shape.
104
+ *
105
+ * @since 1.3
106
+ * @stable
107
+ * @example
108
+ * declare const s: TrianglePatternState;
109
+ * declare const v: Viewport;
110
+ * const prims = decomposeTrianglePattern(s, v);
111
+ * void prims;
112
+ */
113
+ export function decomposeTrianglePattern(state, view) {
114
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
115
+ return namedPolylinePrimitives(points, TRIANGLE_LABELS, state.style);
116
+ }
117
+ /**
118
+ * Decompose a `three-drives-pattern` drawing — a 6-leg labelled open
119
+ * polyline through the 7 anchors (start → d1 → r1 → d2 → r2 → d3 → end).
120
+ *
121
+ * @since 1.3
122
+ * @stable
123
+ * @example
124
+ * declare const s: ThreeDrivesPatternState;
125
+ * declare const v: Viewport;
126
+ * const prims = decomposeThreeDrivesPattern(s, v);
127
+ * void prims;
128
+ */
129
+ export function decomposeThreeDrivesPattern(state, view) {
130
+ const points = state.anchors.map((p) => worldPointToPixel(p, view));
131
+ return namedPolylinePrimitives(points, THREE_DRIVES_LABELS, state.style);
132
+ }
133
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/geometry/kinds/patterns.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// Harmonic-pattern geometry moved from the canvas2d adapter's per-kind\n// renderers\n// examples/canvas2d-adapter/src/render/draw/{xabcdPattern,\n// cypherPattern,headAndShoulders,abcdPattern,trianglePattern,\n// threeDrivesPattern}.ts.\n// The originating math is invinite's pattern tools (commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed\n// MIT for chartlang.\n\nimport type {\n AbcdPatternState,\n CypherPatternState,\n HeadAndShouldersState,\n ThreeDrivesPatternState,\n TrianglePatternState,\n XabcdPatternState,\n} from \"@invinite-org/chartlang-core\";\n\nimport { dashPattern } from \"../_lib/dash.js\";\nimport { namedPolylinePrimitives } from \"../_lib/namedPolyline.js\";\nimport { worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Viewport } from \"../types.js\";\n\nconst XABCD_LABELS: ReadonlyArray<string> = [\"X\", \"A\", \"B\", \"C\", \"D\"];\nconst HEAD_AND_SHOULDERS_LABELS: ReadonlyArray<string> = [\"LS\", \"LL\", \"H\", \"RL\", \"RS\"];\nconst ABCD_LABELS: ReadonlyArray<string> = [\"A\", \"B\", \"C\", \"D\"];\nconst TRIANGLE_LABELS: ReadonlyArray<string> = [\"A\", \"B\", \"C\"];\nconst THREE_DRIVES_LABELS: ReadonlyArray<string> = [\"S\", \"D1\", \"R1\", \"D2\", \"R2\", \"D3\", \"E\"];\n\nconst HEAD_AND_SHOULDERS_DEFAULT_COLOR = \"#f59e0b\";\nconst NECKLINE_WIDTH = 1;\n\n/**\n * Decompose an `xabcd-pattern` drawing — a 4-leg labelled open polyline\n * through the 5 anchors (X-A-B-C-D), one `text` per pivot.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: XabcdPatternState;\n * declare const v: Viewport;\n * const prims = decomposeXabcdPattern(s, v);\n * void prims;\n */\nexport function decomposeXabcdPattern(\n state: XabcdPatternState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n return namedPolylinePrimitives(points, XABCD_LABELS, state.style);\n}\n\n/**\n * Decompose a `cypher-pattern` drawing — structurally identical to\n * `xabcd-pattern`: a 4-leg labelled open polyline through the 5 anchors\n * (X-A-B-C-D). The cypher fib-ratio invariants are a script-author\n * concern, not a geometry one.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: CypherPatternState;\n * declare const v: Viewport;\n * const prims = decomposeCypherPattern(s, v);\n * void prims;\n */\nexport function decomposeCypherPattern(\n state: CypherPatternState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n return namedPolylinePrimitives(points, XABCD_LABELS, state.style);\n}\n\n/**\n * Decompose a `head-and-shoulders` drawing — a 4-leg labelled open\n * polyline through the 5 anchors (LS-LL-H-RL-RS) plus a neckline\n * polyline between the two trough anchors (LL → RL).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: HeadAndShouldersState;\n * declare const v: Viewport;\n * const prims = decomposeHeadAndShoulders(s, v);\n * void prims;\n */\nexport function decomposeHeadAndShoulders(\n state: HeadAndShouldersState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n const out = [...namedPolylinePrimitives(points, HEAD_AND_SHOULDERS_LABELS, state.style)];\n out.push({\n kind: \"polyline\",\n points: [points[1], points[3]],\n closed: false,\n stroke: {\n color: state.style.color ?? HEAD_AND_SHOULDERS_DEFAULT_COLOR,\n width: NECKLINE_WIDTH,\n dash: dashPattern(\"solid\"),\n },\n });\n return out;\n}\n\n/**\n * Decompose an `abcd-pattern` drawing — a 3-leg labelled open polyline\n * through the 4 anchors (A-B-C-D).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: AbcdPatternState;\n * declare const v: Viewport;\n * const prims = decomposeAbcdPattern(s, v);\n * void prims;\n */\nexport function decomposeAbcdPattern(\n state: AbcdPatternState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n return namedPolylinePrimitives(points, ABCD_LABELS, state.style);\n}\n\n/**\n * Decompose a `triangle-pattern` drawing — a 2-leg labelled open\n * polyline through the 3 anchors (A-B-C). Distinct from `draw.triangle`:\n * this is a `LineDrawStyle` harmonic outline, not a filled shape.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: TrianglePatternState;\n * declare const v: Viewport;\n * const prims = decomposeTrianglePattern(s, v);\n * void prims;\n */\nexport function decomposeTrianglePattern(\n state: TrianglePatternState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n return namedPolylinePrimitives(points, TRIANGLE_LABELS, state.style);\n}\n\n/**\n * Decompose a `three-drives-pattern` drawing — a 6-leg labelled open\n * polyline through the 7 anchors (start → d1 → r1 → d2 → r2 → d3 → end).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: ThreeDrivesPatternState;\n * declare const v: Viewport;\n * const prims = decomposeThreeDrivesPattern(s, v);\n * void prims;\n */\nexport function decomposeThreeDrivesPattern(\n state: ThreeDrivesPatternState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const points = state.anchors.map((p) => worldPointToPixel(p, view));\n return namedPolylinePrimitives(points, THREE_DRIVES_LABELS, state.style);\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import type { PitchfanState, PitchforkState } from "@invinite-org/chartlang-core";
2
+ import type { DrawPrimitive, Viewport } from "../types.js";
3
+ /**
4
+ * Decompose a `pitchfork` drawing — three line segments: a median rail
5
+ * from the per-variant `medianOrigin` through `medianTarget`, extended
6
+ * one fork vector (`target − origin`) past the target, plus two
7
+ * parallel handle rails through `anchors[1]` / `anchors[2]` offset by
8
+ * that same extension vector. The `variant` discriminator selects one
9
+ * of the four median-origin formulas via {@link medianOriginFor}.
10
+ *
11
+ * @since 1.3
12
+ * @stable
13
+ * @example
14
+ * declare const s: PitchforkState;
15
+ * declare const v: Viewport;
16
+ * const prims = decomposePitchfork(s, v);
17
+ * // prims.length === 3
18
+ * void prims;
19
+ */
20
+ export declare function decomposePitchfork(state: PitchforkState, view: Viewport): ReadonlyArray<DrawPrimitive>;
21
+ /**
22
+ * Decompose a `pitchfan` drawing — three rays from `anchors[0]` passing
23
+ * through `anchors[1]`, `mid(anchors[1], anchors[2])`, and `anchors[2]`.
24
+ * Each ray extends to `max(pxWidth, pxHeight) · 2`; a zero-magnitude ray
25
+ * is skipped (matching the source `continue`).
26
+ *
27
+ * @since 1.3
28
+ * @stable
29
+ * @example
30
+ * declare const s: PitchfanState;
31
+ * declare const v: Viewport;
32
+ * const prims = decomposePitchfan(s, v);
33
+ * void prims;
34
+ */
35
+ export declare function decomposePitchfan(state: PitchfanState, view: Viewport): ReadonlyArray<DrawPrimitive>;
36
+ //# sourceMappingURL=pitchforks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pitchforks.d.ts","sourceRoot":"","sources":["../../../src/geometry/kinds/pitchforks.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAKlF,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK3D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CAiC9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,QAAQ,GACf,aAAa,CAAC,aAAa,CAAC,CA2B9B"}
@@ -0,0 +1,109 @@
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
+ // Pitchfork geometry moved from the canvas2d adapter's per-kind
5
+ // renderers
6
+ // examples/canvas2d-adapter/src/render/draw/{pitchfork,pitchfan}.ts.
7
+ // The originating math is invinite's pitchfork / pitchfan tools (commit
8
+ // 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed
9
+ // MIT for chartlang.
10
+ import { SOLID_DASH } from "../_lib/dash.js";
11
+ import { medianOriginFor, medianTargetFor } from "../_lib/pitchforkGeom.js";
12
+ import { worldPointToPixel } from "../project.js";
13
+ const DEFAULT_COLOR = "#ec4899";
14
+ const DEFAULT_LINE_WIDTH = 1;
15
+ /**
16
+ * Decompose a `pitchfork` drawing — three line segments: a median rail
17
+ * from the per-variant `medianOrigin` through `medianTarget`, extended
18
+ * one fork vector (`target − origin`) past the target, plus two
19
+ * parallel handle rails through `anchors[1]` / `anchors[2]` offset by
20
+ * that same extension vector. The `variant` discriminator selects one
21
+ * of the four median-origin formulas via {@link medianOriginFor}.
22
+ *
23
+ * @since 1.3
24
+ * @stable
25
+ * @example
26
+ * declare const s: PitchforkState;
27
+ * declare const v: Viewport;
28
+ * const prims = decomposePitchfork(s, v);
29
+ * // prims.length === 3
30
+ * void prims;
31
+ */
32
+ export function decomposePitchfork(state, view) {
33
+ const a = worldPointToPixel(state.anchors[0], view);
34
+ const b = worldPointToPixel(state.anchors[1], view);
35
+ const c = worldPointToPixel(state.anchors[2], view);
36
+ const origin = medianOriginFor(state.variant, a, b, c);
37
+ const target = medianTargetFor(state.variant, a, b, c);
38
+ const dx = target.x - origin.x;
39
+ const dy = target.y - origin.y;
40
+ const stroke = {
41
+ color: state.style.color ?? DEFAULT_COLOR,
42
+ width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,
43
+ dash: SOLID_DASH,
44
+ };
45
+ return [
46
+ {
47
+ kind: "polyline",
48
+ points: [origin, { x: target.x + dx, y: target.y + dy }],
49
+ closed: false,
50
+ stroke,
51
+ },
52
+ {
53
+ kind: "polyline",
54
+ points: [b, { x: b.x + dx, y: b.y + dy }],
55
+ closed: false,
56
+ stroke,
57
+ },
58
+ {
59
+ kind: "polyline",
60
+ points: [c, { x: c.x + dx, y: c.y + dy }],
61
+ closed: false,
62
+ stroke,
63
+ },
64
+ ];
65
+ }
66
+ /**
67
+ * Decompose a `pitchfan` drawing — three rays from `anchors[0]` passing
68
+ * through `anchors[1]`, `mid(anchors[1], anchors[2])`, and `anchors[2]`.
69
+ * Each ray extends to `max(pxWidth, pxHeight) · 2`; a zero-magnitude ray
70
+ * is skipped (matching the source `continue`).
71
+ *
72
+ * @since 1.3
73
+ * @stable
74
+ * @example
75
+ * declare const s: PitchfanState;
76
+ * declare const v: Viewport;
77
+ * const prims = decomposePitchfan(s, v);
78
+ * void prims;
79
+ */
80
+ export function decomposePitchfan(state, view) {
81
+ const a = worldPointToPixel(state.anchors[0], view);
82
+ const b = worldPointToPixel(state.anchors[1], view);
83
+ const c = worldPointToPixel(state.anchors[2], view);
84
+ const midBC = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };
85
+ const stroke = {
86
+ color: state.style.color ?? DEFAULT_COLOR,
87
+ width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,
88
+ dash: SOLID_DASH,
89
+ };
90
+ const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;
91
+ const out = [];
92
+ for (const target of [b, midBC, c]) {
93
+ const dx = target.x - a.x;
94
+ const dy = target.y - a.y;
95
+ const mag = Math.hypot(dx, dy);
96
+ if (mag === 0)
97
+ continue;
98
+ const ux = dx / mag;
99
+ const uy = dy / mag;
100
+ out.push({
101
+ kind: "polyline",
102
+ points: [a, { x: a.x + ux * rayLength, y: a.y + uy * rayLength }],
103
+ closed: false,
104
+ stroke,
105
+ });
106
+ }
107
+ return out;
108
+ }
109
+ //# sourceMappingURL=pitchforks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pitchforks.js","sourceRoot":"","sources":["../../../src/geometry/kinds/pitchforks.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// Pitchfork geometry moved from the canvas2d adapter's per-kind\n// renderers\n// examples/canvas2d-adapter/src/render/draw/{pitchfork,pitchfan}.ts.\n// The originating math is invinite's pitchfork / pitchfan tools (commit\n// 078f41fe2569d659d5aba726da8bcb5d3e2ced02, © Invinite); re-licensed\n// MIT for chartlang.\n\nimport type { PitchfanState, PitchforkState } from \"@invinite-org/chartlang-core\";\n\nimport { SOLID_DASH } from \"../_lib/dash.js\";\nimport { medianOriginFor, medianTargetFor } from \"../_lib/pitchforkGeom.js\";\nimport { worldPointToPixel } from \"../project.js\";\nimport type { DrawPrimitive, Viewport } from \"../types.js\";\n\nconst DEFAULT_COLOR = \"#ec4899\";\nconst DEFAULT_LINE_WIDTH = 1;\n\n/**\n * Decompose a `pitchfork` drawing — three line segments: a median rail\n * from the per-variant `medianOrigin` through `medianTarget`, extended\n * one fork vector (`target − origin`) past the target, plus two\n * parallel handle rails through `anchors[1]` / `anchors[2]` offset by\n * that same extension vector. The `variant` discriminator selects one\n * of the four median-origin formulas via {@link medianOriginFor}.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: PitchforkState;\n * declare const v: Viewport;\n * const prims = decomposePitchfork(s, v);\n * // prims.length === 3\n * void prims;\n */\nexport function decomposePitchfork(\n state: PitchforkState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const c = worldPointToPixel(state.anchors[2], view);\n const origin = medianOriginFor(state.variant, a, b, c);\n const target = medianTargetFor(state.variant, a, b, c);\n const dx = target.x - origin.x;\n const dy = target.y - origin.y;\n const stroke = {\n color: state.style.color ?? DEFAULT_COLOR,\n width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n dash: SOLID_DASH,\n };\n return [\n {\n kind: \"polyline\",\n points: [origin, { x: target.x + dx, y: target.y + dy }],\n closed: false,\n stroke,\n },\n {\n kind: \"polyline\",\n points: [b, { x: b.x + dx, y: b.y + dy }],\n closed: false,\n stroke,\n },\n {\n kind: \"polyline\",\n points: [c, { x: c.x + dx, y: c.y + dy }],\n closed: false,\n stroke,\n },\n ];\n}\n\n/**\n * Decompose a `pitchfan` drawing — three rays from `anchors[0]` passing\n * through `anchors[1]`, `mid(anchors[1], anchors[2])`, and `anchors[2]`.\n * Each ray extends to `max(pxWidth, pxHeight) · 2`; a zero-magnitude ray\n * is skipped (matching the source `continue`).\n *\n * @since 1.3\n * @stable\n * @example\n * declare const s: PitchfanState;\n * declare const v: Viewport;\n * const prims = decomposePitchfan(s, v);\n * void prims;\n */\nexport function decomposePitchfan(\n state: PitchfanState,\n view: Viewport,\n): ReadonlyArray<DrawPrimitive> {\n const a = worldPointToPixel(state.anchors[0], view);\n const b = worldPointToPixel(state.anchors[1], view);\n const c = worldPointToPixel(state.anchors[2], view);\n const midBC = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };\n const stroke = {\n color: state.style.color ?? DEFAULT_COLOR,\n width: state.style.lineWidth ?? DEFAULT_LINE_WIDTH,\n dash: SOLID_DASH,\n };\n const rayLength = Math.max(view.pxWidth, view.pxHeight) * 2;\n const out: DrawPrimitive[] = [];\n for (const target of [b, midBC, c]) {\n const dx = target.x - a.x;\n const dy = target.y - a.y;\n const mag = Math.hypot(dx, dy);\n if (mag === 0) continue;\n const ux = dx / mag;\n const uy = dy / mag;\n out.push({\n kind: \"polyline\",\n points: [a, { x: a.x + ux * rayLength, y: a.y + uy * rayLength }],\n closed: false,\n stroke,\n });\n }\n return out;\n}\n"]}
@@ -0,0 +1,50 @@
1
+ import type { WorldPoint } from "@invinite-org/chartlang-core";
2
+ import type { Point2, Viewport } from "./types.js";
3
+ /**
4
+ * Map a world time (UTC ms) to an x pixel coordinate. When `xMin ===
5
+ * xMax` (single-bar viewport) the result pins to the canvas centre —
6
+ * no NaN propagation. Ported verbatim from the canvas2d adapter's
7
+ * `render/coords.ts` so every adapter projects time identically.
8
+ *
9
+ * @since 1.3
10
+ * @stable
11
+ * @example
12
+ * const x = timeToX(5, { xMin: 0, xMax: 10, yMin: 0, yMax: 1, pxWidth: 100, pxHeight: 1 });
13
+ * // x === 50
14
+ * void x;
15
+ */
16
+ export declare function timeToX(time: number, view: Viewport): number;
17
+ /**
18
+ * Map a world price to a y pixel coordinate. The y axis is flipped
19
+ * (canvas y grows downward, prices grow upward), so a price at
20
+ * `view.yMax` lands at `y = 0` and `view.yMin` lands at `y = pxHeight`.
21
+ * The viewport is assumed non-degenerate (`yMax > yMin`); callers feed a
22
+ * non-empty bar window. Ported verbatim from the canvas2d adapter's
23
+ * `render/coords.ts`.
24
+ *
25
+ * @since 1.3
26
+ * @stable
27
+ * @example
28
+ * const y = priceToY(105, { xMin: 0, xMax: 1, yMin: 100, yMax: 110, pxWidth: 1, pxHeight: 100 });
29
+ * // y === 50
30
+ * void y;
31
+ */
32
+ export declare function priceToY(price: number, view: Viewport): number;
33
+ /**
34
+ * Project a world `(time, price)` point to pixel `(x, y)` space by
35
+ * composing {@link timeToX} and {@link priceToY}. Supersedes the
36
+ * canvas2d adapter's `worldPointToCanvas`. Off-screen points are NOT
37
+ * clipped — finite world inputs map to finite (possibly out-of-range)
38
+ * pixels and the renderer clips at the stroke / fill boundary.
39
+ *
40
+ * @since 1.3
41
+ * @stable
42
+ * @example
43
+ * declare const view: Viewport;
44
+ * const anchor: WorldPoint = { time: 1_700_000_000_000, price: 105 };
45
+ * const px = worldPointToPixel(anchor, view);
46
+ * // px.x === timeToX(anchor.time, view); px.y === priceToY(anchor.price, view)
47
+ * void px;
48
+ */
49
+ export declare function worldPointToPixel(p: WorldPoint, view: Viewport): Point2;
50
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/geometry/project.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE/D,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAK5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAI9D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEvE"}
@@ -0,0 +1,62 @@
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
+ * Map a world time (UTC ms) to an x pixel coordinate. When `xMin ===
5
+ * xMax` (single-bar viewport) the result pins to the canvas centre —
6
+ * no NaN propagation. Ported verbatim from the canvas2d adapter's
7
+ * `render/coords.ts` so every adapter projects time identically.
8
+ *
9
+ * @since 1.3
10
+ * @stable
11
+ * @example
12
+ * const x = timeToX(5, { xMin: 0, xMax: 10, yMin: 0, yMax: 1, pxWidth: 100, pxHeight: 1 });
13
+ * // x === 50
14
+ * void x;
15
+ */
16
+ export function timeToX(time, view) {
17
+ const span = view.xMax - view.xMin;
18
+ if (span === 0)
19
+ return view.pxWidth / 2;
20
+ const normalised = (time - view.xMin) / span;
21
+ return normalised * view.pxWidth;
22
+ }
23
+ /**
24
+ * Map a world price to a y pixel coordinate. The y axis is flipped
25
+ * (canvas y grows downward, prices grow upward), so a price at
26
+ * `view.yMax` lands at `y = 0` and `view.yMin` lands at `y = pxHeight`.
27
+ * The viewport is assumed non-degenerate (`yMax > yMin`); callers feed a
28
+ * non-empty bar window. Ported verbatim from the canvas2d adapter's
29
+ * `render/coords.ts`.
30
+ *
31
+ * @since 1.3
32
+ * @stable
33
+ * @example
34
+ * const y = priceToY(105, { xMin: 0, xMax: 1, yMin: 100, yMax: 110, pxWidth: 1, pxHeight: 100 });
35
+ * // y === 50
36
+ * void y;
37
+ */
38
+ export function priceToY(price, view) {
39
+ const span = view.yMax - view.yMin;
40
+ const normalised = (price - view.yMin) / span;
41
+ return view.pxHeight - normalised * view.pxHeight;
42
+ }
43
+ /**
44
+ * Project a world `(time, price)` point to pixel `(x, y)` space by
45
+ * composing {@link timeToX} and {@link priceToY}. Supersedes the
46
+ * canvas2d adapter's `worldPointToCanvas`. Off-screen points are NOT
47
+ * clipped — finite world inputs map to finite (possibly out-of-range)
48
+ * pixels and the renderer clips at the stroke / fill boundary.
49
+ *
50
+ * @since 1.3
51
+ * @stable
52
+ * @example
53
+ * declare const view: Viewport;
54
+ * const anchor: WorldPoint = { time: 1_700_000_000_000, price: 105 };
55
+ * const px = worldPointToPixel(anchor, view);
56
+ * // px.x === timeToX(anchor.time, view); px.y === priceToY(anchor.price, view)
57
+ * void px;
58
+ */
59
+ export function worldPointToPixel(p, view) {
60
+ return { x: timeToX(p.time, view), y: priceToY(p.price, view) };
61
+ }
62
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/geometry/project.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAM/D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAc;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACnC,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7C,OAAO,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,IAAc;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACnC,MAAM,UAAU,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9C,OAAO,IAAI,CAAC,QAAQ,GAAG,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAAa,EAAE,IAAc;IAC3D,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;AACpE,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\nimport type { WorldPoint } from \"@invinite-org/chartlang-core\";\n\nimport type { Point2, Viewport } from \"./types.js\";\n\n/**\n * Map a world time (UTC ms) to an x pixel coordinate. When `xMin ===\n * xMax` (single-bar viewport) the result pins to the canvas centre —\n * no NaN propagation. Ported verbatim from the canvas2d adapter's\n * `render/coords.ts` so every adapter projects time identically.\n *\n * @since 1.3\n * @stable\n * @example\n * const x = timeToX(5, { xMin: 0, xMax: 10, yMin: 0, yMax: 1, pxWidth: 100, pxHeight: 1 });\n * // x === 50\n * void x;\n */\nexport function timeToX(time: number, view: Viewport): number {\n const span = view.xMax - view.xMin;\n if (span === 0) return view.pxWidth / 2;\n const normalised = (time - view.xMin) / span;\n return normalised * view.pxWidth;\n}\n\n/**\n * Map a world price to a y pixel coordinate. The y axis is flipped\n * (canvas y grows downward, prices grow upward), so a price at\n * `view.yMax` lands at `y = 0` and `view.yMin` lands at `y = pxHeight`.\n * The viewport is assumed non-degenerate (`yMax > yMin`); callers feed a\n * non-empty bar window. Ported verbatim from the canvas2d adapter's\n * `render/coords.ts`.\n *\n * @since 1.3\n * @stable\n * @example\n * const y = priceToY(105, { xMin: 0, xMax: 1, yMin: 100, yMax: 110, pxWidth: 1, pxHeight: 100 });\n * // y === 50\n * void y;\n */\nexport function priceToY(price: number, view: Viewport): number {\n const span = view.yMax - view.yMin;\n const normalised = (price - view.yMin) / span;\n return view.pxHeight - normalised * view.pxHeight;\n}\n\n/**\n * Project a world `(time, price)` point to pixel `(x, y)` space by\n * composing {@link timeToX} and {@link priceToY}. Supersedes the\n * canvas2d adapter's `worldPointToCanvas`. Off-screen points are NOT\n * clipped — finite world inputs map to finite (possibly out-of-range)\n * pixels and the renderer clips at the stroke / fill boundary.\n *\n * @since 1.3\n * @stable\n * @example\n * declare const view: Viewport;\n * const anchor: WorldPoint = { time: 1_700_000_000_000, price: 105 };\n * const px = worldPointToPixel(anchor, view);\n * // px.x === timeToX(anchor.time, view); px.y === priceToY(anchor.price, view)\n * void px;\n */\nexport function worldPointToPixel(p: WorldPoint, view: Viewport): Point2 {\n return { x: timeToX(p.time, view), y: priceToY(p.price, view) };\n}\n"]}