@classytic/stage 0.2.0 → 0.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 (458) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +5 -1
  3. package/dist/_virtual/_rolldown/runtime.mjs +18 -0
  4. package/dist/assets/index.mjs +0 -1
  5. package/dist/atomic/aufbau.d.mts +144 -0
  6. package/dist/atomic/aufbau.mjs +384 -0
  7. package/dist/atomic/elements.d.mts +67 -0
  8. package/dist/atomic/elements.mjs +785 -0
  9. package/dist/atomic/emission.d.mts +112 -0
  10. package/dist/atomic/emission.mjs +179 -0
  11. package/dist/atomic/index.d.mts +8 -0
  12. package/dist/atomic/index.mjs +9 -0
  13. package/dist/atomic/periodic.d.mts +82 -0
  14. package/dist/atomic/periodic.mjs +231 -0
  15. package/dist/atomic/properties.d.mts +100 -0
  16. package/dist/atomic/properties.mjs +574 -0
  17. package/dist/atomic/shells.d.mts +137 -0
  18. package/dist/atomic/shells.mjs +170 -0
  19. package/dist/atomic/trends.d.mts +148 -0
  20. package/dist/atomic/trends.mjs +225 -0
  21. package/dist/builder/Palette.mjs +50 -89
  22. package/dist/builder/SceneBuilder.mjs +15 -73
  23. package/dist/chem/chromatography.d.mts +66 -0
  24. package/dist/chem/chromatography.mjs +63 -0
  25. package/dist/chem/complex.d.mts +114 -0
  26. package/dist/chem/complex.mjs +234 -0
  27. package/dist/chem/cycle.d.mts +75 -0
  28. package/dist/chem/cycle.mjs +145 -0
  29. package/dist/chem/equation.d.mts +86 -0
  30. package/dist/chem/equation.mjs +168 -0
  31. package/dist/chem/formula.d.mts +90 -0
  32. package/dist/chem/formula.mjs +201 -0
  33. package/dist/chem/functional-groups.d.mts +31 -0
  34. package/dist/chem/functional-groups.mjs +187 -0
  35. package/dist/chem/hazard.d.mts +94 -0
  36. package/dist/chem/hazard.mjs +329 -0
  37. package/dist/chem/index.d.mts +34 -33
  38. package/dist/chem/index.mjs +34 -28
  39. package/dist/chem/ionic.d.mts +98 -0
  40. package/dist/chem/ionic.mjs +156 -0
  41. package/dist/chem/layout.d.mts +46 -0
  42. package/dist/chem/layout.mjs +342 -0
  43. package/dist/chem/lewis.d.mts +79 -0
  44. package/dist/chem/lewis.mjs +163 -0
  45. package/dist/chem/molecular-orbital.d.mts +114 -0
  46. package/dist/chem/molecular-orbital.mjs +201 -0
  47. package/dist/chem/molecule.d.mts +154 -0
  48. package/dist/chem/molecule.mjs +282 -0
  49. package/dist/chem/nmr.d.mts +153 -0
  50. package/dist/chem/nmr.mjs +165 -0
  51. package/dist/chem/polymer.d.mts +65 -0
  52. package/dist/chem/polymer.mjs +179 -0
  53. package/dist/chem/redox.d.mts +138 -0
  54. package/dist/chem/redox.mjs +328 -0
  55. package/dist/chem/relations.d.mts +183 -0
  56. package/dist/chem/relations.mjs +299 -0
  57. package/dist/chem/skeleton.d.mts +51 -0
  58. package/dist/chem/skeleton.mjs +186 -0
  59. package/dist/chem/solution.d.mts +116 -0
  60. package/dist/chem/solution.mjs +186 -0
  61. package/dist/chem/species.d.mts +47 -0
  62. package/dist/chem/species.mjs +128 -0
  63. package/dist/chem/stereo.d.mts +108 -0
  64. package/dist/chem/stereo.mjs +286 -0
  65. package/dist/chem/stoichiometry.d.mts +95 -0
  66. package/dist/chem/stoichiometry.mjs +145 -0
  67. package/dist/chem/synthesis.d.mts +113 -0
  68. package/dist/chem/synthesis.mjs +281 -0
  69. package/dist/chem/vsepr.d.mts +80 -0
  70. package/dist/chem/vsepr.mjs +247 -0
  71. package/dist/circuit/ac.d.mts +163 -0
  72. package/dist/circuit/ac.mjs +452 -0
  73. package/dist/circuit/dc.d.mts +92 -0
  74. package/dist/circuit/dc.mjs +333 -0
  75. package/dist/circuit/index.d.mts +5 -92
  76. package/dist/circuit/index.mjs +5 -332
  77. package/dist/circuit/network.d.mts +99 -0
  78. package/dist/circuit/network.mjs +440 -0
  79. package/dist/circuit/topology.d.mts +102 -0
  80. package/dist/circuit/topology.mjs +143 -0
  81. package/dist/colour/absorption.d.mts +79 -0
  82. package/dist/colour/absorption.mjs +137 -0
  83. package/dist/colour/index.d.mts +6 -0
  84. package/dist/colour/index.mjs +7 -0
  85. package/dist/colour/name.d.mts +14 -0
  86. package/dist/colour/name.mjs +76 -0
  87. package/dist/colour/observer.d.mts +31 -0
  88. package/dist/colour/observer.mjs +46 -0
  89. package/dist/colour/spectrum.d.mts +61 -0
  90. package/dist/colour/spectrum.mjs +108 -0
  91. package/dist/colour/srgb.d.mts +49 -0
  92. package/dist/colour/srgb.mjs +108 -0
  93. package/dist/core/constants.d.mts +138 -0
  94. package/dist/core/constants.mjs +139 -0
  95. package/dist/core/index.d.mts +4 -1
  96. package/dist/core/index.mjs +4 -1
  97. package/dist/core/math.d.mts +26 -0
  98. package/dist/core/math.mjs +37 -0
  99. package/dist/core/vec3.d.mts +94 -0
  100. package/dist/core/vec3.mjs +123 -0
  101. package/dist/core/worked.d.mts +35 -0
  102. package/dist/core/worked.mjs +44 -0
  103. package/dist/electro/cell.d.mts +102 -0
  104. package/dist/electro/cell.mjs +170 -0
  105. package/dist/electro/conductivity.d.mts +157 -0
  106. package/dist/electro/conductivity.mjs +156 -0
  107. package/dist/electro/couples.d.mts +114 -0
  108. package/dist/electro/couples.mjs +334 -0
  109. package/dist/electro/electrolysis.d.mts +96 -0
  110. package/dist/electro/electrolysis.mjs +274 -0
  111. package/dist/electro/faraday.d.mts +150 -0
  112. package/dist/electro/faraday.mjs +118 -0
  113. package/dist/electro/index.d.mts +6 -0
  114. package/dist/electro/index.mjs +7 -0
  115. package/dist/finance/accounting/adjustments.d.mts +17 -0
  116. package/dist/finance/accounting/adjustments.mjs +202 -0
  117. package/dist/finance/accounting/analysis.d.mts +8 -0
  118. package/dist/finance/accounting/analysis.mjs +205 -0
  119. package/dist/finance/accounting/clubs.d.mts +21 -0
  120. package/dist/finance/accounting/clubs.mjs +217 -0
  121. package/dist/finance/accounting/company-statements.d.mts +7 -0
  122. package/dist/finance/accounting/company-statements.mjs +150 -0
  123. package/dist/finance/accounting/harness.d.mts +7 -0
  124. package/dist/finance/accounting/harness.mjs +45 -0
  125. package/dist/finance/accounting/index.d.mts +18 -0
  126. package/dist/finance/accounting/index.mjs +18 -0
  127. package/dist/finance/accounting/journalize.d.mts +16 -0
  128. package/dist/finance/accounting/journalize.mjs +128 -0
  129. package/dist/finance/accounting/manufacturing.d.mts +8 -0
  130. package/dist/finance/accounting/manufacturing.mjs +236 -0
  131. package/dist/finance/accounting/partnership.d.mts +7 -0
  132. package/dist/finance/accounting/partnership.mjs +152 -0
  133. package/dist/finance/accounting/period-case.d.mts +6 -0
  134. package/dist/finance/accounting/period-case.mjs +71 -0
  135. package/dist/finance/accounting/post.d.mts +9 -0
  136. package/dist/finance/accounting/post.mjs +84 -0
  137. package/dist/finance/accounting/precision.d.mts +9 -0
  138. package/dist/finance/accounting/precision.mjs +15 -0
  139. package/dist/finance/accounting/rules.d.mts +21 -0
  140. package/dist/finance/accounting/rules.mjs +37 -0
  141. package/dist/finance/accounting/solve.d.mts +28 -0
  142. package/dist/finance/accounting/solve.mjs +92 -0
  143. package/dist/finance/accounting/statements.d.mts +7 -0
  144. package/dist/finance/accounting/statements.mjs +142 -0
  145. package/dist/finance/accounting/types.d.mts +649 -0
  146. package/dist/finance/accounting/validate.d.mts +7 -0
  147. package/dist/finance/accounting/validate.mjs +67 -0
  148. package/dist/finance/accounting/verification.d.mts +24 -0
  149. package/dist/finance/accounting/verification.mjs +386 -0
  150. package/dist/finance/bizsim.d.mts +75 -0
  151. package/dist/finance/bizsim.mjs +296 -0
  152. package/dist/finance/index.d.mts +95 -0
  153. package/dist/finance/index.mjs +145 -0
  154. package/dist/finance/investment-math.d.mts +7 -0
  155. package/dist/finance/investment-math.mjs +18 -0
  156. package/dist/finance/management-accounting/budgeting.d.mts +7 -0
  157. package/dist/finance/management-accounting/budgeting.mjs +189 -0
  158. package/dist/finance/management-accounting/cash-budget.d.mts +7 -0
  159. package/dist/finance/management-accounting/cash-budget.mjs +154 -0
  160. package/dist/finance/management-accounting/costing.d.mts +8 -0
  161. package/dist/finance/management-accounting/costing.mjs +175 -0
  162. package/dist/finance/management-accounting/decisions.d.mts +8 -0
  163. package/dist/finance/management-accounting/decisions.mjs +164 -0
  164. package/dist/finance/management-accounting/index.d.mts +13 -0
  165. package/dist/finance/management-accounting/index.mjs +13 -0
  166. package/dist/finance/management-accounting/integrated-budget.d.mts +8 -0
  167. package/dist/finance/management-accounting/integrated-budget.mjs +67 -0
  168. package/dist/finance/management-accounting/inventory-costing.d.mts +7 -0
  169. package/dist/finance/management-accounting/inventory-costing.mjs +173 -0
  170. package/dist/finance/management-accounting/investment-appraisal.d.mts +7 -0
  171. package/dist/finance/management-accounting/investment-appraisal.mjs +148 -0
  172. package/dist/finance/management-accounting/management-case.d.mts +8 -0
  173. package/dist/finance/management-accounting/management-case.mjs +91 -0
  174. package/dist/finance/management-accounting/master-budget.d.mts +7 -0
  175. package/dist/finance/management-accounting/master-budget.mjs +137 -0
  176. package/dist/finance/management-accounting/production-costing.d.mts +8 -0
  177. package/dist/finance/management-accounting/production-costing.mjs +214 -0
  178. package/dist/finance/management-accounting/types.d.mts +644 -0
  179. package/dist/finance/management-accounting/variances.d.mts +7 -0
  180. package/dist/finance/management-accounting/variances.mjs +150 -0
  181. package/dist/index.d.mts +9 -8
  182. package/dist/index.mjs +5 -5
  183. package/dist/interaction/MovableDot.mjs +19 -0
  184. package/dist/interaction/useDraggable.mjs +24 -4
  185. package/dist/math/analysis.d.mts +61 -0
  186. package/dist/math/analysis.mjs +187 -0
  187. package/dist/math/answer-check.d.mts +50 -0
  188. package/dist/math/answer-check.mjs +106 -0
  189. package/dist/math/calculus.d.mts +17 -1
  190. package/dist/math/calculus.mjs +365 -2
  191. package/dist/math/complex.d.mts +93 -0
  192. package/dist/math/complex.mjs +239 -0
  193. package/dist/math/expr.d.mts +23 -0
  194. package/dist/math/expr.mjs +36 -0
  195. package/dist/math/index.d.mts +9 -23
  196. package/dist/math/index.mjs +9 -38
  197. package/dist/math/latex.mjs +10 -1
  198. package/dist/math/parse.mjs +1 -1
  199. package/dist/math/poly.d.mts +55 -0
  200. package/dist/math/poly.mjs +273 -0
  201. package/dist/math/rearrange.d.mts +14 -0
  202. package/dist/math/rearrange.mjs +125 -0
  203. package/dist/math/solve.d.mts +21 -0
  204. package/dist/math/solve.mjs +75 -0
  205. package/dist/networking/addressing.d.mts +26 -0
  206. package/dist/networking/addressing.mjs +33 -0
  207. package/dist/networking/cloud/access.d.mts +36 -0
  208. package/dist/networking/cloud/access.mjs +95 -0
  209. package/dist/networking/cloud/containers.d.mts +43 -0
  210. package/dist/networking/cloud/containers.mjs +54 -0
  211. package/dist/networking/cloud/edge.d.mts +17 -0
  212. package/dist/networking/cloud/edge.mjs +31 -0
  213. package/dist/networking/cloud/failure-domains.d.mts +19 -0
  214. package/dist/networking/cloud/failure-domains.mjs +22 -0
  215. package/dist/networking/cloud/geo-routing.d.mts +22 -0
  216. package/dist/networking/cloud/geo-routing.mjs +64 -0
  217. package/dist/networking/cloud/index.d.mts +18 -0
  218. package/dist/networking/cloud/index.mjs +19 -0
  219. package/dist/networking/cloud/inference-reliability.d.mts +30 -0
  220. package/dist/networking/cloud/inference-reliability.mjs +76 -0
  221. package/dist/networking/cloud/inference.d.mts +118 -0
  222. package/dist/networking/cloud/inference.mjs +179 -0
  223. package/dist/networking/cloud/infrastructure.d.mts +45 -0
  224. package/dist/networking/cloud/infrastructure.mjs +96 -0
  225. package/dist/networking/cloud/observability.d.mts +24 -0
  226. package/dist/networking/cloud/observability.mjs +34 -0
  227. package/dist/networking/cloud/orchestration.d.mts +32 -0
  228. package/dist/networking/cloud/orchestration.mjs +38 -0
  229. package/dist/networking/cloud/recovery.d.mts +21 -0
  230. package/dist/networking/cloud/recovery.mjs +27 -0
  231. package/dist/networking/cloud/releases.d.mts +42 -0
  232. package/dist/networking/cloud/releases.mjs +135 -0
  233. package/dist/networking/cloud/scaling.d.mts +25 -0
  234. package/dist/networking/cloud/scaling.mjs +65 -0
  235. package/dist/networking/cloud/secrets.d.mts +39 -0
  236. package/dist/networking/cloud/secrets.mjs +68 -0
  237. package/dist/networking/cloud/server-deployment.d.mts +29 -0
  238. package/dist/networking/cloud/server-deployment.mjs +72 -0
  239. package/dist/networking/cloud/storage.d.mts +16 -0
  240. package/dist/networking/cloud/storage.mjs +58 -0
  241. package/dist/networking/cloud/vpc.d.mts +18 -0
  242. package/dist/networking/cloud/vpc.mjs +100 -0
  243. package/dist/networking/delivery.d.mts +34 -0
  244. package/dist/networking/delivery.mjs +52 -0
  245. package/dist/networking/index.d.mts +47 -0
  246. package/dist/networking/index.mjs +47 -0
  247. package/dist/networking/ipv6.d.mts +28 -0
  248. package/dist/networking/ipv6.mjs +17 -0
  249. package/dist/networking/journeys.d.mts +83 -0
  250. package/dist/networking/journeys.mjs +161 -0
  251. package/dist/networking/models/airtime.d.mts +87 -0
  252. package/dist/networking/models/airtime.mjs +130 -0
  253. package/dist/networking/models/cellular.d.mts +157 -0
  254. package/dist/networking/models/cellular.mjs +252 -0
  255. package/dist/networking/models/encapsulation.d.mts +98 -0
  256. package/dist/networking/models/encapsulation.mjs +132 -0
  257. package/dist/networking/models/handover.d.mts +116 -0
  258. package/dist/networking/models/handover.mjs +209 -0
  259. package/dist/networking/models/handshake.d.mts +90 -0
  260. package/dist/networking/models/handshake.mjs +122 -0
  261. package/dist/networking/models/index.d.mts +14 -0
  262. package/dist/networking/models/index.mjs +15 -0
  263. package/dist/networking/models/media.d.mts +82 -0
  264. package/dist/networking/models/media.mjs +188 -0
  265. package/dist/networking/models/satellite.d.mts +86 -0
  266. package/dist/networking/models/satellite.mjs +121 -0
  267. package/dist/networking/models/spectrum.d.mts +83 -0
  268. package/dist/networking/models/spectrum.mjs +235 -0
  269. package/dist/networking/models/switch.d.mts +80 -0
  270. package/dist/networking/models/switch.mjs +155 -0
  271. package/dist/networking/models/vlan.d.mts +62 -0
  272. package/dist/networking/models/vlan.mjs +100 -0
  273. package/dist/networking/models/vpn.d.mts +66 -0
  274. package/dist/networking/models/vpn.mjs +125 -0
  275. package/dist/networking/models/wifi-privacy.d.mts +82 -0
  276. package/dist/networking/models/wifi-privacy.mjs +130 -0
  277. package/dist/networking/models/wifi.d.mts +240 -0
  278. package/dist/networking/models/wifi.mjs +562 -0
  279. package/dist/networking/nat-traversal.d.mts +24 -0
  280. package/dist/networking/nat-traversal.mjs +38 -0
  281. package/dist/networking/protocols.d.mts +17 -0
  282. package/dist/networking/protocols.mjs +57 -0
  283. package/dist/networking/queue.d.mts +12 -0
  284. package/dist/networking/queue.mjs +18 -0
  285. package/dist/networking/route-learning.d.mts +23 -0
  286. package/dist/networking/route-learning.mjs +43 -0
  287. package/dist/networking/routing.d.mts +11 -0
  288. package/dist/networking/routing.mjs +25 -0
  289. package/dist/networking/signals.d.mts +26 -0
  290. package/dist/networking/signals.mjs +35 -0
  291. package/dist/networking/subnet.d.mts +49 -0
  292. package/dist/networking/subnet.mjs +89 -0
  293. package/dist/networking/tcp-reliability.d.mts +24 -0
  294. package/dist/networking/tcp-reliability.mjs +38 -0
  295. package/dist/networking/topology/contract.d.mts +123 -0
  296. package/dist/networking/topology/index.d.mts +4 -0
  297. package/dist/networking/topology/index.mjs +4 -0
  298. package/dist/networking/topology/presets.d.mts +7 -0
  299. package/dist/networking/topology/presets.mjs +302 -0
  300. package/dist/networking/topology/simulation.d.mts +8 -0
  301. package/dist/networking/topology/simulation.mjs +179 -0
  302. package/dist/networking/wireless.d.mts +11 -0
  303. package/dist/networking/wireless.mjs +17 -0
  304. package/dist/optics/imaging.d.mts +218 -0
  305. package/dist/optics/imaging.mjs +276 -0
  306. package/dist/optics/index.d.mts +7 -0
  307. package/dist/optics/index.mjs +8 -0
  308. package/dist/optics/laws.d.mts +70 -0
  309. package/dist/optics/laws.mjs +195 -0
  310. package/dist/optics/prism.d.mts +148 -0
  311. package/dist/optics/prism.mjs +197 -0
  312. package/dist/optics/refraction.d.mts +201 -0
  313. package/dist/optics/refraction.mjs +270 -0
  314. package/dist/optics/wave-optics.d.mts +171 -0
  315. package/dist/optics/wave-optics.mjs +259 -0
  316. package/dist/physics/ac/index.d.mts +183 -0
  317. package/dist/physics/ac/index.mjs +203 -0
  318. package/dist/physics/dynamics/forces.d.mts +98 -0
  319. package/dist/physics/dynamics/forces.mjs +153 -0
  320. package/dist/physics/dynamics/friction.d.mts +76 -0
  321. package/dist/physics/dynamics/friction.mjs +119 -0
  322. package/dist/physics/dynamics/system.d.mts +68 -0
  323. package/dist/physics/dynamics/system.mjs +125 -0
  324. package/dist/physics/electrostatics/index.d.mts +169 -0
  325. package/dist/physics/electrostatics/index.mjs +249 -0
  326. package/dist/physics/energy/centre-of-mass.d.mts +115 -0
  327. package/dist/physics/energy/centre-of-mass.mjs +180 -0
  328. package/dist/physics/energy/conservation.d.mts +81 -0
  329. package/dist/physics/energy/conservation.mjs +129 -0
  330. package/dist/physics/energy/power.d.mts +90 -0
  331. package/dist/physics/energy/power.mjs +167 -0
  332. package/dist/physics/energy/spring.d.mts +73 -0
  333. package/dist/physics/energy/spring.mjs +129 -0
  334. package/dist/physics/energy/store.d.mts +63 -0
  335. package/dist/physics/energy/store.mjs +108 -0
  336. package/dist/physics/energy/theorem.d.mts +65 -0
  337. package/dist/physics/energy/theorem.mjs +113 -0
  338. package/dist/physics/energy/work.d.mts +114 -0
  339. package/dist/physics/energy/work.mjs +219 -0
  340. package/dist/physics/gravitation/body.d.mts +85 -0
  341. package/dist/physics/gravitation/body.mjs +168 -0
  342. package/dist/physics/gravitation/kepler.d.mts +127 -0
  343. package/dist/physics/gravitation/kepler.mjs +216 -0
  344. package/dist/physics/gravitation/law.d.mts +117 -0
  345. package/dist/physics/gravitation/law.mjs +175 -0
  346. package/dist/physics/gravitation/orbit.d.mts +119 -0
  347. package/dist/physics/gravitation/orbit.mjs +202 -0
  348. package/dist/physics/gravitation/potential.d.mts +114 -0
  349. package/dist/physics/gravitation/potential.mjs +202 -0
  350. package/dist/physics/gravitation/variation.d.mts +97 -0
  351. package/dist/physics/gravitation/variation.mjs +175 -0
  352. package/dist/physics/index.d.mts +35 -0
  353. package/dist/physics/index.mjs +36 -0
  354. package/dist/physics/integral-sweep.d.mts +63 -0
  355. package/dist/physics/integral-sweep.mjs +126 -0
  356. package/dist/physics/kinematics/equations.d.mts +47 -0
  357. package/dist/physics/kinematics/equations.mjs +126 -0
  358. package/dist/physics/kinematics/free-fall.d.mts +53 -0
  359. package/dist/physics/kinematics/free-fall.mjs +71 -0
  360. package/dist/physics/kinematics/given.d.mts +16 -0
  361. package/dist/physics/kinematics/given.mjs +55 -0
  362. package/dist/physics/kinematics/roots.d.mts +41 -0
  363. package/dist/physics/kinematics/roots.mjs +72 -0
  364. package/dist/physics/kinematics/solve.d.mts +52 -0
  365. package/dist/physics/kinematics/solve.mjs +172 -0
  366. package/dist/physics/magnetism/biot-savart.d.mts +161 -0
  367. package/dist/physics/magnetism/biot-savart.mjs +353 -0
  368. package/dist/physics/magnetism/flux.d.mts +94 -0
  369. package/dist/physics/magnetism/flux.mjs +313 -0
  370. package/dist/physics/magnetism/geomagnetism.d.mts +151 -0
  371. package/dist/physics/magnetism/geomagnetism.mjs +184 -0
  372. package/dist/physics/magnetism/index.d.mts +12 -0
  373. package/dist/physics/magnetism/index.mjs +74 -0
  374. package/dist/physics/magnetism/lorentz.d.mts +28 -0
  375. package/dist/physics/magnetism/lorentz.mjs +88 -0
  376. package/dist/physics/magnetism/magnetic-force.d.mts +102 -0
  377. package/dist/physics/magnetism/magnetic-force.mjs +169 -0
  378. package/dist/physics/momentum/collide.d.mts +77 -0
  379. package/dist/physics/momentum/collide.mjs +115 -0
  380. package/dist/physics/momentum/impulse.d.mts +89 -0
  381. package/dist/physics/momentum/impulse.mjs +159 -0
  382. package/dist/physics/momentum/planar.d.mts +60 -0
  383. package/dist/physics/momentum/planar.mjs +110 -0
  384. package/dist/physics/projectile.d.mts +107 -0
  385. package/dist/physics/projectile.mjs +185 -0
  386. package/dist/physics/rotation/angular.d.mts +85 -0
  387. package/dist/physics/rotation/angular.mjs +130 -0
  388. package/dist/physics/rotation/circular.d.mts +79 -0
  389. package/dist/physics/rotation/circular.mjs +131 -0
  390. package/dist/physics/rotation/inertia.d.mts +164 -0
  391. package/dist/physics/rotation/inertia.mjs +332 -0
  392. package/dist/physics/rotation/torque.d.mts +65 -0
  393. package/dist/physics/rotation/torque.mjs +134 -0
  394. package/dist/physics/vectors.d.mts +64 -0
  395. package/dist/physics/vectors.mjs +114 -0
  396. package/dist/primitives/Dot.d.mts +2 -15
  397. package/dist/primitives/Dot.mjs +6 -4
  398. package/dist/primitives/Grid.d.mts +33 -17
  399. package/dist/primitives/Grid.mjs +89 -17
  400. package/dist/primitives/Label.d.mts +1 -14
  401. package/dist/primitives/Label.mjs +3 -2
  402. package/dist/primitives/Lines.d.mts +4 -32
  403. package/dist/primitives/Lines.mjs +10 -8
  404. package/dist/primitives/Shapes.d.mts +5 -43
  405. package/dist/primitives/Shapes.mjs +12 -10
  406. package/dist/primitives/index.d.mts +2 -2
  407. package/dist/primitives/index.mjs +2 -2
  408. package/dist/primitives/props.mjs +31 -0
  409. package/dist/quantity/index.d.mts +119 -0
  410. package/dist/quantity/index.mjs +417 -0
  411. package/dist/relation/derive.d.mts +74 -0
  412. package/dist/relation/derive.mjs +73 -0
  413. package/dist/relation/index.d.mts +56 -0
  414. package/dist/relation/index.mjs +234 -0
  415. package/dist/scene/Scene.d.mts +6 -1
  416. package/dist/scene/Scene.mjs +15 -40
  417. package/dist/scene/evaluators.mjs +1 -1
  418. package/dist/sim/thermal.mjs +45 -20
  419. package/dist/thermo/calorimetry.d.mts +207 -0
  420. package/dist/thermo/calorimetry.mjs +345 -0
  421. package/dist/thermo/cycles.d.mts +142 -0
  422. package/dist/thermo/cycles.mjs +256 -0
  423. package/dist/thermo/entropy.d.mts +87 -0
  424. package/dist/thermo/entropy.mjs +140 -0
  425. package/dist/thermo/expansion.d.mts +130 -0
  426. package/dist/thermo/expansion.mjs +178 -0
  427. package/dist/thermo/gas.d.mts +197 -0
  428. package/dist/thermo/gas.mjs +279 -0
  429. package/dist/thermo/heat-transfer.d.mts +164 -0
  430. package/dist/thermo/heat-transfer.mjs +217 -0
  431. package/dist/thermo/index.d.mts +9 -82
  432. package/dist/thermo/index.mjs +9 -132
  433. package/dist/thermo/processes.d.mts +95 -0
  434. package/dist/thermo/processes.mjs +154 -0
  435. package/dist/thermo/temperature.d.mts +174 -0
  436. package/dist/thermo/temperature.mjs +236 -0
  437. package/dist/view/Stage.mjs +4 -11
  438. package/dist/waves/damping.d.mts +224 -0
  439. package/dist/waves/damping.mjs +317 -0
  440. package/dist/waves/doppler.d.mts +72 -0
  441. package/dist/waves/doppler.mjs +125 -0
  442. package/dist/waves/index.d.mts +10 -0
  443. package/dist/waves/index.mjs +10 -0
  444. package/dist/waves/pendulum.d.mts +130 -0
  445. package/dist/waves/pendulum.mjs +211 -0
  446. package/dist/waves/pitch.d.mts +125 -0
  447. package/dist/waves/pitch.mjs +158 -0
  448. package/dist/waves/shm.d.mts +170 -0
  449. package/dist/waves/shm.mjs +279 -0
  450. package/dist/waves/springs.d.mts +103 -0
  451. package/dist/waves/springs.mjs +176 -0
  452. package/dist/waves/superposition.d.mts +124 -0
  453. package/dist/waves/superposition.mjs +196 -0
  454. package/dist/waves/wave.d.mts +154 -0
  455. package/dist/waves/wave.mjs +178 -0
  456. package/package.json +103 -23
  457. package/styles.css +125 -8
  458. package/dist/assets/kit/index.mjs +0 -4
