@litfamily/litopencode 1.0.31 → 1.0.33

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 (282) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README-Ko-KR.md +6 -6
  3. package/README.md +6 -6
  4. package/TRADEMARK.md +12 -0
  5. package/dist/activation-primary-prompts.js +5 -1
  6. package/dist/activation-probe.js +1 -0
  7. package/dist/activation-prompt-utils.d.ts +1 -1
  8. package/dist/activation-reentry.d.ts +20 -0
  9. package/dist/activation-reentry.js +110 -1
  10. package/dist/activation-routing.d.ts +2 -1
  11. package/dist/activation-routing.js +100 -11
  12. package/dist/activation-workflow-prompts.d.ts +4 -0
  13. package/dist/activation-workflow-prompts.js +30 -0
  14. package/dist/activation.d.ts +1 -1
  15. package/dist/activation.js +44 -5
  16. package/dist/adhd-next.d.ts +51 -0
  17. package/dist/adhd-next.js +150 -0
  18. package/dist/agents/defaults.js +1 -1
  19. package/dist/cli/managed-skill-assets.d.ts +1 -1
  20. package/dist/cli/managed-skill-assets.js +7 -1
  21. package/dist/cli/native-skill-integrity.js +1 -1
  22. package/dist/cli.js +7 -1
  23. package/dist/commands.d.ts +28 -1
  24. package/dist/commands.js +58 -5
  25. package/dist/durable-plan.d.ts +5 -1
  26. package/dist/durable-plan.js +14 -2
  27. package/dist/features.d.ts +80 -1
  28. package/dist/features.js +105 -1
  29. package/dist/hooks.js +3 -0
  30. package/dist/ledger.js +12 -1
  31. package/dist/plan-scope-roots.d.ts +23 -0
  32. package/dist/plan-scope-roots.js +116 -0
  33. package/dist/restart-notice.d.ts +7 -0
  34. package/dist/restart-notice.js +40 -0
  35. package/dist/secret-shapes.js +5 -5
  36. package/dist/server.js +4 -0
  37. package/dist/session-liveness.d.ts +20 -0
  38. package/dist/session-liveness.js +99 -0
  39. package/dist/skills.d.ts +22 -1
  40. package/dist/skills.js +36 -0
  41. package/dist/start-work-typed.d.ts +34 -0
  42. package/dist/start-work-typed.js +167 -0
  43. package/dist/state.d.ts +3 -0
  44. package/dist/state.js +3 -0
  45. package/dist/tools.js +22 -2
  46. package/dist/tui/mission-band.d.ts +22 -1
  47. package/dist/tui/mission-band.js +75 -16
  48. package/dist/tui/mission-looks.d.ts +38 -0
  49. package/dist/tui/mission-looks.js +73 -0
  50. package/dist/tui/mission-model.d.ts +2 -0
  51. package/dist/tui/mission-model.js +3 -1
  52. package/dist/tui/mission-rainbow.d.ts +33 -0
  53. package/dist/tui/mission-rainbow.js +90 -0
  54. package/dist/tui/mission-source.d.ts +49 -0
  55. package/dist/tui/mission-source.js +217 -2
  56. package/dist/tui/mission-text.d.ts +2 -0
  57. package/dist/tui/mission-text.js +9 -1
  58. package/dist/tui/mission-threads.d.ts +58 -0
  59. package/dist/tui/mission-threads.js +128 -0
  60. package/dist/tui.js +112 -7
  61. package/dist/uiux-visual-catalog.js +8 -1
  62. package/dist/v2/events.js +3 -0
  63. package/dist/v2/session-hooks.js +38 -5
  64. package/dist/workflow-stack.d.ts +53 -0
  65. package/dist/workflow-stack.js +196 -0
  66. package/docs/assets/readme/badge-version.svg +1 -1
  67. package/docs/assets/skills/lit-schematic.webp +0 -0
  68. package/docs/reference-Ko-KR.md +48 -3
  69. package/docs/reference.md +54 -3
  70. package/package.json +1 -1
  71. package/skills/frontend-ui-ux/SKILL.md +3 -2
  72. package/skills/frontend-ui-ux/kit/components.css +414 -0
  73. package/skills/frontend-ui-ux/kit/fonts.json +22 -0
  74. package/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
  75. package/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
  76. package/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
  77. package/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
  78. package/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
  79. package/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
  80. package/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
  81. package/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
  82. package/skills/frontend-ui-ux/kit/ui.js +226 -0
  83. package/skills/frontend-ui-ux/references/brand-and-imagery.md +2 -2
  84. package/skills/frontend-ui-ux/references/complete-contract.md +2 -1
  85. package/skills/frontend-ui-ux/references/craft-floor.md +6 -2
  86. package/skills/frontend-ui-ux/references/default-editorial-pixel.json +3 -0
  87. package/skills/frontend-ui-ux/references/house-styles.md +236 -0
  88. package/skills/frontend-ui-ux/references/motion-guide.md +2 -2
  89. package/skills/frontend-ui-ux/references/production.md +3 -1
  90. package/skills/frontend-ui-ux/references/slop-register.md +4 -4
  91. package/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
  92. package/skills/frontend-ui-ux/scripts/pixel-art.mjs +60 -0
  93. package/skills/frontend-ui-ux/scripts/probe-lock.mjs +235 -0
  94. package/skills/frontend-ui-ux/scripts/probe-page.mjs +34 -12
  95. package/skills/frontend-ui-ux/scripts/probe-static.mjs +16 -3
  96. package/skills/frontend-ui-ux/scripts/probe-thresholds.json +8 -2
  97. package/skills/frontend-ui-ux/scripts/probe.mjs +35 -7
  98. package/skills/lit-adhd/SKILL.md +209 -0
  99. package/skills/lit-adhd/references/brief-format.md +46 -0
  100. package/skills/lit-adhd/references/card-examples.md +103 -0
  101. package/skills/lit-adhd/references/status-taxonomy.md +34 -0
  102. package/skills/lit-adhd/scripts/rewind.mjs +933 -0
  103. package/skills/lit-diagram-drawer/SKILL.md +4 -3
  104. package/skills/lit-diagram-drawer/references/style-guide.md +20 -11
  105. package/skills/lit-diagram-drawer/references/type-architecture.md +3 -3
  106. package/skills/lit-diagram-drawer/references/type-dependency.md +1 -1
  107. package/skills/lit-diagram-drawer/references/type-tree.md +3 -3
  108. package/skills/lit-diagram-drawer/references/type-wardley.md +1 -1
  109. package/skills/lit-diagram-drawer/references/verifier-guide.md +1 -0
  110. package/skills/lit-diagram-drawer/scripts/craft-thresholds.json +1 -2
  111. package/skills/lit-diagram-drawer/scripts/visual-quality.mjs +1 -2
  112. package/skills/lit-docx/SKILL.md +2 -1
  113. package/skills/lit-handoff/SKILL.md +2 -0
  114. package/skills/lit-hijack/SKILL.md +422 -0
  115. package/skills/lit-hijack/references/capture-and-provenance.md +87 -0
  116. package/skills/lit-hijack/references/clean-room-build.md +111 -0
  117. package/skills/lit-hijack/references/distil-and-licence.md +100 -0
  118. package/skills/lit-hijack/references/gap-and-port-design.md +126 -0
  119. package/skills/lit-hijack/references/parity-and-report.md +110 -0
  120. package/skills/lit-hijack/scripts/hijack.mjs +993 -0
  121. package/skills/lit-plan/SKILL.md +8 -0
  122. package/skills/lit-pptx/SKILL.md +27 -11
  123. package/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
  124. package/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
  125. package/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
  126. package/skills/lit-pptx/fonts/jetbrains-mono/SOURCE.md +17 -0
  127. package/skills/lit-pptx/references/density-and-fill.md +9 -3
  128. package/skills/lit-pptx/references/direction-step.md +21 -6
  129. package/skills/lit-pptx/references/examples/04-lecture-chalk-ko.md +3 -6
  130. package/skills/lit-pptx/references/examples/05-research-talk-paper-en.md +1 -5
  131. package/skills/lit-pptx/references/examples/08-reliability-keynote-night-en.md +0 -4
  132. package/skills/lit-pptx/references/examples/09-status-update-ledger-en.md +2 -2
  133. package/skills/lit-pptx/references/examples/11-forecast-review-night-ko.md +4 -7
  134. package/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +173 -0
  135. package/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +154 -0
  136. package/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +139 -0
  137. package/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +158 -0
  138. package/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +179 -0
  139. package/skills/lit-pptx/references/layout-families.md +65 -47
  140. package/skills/lit-pptx/references/title-treatments.md +83 -134
  141. package/skills/lit-pptx/references/tonalities/atlas.md +3 -3
  142. package/skills/lit-pptx/references/tonalities/bauhaus.md +140 -0
  143. package/skills/lit-pptx/references/tonalities/chalk.md +7 -6
  144. package/skills/lit-pptx/references/tonalities/computational-brutalism.md +150 -0
  145. package/skills/lit-pptx/references/tonalities/gazette.md +1 -1
  146. package/skills/lit-pptx/references/tonalities/ledger.md +1 -1
  147. package/skills/lit-pptx/references/tonalities/neo-brutalism.md +161 -0
  148. package/skills/lit-pptx/references/tonalities/newsroom.md +144 -0
  149. package/skills/lit-pptx/references/tonalities/night.md +1 -1
  150. package/skills/lit-pptx/references/tonalities/paper.md +2 -2
  151. package/skills/lit-pptx/references/tonalities/riso.md +137 -0
  152. package/skills/lit-pptx/references/tonalities/signal.md +3 -3
  153. package/skills/lit-pptx/references/tonalities/studio.md +7 -6
  154. package/skills/lit-pptx/run.mjs +2 -1
  155. package/skills/lit-pptx/scripts/compile-deck.js +1 -1
  156. package/skills/lit-pptx/scripts/craft-thresholds.json +4 -8
  157. package/skills/lit-pptx/scripts/craft_extras.py +56 -3
  158. package/skills/lit-pptx/scripts/deck_output.py +75 -90
  159. package/skills/lit-pptx/scripts/embed_fonts.py +13 -1
  160. package/skills/lit-pptx/scripts/layout_inventory.py +12 -5
  161. package/skills/lit-pptx/scripts/lib/grid-resolver.js +101 -110
  162. package/skills/lit-pptx/scripts/lib/layout-resolver.js +9 -15
  163. package/skills/lit-pptx/scripts/lib/render-pack.js +373 -566
  164. package/skills/lit-pptx/scripts/lib/slide-grid.js +104 -0
  165. package/skills/lit-pptx/scripts/lib/template-registry.js +57 -6
  166. package/skills/lit-pptx/scripts/qa_deck.py +10 -11
  167. package/skills/lit-pptx/scripts/render_pages.py +118 -0
  168. package/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +1 -1
  169. package/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +98 -0
  170. package/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +1 -1
  171. package/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
  172. package/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +1 -1
  173. package/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +1 -1
  174. package/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
  175. package/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
  176. package/skills/lit-pptx/templates/tonalities/night/pack.yaml +1 -1
  177. package/skills/lit-pptx/templates/tonalities/paper/pack.yaml +1 -1
  178. package/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
  179. package/skills/lit-pptx/templates/tonalities/signal/pack.yaml +2 -2
  180. package/skills/lit-pptx/templates/tonalities/studio/pack.yaml +1 -1
  181. package/skills/lit-recap/SKILL.md +4 -0
  182. package/skills/lit-schematic/SKILL.md +118 -0
  183. package/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +38 -0
  184. package/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +133 -0
  185. package/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +87 -0
  186. package/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +85 -0
  187. package/skills/lit-schematic/references/cutaway-outputs.md +70 -0
  188. package/skills/lit-schematic/references/isometric-craft.md +101 -0
  189. package/skills/lit-schematic/references/journal-figures.md +72 -0
  190. package/skills/lit-schematic/references/kit-api.md +134 -0
  191. package/skills/lit-schematic/references/pfd-and-layout.md +53 -0
  192. package/skills/lit-schematic/references/pid-drafting.md +103 -0
  193. package/skills/lit-schematic/references/pid-symbols.md +163 -0
  194. package/skills/lit-schematic/references/verification.md +100 -0
  195. package/skills/lit-schematic/scripts/doctor.mjs +57 -0
  196. package/skills/lit-schematic/scripts/lib/figure.mjs +81 -0
  197. package/skills/lit-schematic/scripts/lib/fonts.mjs +25 -0
  198. package/skills/lit-schematic/scripts/lib/geom.mjs +113 -0
  199. package/skills/lit-schematic/scripts/lib/interactive.mjs +292 -0
  200. package/skills/lit-schematic/scripts/lib/isa.mjs +115 -0
  201. package/skills/lit-schematic/scripts/lib/iso.mjs +719 -0
  202. package/skills/lit-schematic/scripts/lib/legend-sheet.mjs +162 -0
  203. package/skills/lit-schematic/scripts/lib/pid.mjs +937 -0
  204. package/skills/lit-schematic/scripts/lib/png.mjs +132 -0
  205. package/skills/lit-schematic/scripts/lib/qa-core.mjs +658 -0
  206. package/skills/lit-schematic/scripts/lib/route.mjs +218 -0
  207. package/skills/lit-schematic/scripts/lib/sheet.mjs +890 -0
  208. package/skills/lit-schematic/scripts/lib/svg.mjs +82 -0
  209. package/skills/lit-schematic/scripts/lib/theme.mjs +115 -0
  210. package/skills/lit-schematic/scripts/motion.mjs +129 -0
  211. package/skills/lit-schematic/scripts/qa.mjs +92 -0
  212. package/skills/lit-schematic/scripts/render.mjs +233 -0
  213. package/skills/lit-schematic/scripts/scaffold.mjs +60 -0
  214. package/skills/lit-scientific-visualization/SKILL.md +27 -4
  215. package/skills/lit-typographic-motion/SKILL.md +19 -17
  216. package/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +9 -0
  217. package/skills/lit-typographic-motion/engine/accents.mjs +6 -0
  218. package/skills/lit-typographic-motion/engine/assets.mjs +282 -0
  219. package/skills/lit-typographic-motion/engine/blender/runner.py +228 -0
  220. package/skills/lit-typographic-motion/engine/blender.mjs +126 -0
  221. package/skills/lit-typographic-motion/engine/board.mjs +138 -0
  222. package/skills/lit-typographic-motion/engine/cover/assets.mjs +11 -8
  223. package/skills/lit-typographic-motion/engine/cover/capture.mjs +6 -6
  224. package/skills/lit-typographic-motion/engine/cover/render.mjs +20 -7
  225. package/skills/lit-typographic-motion/engine/cover/treatment.mjs +30 -3
  226. package/skills/lit-typographic-motion/engine/fill.mjs +54 -0
  227. package/skills/lit-typographic-motion/engine/gallery.mjs +118 -0
  228. package/skills/lit-typographic-motion/engine/house.mjs +42 -0
  229. package/skills/lit-typographic-motion/engine/look.mjs +65 -8
  230. package/skills/lit-typographic-motion/engine/loudness.mjs +101 -0
  231. package/skills/lit-typographic-motion/engine/scaffold.mjs +219 -0
  232. package/skills/lit-typographic-motion/engine/score.mjs +594 -0
  233. package/skills/lit-typographic-motion/engine/sound.mjs +9 -57
  234. package/skills/lit-typographic-motion/engine/stage/capture.mjs +23 -9
  235. package/skills/lit-typographic-motion/engine/stage/clock.js +8 -2
  236. package/skills/lit-typographic-motion/engine/stage/cover-init.js +12 -1
  237. package/skills/lit-typographic-motion/engine/stage/frame-worker.mjs +15 -7
  238. package/skills/lit-typographic-motion/engine/stage/gate.mjs +48 -4
  239. package/skills/lit-typographic-motion/engine/stage/qa-page.js +16 -5
  240. package/skills/lit-typographic-motion/engine/stage/qa.mjs +148 -7
  241. package/skills/lit-typographic-motion/engine/stage/render.mjs +69 -7
  242. package/skills/lit-typographic-motion/engine/stage/scan.mjs +20 -1
  243. package/skills/lit-typographic-motion/engine/stage/stage-kit.js +85 -4
  244. package/skills/lit-typographic-motion/engine/stills.mjs +1 -1
  245. package/skills/lit-typographic-motion/engine/styles.mjs +221 -0
  246. package/skills/lit-typographic-motion/engine/text.mjs +1 -1
  247. package/skills/lit-typographic-motion/engine/treatment.mjs +190 -6
  248. package/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
  249. package/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
  250. package/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
  251. package/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
  252. package/skills/lit-typographic-motion/gate.mjs +18 -2
  253. package/skills/lit-typographic-motion/probe.mjs +13 -1
  254. package/skills/lit-typographic-motion/recipes/hero-3d/expected.json +64 -0
  255. package/skills/lit-typographic-motion/recipes/hero-3d/index.html +132 -0
  256. package/skills/lit-typographic-motion/recipes/orbit-loop/expected.json +2 -0
  257. package/skills/lit-typographic-motion/recipes/painted-light/expected.json +2 -0
  258. package/skills/lit-typographic-motion/recipes/weight-tide/expected.json +2 -0
  259. package/skills/lit-typographic-motion/references/cover.md +56 -13
  260. package/skills/lit-typographic-motion/references/craft-loop.md +20 -7
  261. package/skills/lit-typographic-motion/references/depth.md +112 -0
  262. package/skills/lit-typographic-motion/references/runtime.md +21 -6
  263. package/skills/lit-typographic-motion/references/sound.md +76 -0
  264. package/skills/lit-typographic-motion/references/stage.md +50 -7
  265. package/skills/lit-typographic-motion/references/studio.md +162 -0
  266. package/skills/lit-typographic-motion/references/style-bibles.md +18 -0
  267. package/skills/lit-typographic-motion/references/treatment.md +108 -9
  268. package/skills/lit-typographic-motion/render.mjs +60 -3
  269. package/skills/lit-typographic-motion/styles/paper.json +185 -0
  270. package/skills/lit-typographic-motion/styles/swiss-signal.json +188 -0
  271. package/skills/lit-typographic-motion/styles/terminalcore.json +182 -0
  272. package/skills/lit-typographic-motion/styles/tidal.json +187 -0
  273. package/skills/litresearch/SKILL.md +2 -0
  274. package/skills/managed-skill-manifest.json +517 -88
  275. package/skills/readme-studio/SKILL.md +1 -1
  276. package/skills/review-work/SKILL.md +2 -0
  277. package/skills/start-work/SKILL.md +8 -0
  278. package/skills/workflow-loop/SKILL.md +4 -0
  279. package/tools/check-pack-payload.mjs +5 -2
  280. package/tools/payload-substance-parity.json +4 -3
  281. package/tools/qa-cover-recipes.mjs +4 -3
  282. package/tools/version-manifests.json +2 -2
@@ -0,0 +1,218 @@
1
+ // Orthogonal line routing for P&IDs, PFDs and 2D apparatus panels.
2
+ //
3
+ // Drafting rules it encodes: a line leaves its port straight out along the port's normal
4
+ // before it turns; it never enters a symbol other than at its own ports; it turns as little
5
+ // as possible; it may cross another line only at a right angle on a straight run, never turn
6
+ // on top of another line and never run along one. A failed search throws instead of drawing
7
+ // through a symbol, so the caller moves a symbol or gives waypoints.
8
+
9
+ import { EPS, dist, pointIn, simplify } from "./geom.mjs";
10
+
11
+ const DIRS = [[1, 0], [0, 1], [-1, 0], [0, -1]];
12
+ const dirIndex = ([dx, dy]) => DIRS.findIndex(([x, y]) => Math.abs(x - dx) < EPS && Math.abs(y - dy) < EPS);
13
+
14
+ class Heap {
15
+ constructor() { this.items = []; }
16
+ get size() { return this.items.length; }
17
+ push(item) {
18
+ const items = this.items;
19
+ items.push(item);
20
+ let i = items.length - 1;
21
+ while (i > 0) {
22
+ const parent = (i - 1) >> 1;
23
+ if (less(items[parent], items[i])) break;
24
+ [items[parent], items[i]] = [items[i], items[parent]];
25
+ i = parent;
26
+ }
27
+ }
28
+ pop() {
29
+ const items = this.items;
30
+ const top = items[0];
31
+ const last = items.pop();
32
+ if (items.length) {
33
+ items[0] = last;
34
+ let i = 0;
35
+ for (;;) {
36
+ const l = 2 * i + 1;
37
+ const r = l + 1;
38
+ let m = i;
39
+ if (l < items.length && less(items[l], items[m])) m = l;
40
+ if (r < items.length && less(items[r], items[m])) m = r;
41
+ if (m === i) break;
42
+ [items[m], items[i]] = [items[i], items[m]];
43
+ i = m;
44
+ }
45
+ }
46
+ return top;
47
+ }
48
+ }
49
+ const less = (a, b) => (a.f !== b.f ? a.f < b.f : a.seq < b.seq);
50
+
51
+ function axisValues(lo, hi, step, extra) {
52
+ const values = new Set();
53
+ const first = Math.ceil(lo / step) * step;
54
+ for (let v = first; v <= hi + EPS; v += step) values.add(Math.round(v * 1000) / 1000);
55
+ for (const v of extra) if (v >= lo - EPS && v <= hi + EPS) values.add(Math.round(v * 1000) / 1000);
56
+ return [...values].sort((a, b) => a - b);
57
+ }
58
+
59
+ // Existing lines as axis-aligned runs, so a candidate move can be classified as a clean
60
+ // crossing, an overlap or a turn on a line.
61
+ function occupancy(lines) {
62
+ const horizontal = [];
63
+ const vertical = [];
64
+ for (const points of lines) {
65
+ for (let i = 1; i < points.length; i++) {
66
+ const [a, b] = [points[i - 1], points[i]];
67
+ if (Math.abs(a[1] - b[1]) < EPS) horizontal.push({ y: a[1], x0: Math.min(a[0], b[0]), x1: Math.max(a[0], b[0]) });
68
+ else if (Math.abs(a[0] - b[0]) < EPS) vertical.push({ x: a[0], y0: Math.min(a[1], b[1]), y1: Math.max(a[1], b[1]) });
69
+ }
70
+ }
71
+ return {
72
+ onHorizontal: ([x, y], margin = 0) => horizontal.some((s) => Math.abs(s.y - y) < margin + EPS && x >= s.x0 - EPS && x <= s.x1 + EPS),
73
+ onVertical: ([x, y], margin = 0) => vertical.some((s) => Math.abs(s.x - x) < margin + EPS && y >= s.y0 - EPS && y <= s.y1 + EPS),
74
+ };
75
+ }
76
+
77
+ export function routeOrthogonal({
78
+ from,
79
+ to,
80
+ obstacles = [],
81
+ lines = [],
82
+ bounds,
83
+ grid = 2.5,
84
+ clearance = 2,
85
+ stub = 5,
86
+ bendCost = 9,
87
+ crossCost = 4,
88
+ nearCost = 3,
89
+ exempt = [],
90
+ }) {
91
+ if (!bounds) throw new Error("routeOrthogonal: bounds are required");
92
+ const startDir = dirIndex(from.dir ?? [1, 0]);
93
+ const endDir = dirIndex(to.dir ?? [-1, 0]);
94
+ if (startDir < 0 || endDir < 0) throw new Error("routeOrthogonal: port directions must be axis-aligned unit vectors");
95
+ const inflated = obstacles.map((box) => [box[0] - clearance, box[1] - clearance, box[2] + clearance, box[3] + clearance]);
96
+ const blockedPoint = (p) => inflated.some((box) => pointIn(p, box, EPS));
97
+ // The stub runs out along the port normal until it clears every symbol's clearance zone,
98
+ // its own included: a nozzle on a curved head sits inside its vessel's bounding box.
99
+ const stubEnd = (port, d, label) => {
100
+ for (let k = stub; k <= stub + 80; k += 0.5) {
101
+ const p = [port.x + DIRS[d][0] * k, port.y + DIRS[d][1] * k];
102
+ if (!blockedPoint(p)) return p;
103
+ }
104
+ throw new Error(`routeOrthogonal: the ${label} port at (${port.x}, ${port.y}) faces into another symbol; turn the port or move the symbol`);
105
+ };
106
+ // Two ports that face each other on one axis with nothing between them join straight.
107
+ if ((startDir + 2) % 4 === endDir) {
108
+ const along = [to.x - from.x, to.y - from.y];
109
+ const straight = DIRS[startDir][0] === 0 ? Math.abs(along[0]) < EPS : Math.abs(along[1]) < EPS;
110
+ const ahead = along[0] * DIRS[startDir][0] + along[1] * DIRS[startDir][1] > 0;
111
+ if (straight && ahead) {
112
+ let clear = true;
113
+ const length = Math.hypot(along[0], along[1]);
114
+ for (let k = 0.5; k < length - 0.5 && clear; k += 0.5) {
115
+ const p = [from.x + DIRS[startDir][0] * k, from.y + DIRS[startDir][1] * k];
116
+ if (obstacles.some((box) => !exempt.includes(box) && pointIn(p, box, EPS))) clear = false;
117
+ }
118
+ if (clear) return [[from.x, from.y], [to.x, to.y]];
119
+ }
120
+ }
121
+ const s = stubEnd(from, startDir, "start");
122
+ const t = stubEnd(to, endDir, "end");
123
+ const xs = axisValues(bounds[0], bounds[2], grid, [s[0], t[0], from.x, to.x]);
124
+ const ys = axisValues(bounds[1], bounds[3], grid, [s[1], t[1], from.y, to.y]);
125
+ const xi = new Map(xs.map((v, i) => [v, i]));
126
+ const yi = new Map(ys.map((v, i) => [v, i]));
127
+ const key = (ix, iy) => iy * xs.length + ix;
128
+ const occ = occupancy(lines);
129
+ const sx = xi.get(Math.round(s[0] * 1000) / 1000);
130
+ const sy = yi.get(Math.round(s[1] * 1000) / 1000);
131
+ const tx = xi.get(Math.round(t[0] * 1000) / 1000);
132
+ const ty = yi.get(Math.round(t[1] * 1000) / 1000);
133
+ const arrive = (endDir + 2) % 4;
134
+ const heap = new Heap();
135
+ const best = new Map();
136
+ const came = new Map();
137
+ let seq = 0;
138
+ const h = (ix, iy) => Math.abs(xs[ix] - t[0]) + Math.abs(ys[iy] - t[1]);
139
+ heap.push({ ix: sx, iy: sy, d: startDir, g: 0, f: h(sx, sy), seq: seq++ });
140
+ best.set(`${key(sx, sy)}:${startDir}`, 0);
141
+ let goal = null;
142
+ while (heap.size) {
143
+ const cur = heap.pop();
144
+ const stateKey = `${key(cur.ix, cur.iy)}:${cur.d}`;
145
+ if (cur.g > (best.get(stateKey) ?? Infinity) + EPS) continue;
146
+ if (cur.ix === tx && cur.iy === ty) {
147
+ const finalCost = cur.g + (cur.d === arrive ? 0 : bendCost);
148
+ if (!goal || finalCost < goal.cost) goal = { state: stateKey, cost: finalCost };
149
+ if (cur.d === arrive) break;
150
+ continue;
151
+ }
152
+ const here = [xs[cur.ix], ys[cur.iy]];
153
+ for (let d = 0; d < 4; d++) {
154
+ if (d === (cur.d + 2) % 4) continue;
155
+ const nx = cur.ix + DIRS[d][0];
156
+ const ny = cur.iy + DIRS[d][1];
157
+ if (nx < 0 || ny < 0 || nx >= xs.length || ny >= ys.length) continue;
158
+ const next = [xs[nx], ys[ny]];
159
+ const mid = [(here[0] + next[0]) / 2, (here[1] + next[1]) / 2];
160
+ if (blockedPoint(next) || blockedPoint(mid)) continue;
161
+ const horizontalMove = d % 2 === 0;
162
+ // Running along an existing line, or turning on top of one, is never allowed.
163
+ if (horizontalMove ? occ.onHorizontal(mid) : occ.onVertical(mid)) continue;
164
+ const turning = d !== cur.d;
165
+ const isEndpoint = (cur.ix === sx && cur.iy === sy);
166
+ if (turning && !isEndpoint && (occ.onHorizontal(here) || occ.onVertical(here))) continue;
167
+ let step = dist(here, next);
168
+ if (horizontalMove ? occ.onVertical(next) : occ.onHorizontal(next)) {
169
+ const atGoal = nx === tx && ny === ty;
170
+ if (atGoal) continue;
171
+ step += crossCost;
172
+ }
173
+ if (horizontalMove ? occ.onHorizontal(mid, grid * 0.99) : occ.onVertical(mid, grid * 0.99)) step += nearCost;
174
+ const g = cur.g + step + (turning ? bendCost : 0);
175
+ const nextKey = `${key(nx, ny)}:${d}`;
176
+ if (g + EPS < (best.get(nextKey) ?? Infinity)) {
177
+ best.set(nextKey, g);
178
+ came.set(nextKey, stateKey);
179
+ heap.push({ ix: nx, iy: ny, d, g, f: g + h(nx, ny), seq: seq++ });
180
+ }
181
+ }
182
+ }
183
+ if (!goal) {
184
+ throw new Error(`routeOrthogonal: no clear route from (${from.x}, ${from.y}) to (${to.x}, ${to.y}); move a symbol, widen the gap, or pass waypoints`);
185
+ }
186
+ const cells = [];
187
+ for (let k = goal.state; k; k = came.get(k)) {
188
+ const index = Number(k.split(":")[0]);
189
+ cells.push([xs[index % xs.length], ys[Math.floor(index / xs.length)]]);
190
+ if (!came.has(k)) break;
191
+ }
192
+ cells.reverse();
193
+ return simplify([[from.x, from.y], ...cells, [to.x, to.y]]);
194
+ }
195
+
196
+ // Checks a hand-given polyline: every leg axis-aligned and the first and last legs on the
197
+ // port normals. Returns the simplified corners.
198
+ export function orthogonal(points, { from, to } = {}) {
199
+ const out = simplify(points);
200
+ for (let i = 1; i < out.length; i++) {
201
+ const [a, b] = [out[i - 1], out[i]];
202
+ if (Math.abs(a[0] - b[0]) > EPS && Math.abs(a[1] - b[1]) > EPS) {
203
+ throw new Error(`orthogonal: leg ${i} from (${a}) to (${b}) is diagonal; P&ID lines run horizontally or vertically`);
204
+ }
205
+ }
206
+ const check = (port, a, b, label) => {
207
+ if (!port?.dir) return;
208
+ const d = [Math.sign(b[0] - a[0]), Math.sign(b[1] - a[1])];
209
+ if (d[0] !== Math.sign(port.dir[0]) || d[1] !== Math.sign(port.dir[1])) {
210
+ throw new Error(`orthogonal: the ${label} leg must leave its port along the port's normal`);
211
+ }
212
+ };
213
+ if (out.length >= 2) {
214
+ check(from, out[0], out[1], "first");
215
+ check(to, out.at(-1), out.at(-2), "last");
216
+ }
217
+ return out;
218
+ }