@@ -0,0 +1,440 @@
1
+ import { solveDC } from "./dc.mjs";
2
+ import { findBridgeShape, findDangling as findDangling$1, findParallelGroup, findSelfLoop, findSeriesPair, isReduced, mergedId, validateTopology } from "./topology.mjs";
3
+
4
+ //#region src/circuit/network.ts
5
+ /**
6
+ * @classytic/stage/circuit/network — Network reduction: turning a circuit nobody can read into one component, one move at a time.
7
+ *
8
+ * A student who can only apply `V = IR` is stuck the moment a question shows six resistors in a
9
+ * shape that is neither series nor parallel. The skill that unsticks them is not algebra, it is
10
+ * SEEING: noticing that two of those components share both ends, or that a node in the middle joins
11
+ * exactly two of them and nothing else, collapsing that pair, and looking again. Do it four times
12
+ * and the shape everyone was afraid of is a single number.
13
+ *
14
+ * That is the skill this kernel models, and the reason it returns a list of steps rather than an
15
+ * answer. `solveDC` in `@classytic/stage/circuit` already returns the answer, correctly, for any
16
+ * network: it assembles a modified nodal analysis matrix and inverts it. What it cannot do is
17
+ * teach, because Gaussian elimination is not how a person reduces a circuit and watching a matrix
18
+ * invert teaches nobody to look at a bridge and think "those two are in parallel".
19
+ *
20
+ * So the two engines do different jobs, and the tests pit them against each other: every reduction
21
+ * this file performs is checked against the MNA solver's independent answer for the same network.
22
+ * The narrative has to be not just readable but RIGHT, and an independent solver is the only honest
23
+ * way to know it is.
24
+ *
25
+ * ── Resistors and capacitors are ONE engine, with the rules swapped ─────────────────────────────
26
+ * Every rule below is about the SHAPE of the network: which components share a node, which node
27
+ * joins only two. Shape does not care what is plugged into it. Only the arithmetic differs, and it
28
+ * differs in exactly one way:
29
+ *
30
+ * series parallel
31
+ * resistance R₁ + R₂ 1/(1/R₁ + 1/R₂)
32
+ * capacitance 1/(1/C₁ + 1/C₂) C₁ + C₂
33
+ *
34
+ * which is the single most-confused pair of formulas on the syllabus, precisely because students
35
+ * meet them months apart and memorise them as four unrelated facts. Here they are two facts and a
36
+ * swap, and a student can flip the same drawing between the two and watch which rule moves.
37
+ *
38
+ * The balanced bridge does not even need the swap. Working through the divider ratios, a capacitive
39
+ * bridge balances when C_P/C_Q = C_R/C_S, the SAME form as P/Q = R/S for resistors, because the
40
+ * reciprocal in a capacitor's impedance cancels top and bottom. That is not a coincidence worth
41
+ * hiding; it is why one `findBalancedBridge` serves both.
42
+ *
43
+ * ── The rules, in the order a person actually uses them ─────────────────────────────────────────
44
+ * Order matters for the narrative, not for the answer. These are applied in the sequence an
45
+ * experienced solver reaches for them, so the steps read like someone thinking rather than like a
46
+ * program searching:
47
+ *
48
+ * 1. dangling a component hanging off a dead end is in no closed loop, so delete it.
49
+ * Students lose marks to this one constantly: a branch that leads nowhere is
50
+ * still drawn on the page, and it still looks like it must matter.
51
+ * 2. shorted both ends on the same node, so there is no potential difference across it.
52
+ * Same deletion, different reason, and worth separating because the reasons are
53
+ * what is being taught.
54
+ * 3. parallel two or more components sharing BOTH ends.
55
+ * 4. series a node that is not a terminal and joins exactly two components.
56
+ * 5. bridge a Wheatstone whose arms balance: P/Q = R/S puts both midpoints at the same
57
+ * potential, so the bridging component carries nothing whatever its value, and
58
+ * deleting it leaves two simple series pairs in parallel. This is the trick
59
+ * question in every admission paper, and the one students most often grind
60
+ * through with three simultaneous equations instead.
61
+ *
62
+ * Steps 1-4 alone cannot finish every network; a bridge that does NOT balance defeats all of them,
63
+ * which is exactly why the star-delta transform exists and why `reduce` reports the network it got
64
+ * stuck on rather than pretending.
65
+ */
66
+ const PHYSICS = {
67
+ resistance: {
68
+ symbol: "R",
69
+ unit: "Ω",
70
+ unitTex: "\\,\\Omega",
71
+ seriesAdds: true,
72
+ seriesReason: "so every coulomb through one must go through the other. Same current means series, and resistances in series add.",
73
+ parallelReason: "so they share the same potential difference. That is parallel, and the combination is always SMALLER than the smallest of them, because you have opened another path."
74
+ },
75
+ capacitance: {
76
+ symbol: "C",
77
+ unit: "µF",
78
+ unitTex: "\\,\\mu\\mathrm{F}",
79
+ seriesAdds: false,
80
+ seriesReason: "so the same charge is pushed onto both, while their potential differences add. Equal charge and a bigger total p.d. means a SMALLER capacitance, so capacitors in series combine like resistors in parallel.",
81
+ parallelReason: "so they share the same potential difference, and each stores its own charge on top of the other. Capacitance in parallel simply ADDS, which is the opposite way round from resistance."
82
+ }
83
+ };
84
+ const plainSum = (values) => values.reduce((sum, v) => sum + v, 0);
85
+ const reciprocalSum = (values) => 1 / values.reduce((sum, v) => sum + 1 / v, 0);
86
+ /** Combine components that are in series, for this quantity. */
87
+ function combineSeries(quantity, values) {
88
+ return PHYSICS[quantity].seriesAdds ? plainSum(values) : reciprocalSum(values);
89
+ }
90
+ /** Combine components that are in parallel, for this quantity. */
91
+ function combineParallel(quantity, values) {
92
+ return PHYSICS[quantity].seriesAdds ? reciprocalSum(values) : plainSum(values);
93
+ }
94
+ const finiteNonNegative = (value, what) => {
95
+ if (!Number.isFinite(value) || value < 0) throw new RangeError(`${what} must be zero or more`);
96
+ return value;
97
+ };
98
+ /** A value so small the step narrative should treat it as a degenerate component. */
99
+ const NEGLIGIBLE = 1e-9;
100
+ function validateNetwork(network) {
101
+ validateTopology({
102
+ edges: network.components,
103
+ terminals: network.terminals
104
+ });
105
+ if (!(network.quantity in PHYSICS)) throw new RangeError(`unknown quantity ${network.quantity}`);
106
+ for (const c of network.components) finiteNonNegative(c.value, `component ${c.id}`);
107
+ return network;
108
+ }
109
+ const clone = (n) => ({
110
+ quantity: n.quantity,
111
+ components: n.components.map((c) => ({ ...c })),
112
+ terminals: [...n.terminals]
113
+ });
114
+ /**
115
+ * The network seen as pure shape, which is what `topology.ts` operates on.
116
+ *
117
+ * A `Component` is already an `Edge` plus a value, so this is a rename rather than a conversion.
118
+ * Every structural question below is asked of the topology and every arithmetic one of the values,
119
+ * which is the split that let the AC engine reuse this walk instead of copying it.
120
+ */
121
+ const asTopology = (network) => ({
122
+ edges: network.components,
123
+ terminals: network.terminals
124
+ });
125
+ /** Look a component back up by the id a topology finder returned. */
126
+ const byId = (network, id) => network.components.find((c) => c.id === id);
127
+ /** Value to 3 decimal places, for the step text only. */
128
+ const show = (value) => String(Math.round(value * 1e3) / 1e3);
129
+ const withComponents = (network, components) => ({
130
+ quantity: network.quantity,
131
+ terminals: [...network.terminals],
132
+ components
133
+ });
134
+ const without = (network, ids) => network.components.filter((c) => !ids.has(c.id)).map((c) => ({ ...c }));
135
+ /**
136
+ * A component with a free end. Not a terminal, joins nothing else, so it sits in no closed loop.
137
+ *
138
+ * Deliberately checked first. A dead-end branch is the cheapest thing on the page to eliminate and
139
+ * the one most likely to frighten a student into thinking the network is bigger than it is.
140
+ */
141
+ function findDangling(network) {
142
+ const found = findDangling$1(asTopology(network));
143
+ if (!found) return null;
144
+ const c = byId(network, found.edge.id);
145
+ return {
146
+ rule: "dangling",
147
+ consumed: [{ ...c }],
148
+ produced: null,
149
+ at: found.node,
150
+ reason: `Nothing else meets ${found.node}, so ${c.id} is not part of any closed loop. A branch with a dead end can never affect what happens between the terminals, whatever its value.`,
151
+ arithmetic: "",
152
+ after: withComponents(network, without(network, new Set([c.id])))
153
+ };
154
+ }
155
+ /** Both ends on one node: no potential difference across it, so it does nothing. */
156
+ function findShorted(network) {
157
+ const loop = findSelfLoop(asTopology(network));
158
+ if (!loop) return null;
159
+ const c = byId(network, loop.id);
160
+ return {
161
+ rule: "shorted",
162
+ consumed: [{ ...c }],
163
+ produced: null,
164
+ at: c.a,
165
+ reason: `Both ends of ${c.id} are at ${c.a}, so there is no potential difference across it and it plays no part.`,
166
+ arithmetic: "",
167
+ after: withComponents(network, without(network, new Set([c.id])))
168
+ };
169
+ }
170
+ /**
171
+ * Two or more components sharing BOTH ends.
172
+ *
173
+ * Collapsed all at once rather than in pairs. Three components between the same two nodes is a
174
+ * single observation to a person ("those three are all in parallel"), and splitting it into two
175
+ * steps would make the narrative longer than the thought it represents.
176
+ */
177
+ function findParallel(network) {
178
+ const topology = asTopology(network);
179
+ const edges = findParallelGroup(topology);
180
+ if (!edges) return null;
181
+ const group = edges.map((e) => byId(network, e.id));
182
+ const first = group[0];
183
+ const physics = PHYSICS[network.quantity];
184
+ const values = group.map((c) => c.value);
185
+ const value = combineParallel(network.quantity, values);
186
+ const produced = {
187
+ id: mergedId(topology, edges),
188
+ a: first.a,
189
+ b: first.b,
190
+ value
191
+ };
192
+ const ids = new Set(group.map((c) => c.id));
193
+ return {
194
+ rule: "parallel",
195
+ consumed: group.map((c) => ({ ...c })),
196
+ produced,
197
+ at: null,
198
+ reason: `${group.map((c) => c.id).join(", ")} all run between ${first.a} and ${first.b}, ${physics.parallelReason}`,
199
+ arithmetic: combineTex(network.quantity, values, value, false),
200
+ after: withComponents(network, [...without(network, ids), produced])
201
+ };
202
+ }
203
+ /**
204
+ * A node that is not a terminal and joins exactly two components, so they are in series.
205
+ *
206
+ * The check that it is not a terminal is the whole subtlety. A node joining two components IS a
207
+ * series junction, unless the measurement is being taken there, in which case collapsing it would
208
+ * delete the very point the question asks about.
209
+ */
210
+ function findSeries(network) {
211
+ const topology = asTopology(network);
212
+ const pair = findSeriesPair(topology);
213
+ if (!pair) return null;
214
+ const c1 = byId(network, pair.edges[0].id);
215
+ const c2 = byId(network, pair.edges[1].id);
216
+ const physics = PHYSICS[network.quantity];
217
+ const values = [c1.value, c2.value];
218
+ const value = combineSeries(network.quantity, values);
219
+ const produced = {
220
+ id: mergedId(topology, pair.edges),
221
+ a: pair.ends[0],
222
+ b: pair.ends[1],
223
+ value
224
+ };
225
+ const ids = new Set([c1.id, c2.id]);
226
+ return {
227
+ rule: "series",
228
+ consumed: [{ ...c1 }, { ...c2 }],
229
+ produced,
230
+ at: pair.node,
231
+ reason: `${pair.node} joins ${c1.id} and ${c2.id} and nothing else, ${physics.seriesReason}`,
232
+ arithmetic: combineTex(network.quantity, values, value, true),
233
+ after: withComponents(network, [...without(network, ids), produced])
234
+ };
235
+ }
236
+ /** The arithmetic, as LaTeX, in whichever of the two forms this combination actually takes. */
237
+ function combineTex(quantity, values, result, series) {
238
+ const physics = PHYSICS[quantity];
239
+ const { symbol, unitTex } = physics;
240
+ if (series === physics.seriesAdds) return `${symbol} = ${values.map(show).join(" + ")} = ${show(result)}${unitTex}`;
241
+ if (!Number.isFinite(result)) return `\\frac{1}{${symbol}} = \\infty \\Rightarrow ${symbol} = 0`;
242
+ return `\\frac{1}{${symbol}} = ${values.map((v) => `\\frac{1}{${show(v)}}`).join(" + ")} \\Rightarrow ${symbol} = ${show(result)}${unitTex}`;
243
+ }
244
+ /**
245
+ * A bridge whose arms balance, so the bridging component can be deleted.
246
+ *
247
+ * Detection is structural, not by pattern-matching a drawing: look for a component whose two ends
248
+ * BOTH have degree three, then check whether the four remaining arms form two paths from one
249
+ * terminal to the other through those midpoints. If P/Q = R/S the midpoints sit at equal potential,
250
+ * the bridge carries nothing, and it goes.
251
+ *
252
+ * The SAME ratio test serves capacitance. A capacitive divider puts the midpoint at
253
+ * C_P/(C_P + C_Q) of the supply rather than R_Q/(R_P + R_Q), and setting the two midpoints equal
254
+ * rearranges to C_P/C_Q = C_R/C_S either way, so the reciprocal cancels out of the condition.
255
+ *
256
+ * Worth its own rule rather than leaving it to the star-delta transform, because the insight is the
257
+ * point. A student who spots the balance does the question in two lines; one who does not is
258
+ * looking at three simultaneous equations for the same answer.
259
+ */
260
+ function findBalancedBridge(network) {
261
+ const shape = findBridgeShape(asTopology(network));
262
+ if (!shape) return null;
263
+ const p = byId(network, shape.p.id);
264
+ const q = byId(network, shape.q.id);
265
+ const r = byId(network, shape.r.id);
266
+ const s = byId(network, shape.s.id);
267
+ const bridge = byId(network, shape.bridge.id);
268
+ if (q.value <= 0 || s.value <= 0) return null;
269
+ const left = p.value / q.value;
270
+ const right = r.value / s.value;
271
+ if (Math.abs(left - right) > 1e-9 * Math.max(1, left, right)) return null;
272
+ return {
273
+ rule: "balanced-bridge",
274
+ consumed: [{ ...bridge }],
275
+ produced: null,
276
+ at: null,
277
+ reason: `${p.id}/${q.id} = ${r.id}/${s.id}, so ${shape.m1} and ${shape.m2} sit at the same potential. With no potential difference across it, ${bridge.id} carries nothing and can be removed whatever its value.`,
278
+ arithmetic: `\\frac{${show(p.value)}}{${show(q.value)}} = \\frac{${show(r.value)}}{${show(s.value)}} = ${show(left)}`,
279
+ after: withComponents(network, without(network, new Set([bridge.id])))
280
+ };
281
+ }
282
+ const RULES = [
283
+ findDangling,
284
+ findShorted,
285
+ findParallel,
286
+ findSeries,
287
+ findBalancedBridge
288
+ ];
289
+ /**
290
+ * Reduce until one component spans the terminals, or until no rule applies.
291
+ *
292
+ * The step cap guards against a rule that fails to shrink the network rather than imposing a real
293
+ * limit: every rule above removes at least one component, so a network of n finishes in under n
294
+ * steps and the cap should never be reached.
295
+ */
296
+ function reduce(input) {
297
+ let network = clone(validateNetwork(input));
298
+ const steps = [];
299
+ const cap = network.components.length * 4 + 8;
300
+ while (steps.length < cap) {
301
+ if (isFinished(network)) break;
302
+ let applied = null;
303
+ for (const rule of RULES) {
304
+ applied = rule(network);
305
+ if (applied) break;
306
+ }
307
+ if (!applied) break;
308
+ steps.push(applied);
309
+ network = applied.after;
310
+ }
311
+ if (isFinished(network)) {
312
+ const only = network.components[0];
313
+ return {
314
+ steps,
315
+ value: only ? only.value : 0,
316
+ stuck: null
317
+ };
318
+ }
319
+ return {
320
+ steps,
321
+ value: null,
322
+ stuck: network
323
+ };
324
+ }
325
+ /** Finished means exactly one component left, and it spans the terminals. */
326
+ const isFinished = (network) => isReduced(asTopology(network));
327
+ /**
328
+ * Equivalent value straight from the nodal solver: put 1 V across the terminals, read the current,
329
+ * divide.
330
+ *
331
+ * This is what the reduction narrative is checked against. It shares no code with the rules above,
332
+ * which is the entire point: if a rule were wrong, agreeing with itself would prove nothing.
333
+ *
334
+ * A CAPACITANCE network is solved by handing the matrix the elastances, 1/C, as if they were
335
+ * resistances. That is not a trick: a capacitor's impedance is proportional to 1/C, so a network of
336
+ * capacitors combines exactly as a network of resistors of value 1/C does, and inverting the answer
337
+ * gives the equivalent capacitance. It also means capacitance is checked by a solver that has never
338
+ * heard of a capacitor, which is the strongest form this check can take.
339
+ */
340
+ function equivalentByNodalAnalysis(input) {
341
+ const network = validateNetwork(input);
342
+ const [t1, t2] = network.terminals;
343
+ const asResistance = network.quantity === "resistance";
344
+ const index = new Map([[t2, 0]]);
345
+ const idFor = (node) => {
346
+ const existing = index.get(node);
347
+ if (existing !== void 0) return existing;
348
+ const next = index.size;
349
+ index.set(node, next);
350
+ return next;
351
+ };
352
+ idFor(t1);
353
+ const elems = network.components.map((c) => {
354
+ const asOhms = asResistance ? c.value : c.value <= 1e-9 ? Infinity : 1 / c.value;
355
+ return {
356
+ kind: "R",
357
+ n1: idFor(c.a),
358
+ n2: idFor(c.b),
359
+ value: Number.isFinite(asOhms) ? Math.max(asOhms, NEGLIGIBLE) : 1 / NEGLIGIBLE,
360
+ id: c.id
361
+ };
362
+ });
363
+ elems.push({
364
+ kind: "V",
365
+ n1: idFor(t1),
366
+ n2: 0,
367
+ value: 1,
368
+ id: "__probe"
369
+ });
370
+ const solved = solveDC(elems);
371
+ if (!solved.ok) return null;
372
+ const current = solved.current["__probe"];
373
+ if (current === void 0 || !Number.isFinite(current)) return null;
374
+ const amps = Math.abs(current);
375
+ if (amps < 1e-12) return asResistance ? Infinity : 0;
376
+ const ohms = 1 / amps;
377
+ return asResistance ? ohms : 1 / ohms;
378
+ }
379
+ /**
380
+ * An UNBALANCED five-component bridge, by the star-delta transform.
381
+ *
382
+ * Series and parallel genuinely cannot touch this network: no two components share both ends, and
383
+ * every internal node joins three. That is not a gap in the rules, it is a fact about the shape,
384
+ * and it is the honest reason star-delta is taught at all.
385
+ *
386
+ * Stated for resistance, which is where the syllabus uses it. The same numbers serve a capacitive
387
+ * bridge if the arms are given as elastances, for the reason spelled out above.
388
+ */
389
+ function starFromDelta(ab, bc, ca) {
390
+ const sum = finiteNonNegative(ab, "delta arm") + finiteNonNegative(bc, "delta arm") + finiteNonNegative(ca, "delta arm");
391
+ if (sum <= 0) return [
392
+ 0,
393
+ 0,
394
+ 0
395
+ ];
396
+ return [
397
+ ca * ab / sum,
398
+ ab * bc / sum,
399
+ bc * ca / sum
400
+ ];
401
+ }
402
+ function deltaFromStar(a, b, c) {
403
+ const a0 = finiteNonNegative(a, "star arm");
404
+ const b0 = finiteNonNegative(b, "star arm");
405
+ const c0 = finiteNonNegative(c, "star arm");
406
+ const products = a0 * b0 + b0 * c0 + c0 * a0;
407
+ if (products <= 0) return [
408
+ 0,
409
+ 0,
410
+ 0
411
+ ];
412
+ return [
413
+ products / c0,
414
+ products / a0,
415
+ products / b0
416
+ ];
417
+ }
418
+ /**
419
+ * The classic infinite ladder: `series` in each arm and `shunt` across, repeating forever.
420
+ *
421
+ * Solved the way the textbook does, by noticing that adding one more section leaves the network
422
+ * looking exactly as it did:
423
+ *
424
+ * x = series + (shunt · x) / (shunt + x)
425
+ *
426
+ * which rearranges to a quadratic whose positive root is the answer. Included because it is the one
427
+ * network where the reduction rules are useless and the INSIGHT is the whole method, and because
428
+ * students meet it in every admission paper.
429
+ */
430
+ function infiniteLadderOhm(series, shunt) {
431
+ const a = finiteNonNegative(series, "series arm");
432
+ const b = finiteNonNegative(shunt, "shunt arm");
433
+ return (a + Math.sqrt(a * a + 4 * a * b)) / 2;
434
+ }
435
+ /** The unit a quantity is quoted in, for a scene that has to label it. */
436
+ const unitFor = (quantity) => PHYSICS[quantity].unit;
437
+ const symbolFor = (quantity) => PHYSICS[quantity].symbol;
438
+
439
+ //#endregion
440
+ export { NEGLIGIBLE, combineParallel, combineSeries, deltaFromStar, equivalentByNodalAnalysis, infiniteLadderOhm, reduce, starFromDelta, symbolFor, unitFor, validateNetwork };
@@ -0,0 +1,102 @@
1
+ //#region src/circuit/topology.d.ts
2
+ /**
3
+ * @classytic/stage/circuit/topology — The SHAPE of a two-terminal network, with nothing in it.
4
+ *
5
+ * Every reduction rule splits cleanly into two halves that have nothing to do with each other:
6
+ *
7
+ * structure is there a node joining exactly two edges? do two edges share both ends? is there
8
+ * an edge whose far end joins nothing?
9
+ * arithmetic given that those two are in series, what is the combined value?
10
+ *
11
+ * The first half is identical for resistance, capacitance, impedance, thermal conductance, pipe
12
+ * flow and anything else that lives on a graph. The second half is the only thing that differs. So
13
+ * this file owns the first half and knows nothing about ohms, farads or complex numbers.
14
+ *
15
+ * ── Why this was extracted ──────────────────────────────────────────────────────────────────────
16
+ * `network-reduction/core.ts` was written first and carried both halves. Adding AC impedance meant
17
+ * either writing the same graph walk a second time, against a second set of values, or lifting the
18
+ * walk out. The walk is fiddly in exactly the places that are easy to get subtly wrong (a series
19
+ * junction that is also a measurement terminal, a self-loop, a bridge whose midpoints are not both
20
+ * degree three), and two copies of it would drift.
21
+ *
22
+ * ── The one thing deliberately NOT here ─────────────────────────────────────────────────────────
23
+ * Whether a bridge BALANCES is not a structural question. For resistance it is P/Q = R/S with real
24
+ * numbers; for impedance it is the same ratio with complex ones, which is a much stronger condition
25
+ * and the reason a real AC bridge needs two adjustable components rather than one. So this file
26
+ * finds bridge-shaped subgraphs and each engine decides for itself whether the one it found is
27
+ * balanced.
28
+ */
29
+ /** An edge is an identity and two endpoints. What flows along it is somebody else's business. */
30
+ interface Edge {
31
+ id: string;
32
+ a: string;
33
+ b: string;
34
+ }
35
+ interface Topology {
36
+ edges: readonly Edge[];
37
+ /** The two nodes the network is measured between. */
38
+ terminals: readonly [string, string];
39
+ }
40
+ declare function validateTopology(t: Topology): Topology;
41
+ /** Every edge touching each node. A self-loop is listed once, not twice. */
42
+ declare function degrees(t: Topology): Map<string, Edge[]>;
43
+ declare const isTerminal: (t: Topology, node: string) => boolean;
44
+ /** Node pair as an order-independent key, so A-B and B-A collide. */
45
+ declare const pairKey: (edge: Edge) => string;
46
+ /**
47
+ * An edge with a free end: its far node is not a terminal and nothing else meets there.
48
+ *
49
+ * Such an edge lies on no path between the terminals, so nothing can flow along it whatever it is
50
+ * made of. Reported first because it is the cheapest thing to eliminate.
51
+ */
52
+ declare function findDangling(t: Topology): {
53
+ edge: Edge;
54
+ node: string;
55
+ } | null;
56
+ /** An edge whose two ends are the same node, so it spans nothing. */
57
+ declare function findSelfLoop(t: Topology): Edge | null;
58
+ /** Two or more edges sharing BOTH endpoints. Returned as one group, because that is one observation. */
59
+ declare function findParallelGroup(t: Topology): Edge[] | null;
60
+ interface SeriesPair {
61
+ /** The node that disappears. */
62
+ node: string;
63
+ edges: [Edge, Edge];
64
+ /** The two far ends, which the replacement edge will span. */
65
+ ends: [string, string];
66
+ }
67
+ /**
68
+ * A node that is NOT a terminal and joins exactly two edges.
69
+ *
70
+ * The terminal check is the whole subtlety and the easiest thing to omit. A node joining two edges
71
+ * is a series junction unless the measurement is being taken there, in which case collapsing it
72
+ * would delete the very point the question asks about.
73
+ */
74
+ declare function findSeriesPair(t: Topology): SeriesPair | null;
75
+ interface BridgeShape {
76
+ bridge: Edge;
77
+ /** The two midpoints the bridge spans. */
78
+ m1: string;
79
+ m2: string;
80
+ /** Arms, named the way every textbook names them: P and Q on one midpoint, R and S on the other. */
81
+ p: Edge;
82
+ q: Edge;
83
+ r: Edge;
84
+ s: Edge;
85
+ }
86
+ /**
87
+ * A bridge-SHAPED subgraph: an edge between two degree-three midpoints, each of which reaches each
88
+ * terminal by exactly one other edge.
89
+ *
90
+ * Whether it balances is not asked here, on purpose. That test depends on what the edges are made
91
+ * of, and for impedance it is a strictly stronger condition than for resistance.
92
+ */
93
+ declare function findBridgeShape(t: Topology): BridgeShape | null;
94
+ /** One edge left, and it spans the terminals. */
95
+ declare function isReduced(t: Topology): boolean;
96
+ /**
97
+ * A fresh edge id that cannot collide, built from the ids it replaces so a narrative stays
98
+ * traceable back to the original drawing.
99
+ */
100
+ declare function mergedId(t: Topology, from: readonly Edge[]): string;
101
+ //#endregion
102
+ export { BridgeShape, Edge, SeriesPair, Topology, degrees, findBridgeShape, findDangling, findParallelGroup, findSelfLoop, findSeriesPair, isReduced, isTerminal, mergedId, pairKey, validateTopology };
@@ -0,0 +1,143 @@
1
+ //#region src/circuit/topology.ts
2
+ function validateTopology(t) {
3
+ if (t.terminals[0] === t.terminals[1]) throw new RangeError("the two terminals must be different nodes");
4
+ const seen = /* @__PURE__ */ new Set();
5
+ for (const edge of t.edges) {
6
+ if (seen.has(edge.id)) throw new RangeError(`duplicate edge id ${edge.id}`);
7
+ seen.add(edge.id);
8
+ }
9
+ return t;
10
+ }
11
+ /** Every edge touching each node. A self-loop is listed once, not twice. */
12
+ function degrees(t) {
13
+ const out = /* @__PURE__ */ new Map();
14
+ const push = (node, edge) => out.set(node, [...out.get(node) ?? [], edge]);
15
+ for (const edge of t.edges) {
16
+ push(edge.a, edge);
17
+ if (edge.b !== edge.a) push(edge.b, edge);
18
+ }
19
+ return out;
20
+ }
21
+ const isTerminal = (t, node) => node === t.terminals[0] || node === t.terminals[1];
22
+ /** Node pair as an order-independent key, so A-B and B-A collide. */
23
+ const pairKey = (edge) => [edge.a, edge.b].slice().sort().join(" ");
24
+ /**
25
+ * An edge with a free end: its far node is not a terminal and nothing else meets there.
26
+ *
27
+ * Such an edge lies on no path between the terminals, so nothing can flow along it whatever it is
28
+ * made of. Reported first because it is the cheapest thing to eliminate.
29
+ */
30
+ function findDangling(t) {
31
+ const deg = degrees(t);
32
+ for (const edge of t.edges) for (const end of [edge.a, edge.b]) {
33
+ if (isTerminal(t, end)) continue;
34
+ if ((deg.get(end) ?? []).length !== 1) continue;
35
+ return {
36
+ edge,
37
+ node: end
38
+ };
39
+ }
40
+ return null;
41
+ }
42
+ /** An edge whose two ends are the same node, so it spans nothing. */
43
+ function findSelfLoop(t) {
44
+ return t.edges.find((edge) => edge.a === edge.b) ?? null;
45
+ }
46
+ /** Two or more edges sharing BOTH endpoints. Returned as one group, because that is one observation. */
47
+ function findParallelGroup(t) {
48
+ const groups = /* @__PURE__ */ new Map();
49
+ for (const edge of t.edges) {
50
+ if (edge.a === edge.b) continue;
51
+ const key = pairKey(edge);
52
+ groups.set(key, [...groups.get(key) ?? [], edge]);
53
+ }
54
+ for (const group of groups.values()) if (group.length >= 2) return group;
55
+ return null;
56
+ }
57
+ /**
58
+ * A node that is NOT a terminal and joins exactly two edges.
59
+ *
60
+ * The terminal check is the whole subtlety and the easiest thing to omit. A node joining two edges
61
+ * is a series junction unless the measurement is being taken there, in which case collapsing it
62
+ * would delete the very point the question asks about.
63
+ */
64
+ function findSeriesPair(t) {
65
+ for (const [node, touching] of degrees(t)) {
66
+ if (isTerminal(t, node)) continue;
67
+ if (touching.length !== 2) continue;
68
+ const [e1, e2] = touching;
69
+ if (e1.id === e2.id) continue;
70
+ const far = (e) => e.a === node ? e.b : e.a;
71
+ return {
72
+ node,
73
+ edges: [e1, e2],
74
+ ends: [far(e1), far(e2)]
75
+ };
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * A bridge-SHAPED subgraph: an edge between two degree-three midpoints, each of which reaches each
81
+ * terminal by exactly one other edge.
82
+ *
83
+ * Whether it balances is not asked here, on purpose. That test depends on what the edges are made
84
+ * of, and for impedance it is a strictly stronger condition than for resistance.
85
+ */
86
+ function findBridgeShape(t) {
87
+ const deg = degrees(t);
88
+ const [t1, t2] = t.terminals;
89
+ for (const bridge of t.edges) {
90
+ const m1 = bridge.a;
91
+ const m2 = bridge.b;
92
+ if (m1 === m2) continue;
93
+ if (isTerminal(t, m1) || isTerminal(t, m2)) continue;
94
+ if ((deg.get(m1) ?? []).length !== 3 || (deg.get(m2) ?? []).length !== 3) continue;
95
+ const arm = (mid, term) => {
96
+ const found = (deg.get(mid) ?? []).filter((e) => e.id !== bridge.id && (e.a === term || e.b === term));
97
+ return found.length === 1 ? found[0] : null;
98
+ };
99
+ const p = arm(m1, t1);
100
+ const q = arm(m1, t2);
101
+ const r = arm(m2, t1);
102
+ const s = arm(m2, t2);
103
+ if (!p || !q || !r || !s) continue;
104
+ if (new Set([
105
+ p.id,
106
+ q.id,
107
+ r.id,
108
+ s.id
109
+ ]).size !== 4) continue;
110
+ return {
111
+ bridge,
112
+ m1,
113
+ m2,
114
+ p,
115
+ q,
116
+ r,
117
+ s
118
+ };
119
+ }
120
+ return null;
121
+ }
122
+ /** One edge left, and it spans the terminals. */
123
+ function isReduced(t) {
124
+ if (t.edges.length !== 1) return false;
125
+ const only = t.edges[0];
126
+ const [t1, t2] = t.terminals;
127
+ return only.a === t1 && only.b === t2 || only.a === t2 && only.b === t1;
128
+ }
129
+ /**
130
+ * A fresh edge id that cannot collide, built from the ids it replaces so a narrative stays
131
+ * traceable back to the original drawing.
132
+ */
133
+ function mergedId(t, from) {
134
+ const taken = new Set(t.edges.map((e) => e.id));
135
+ const base = from.map((e) => e.id).join("");
136
+ let id = base;
137
+ let n = 2;
138
+ while (taken.has(id)) id = `${base}_${n++}`;
139
+ return id;
140
+ }
141
+
142
+ //#endregion
143
+ export { degrees, findBridgeShape, findDangling, findParallelGroup, findSelfLoop, findSeriesPair, isReduced, isTerminal, mergedId, pairKey, validateTopology };