@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,234 @@
1
+ import { evaluate } from "../math/ast.mjs";
2
+ import { parse } from "../math/parse.mjs";
3
+ import { isolate, occurrences } from "../math/rearrange.mjs";
4
+ import { toLatex } from "../math/latex.mjs";
5
+ import { calc, texSigFigs } from "../core/worked.mjs";
6
+ import { Quantity, describeDimension, divDim, isDimensionless, mulDim, parseUnit, powDim, qty, sameDim } from "../quantity/index.mjs";
7
+ import { checkDerivation, deriveValue, explain } from "./derive.mjs";
8
+
9
+ //#region src/relation/index.ts
10
+ /**
11
+ * relation, a declared equation that can be solved for ANY of its variables and can show its
12
+ * working.
13
+ *
14
+ * ```ts
15
+ * const moles = relation('n = c * V', { n: 'mol', c: 'mol/dm3', V: 'dm3' });
16
+ * moles.solve({ c: 0.100, V: 0.02450 }); // n = 2.45e-3 mol
17
+ * moles.solve({ c: 0.100, V: qty(24.50, 'cm3') }); // the same, titre straight off the burette
18
+ * moles.solve({ n: 2.45e-3, V: 0.02450 }); // c = 0.100 mol/dm3
19
+ * moles.steps({ c: 0.100, V: 0.02450 }); // the substitution, line by line
20
+ * ```
21
+ *
22
+ * ONE DECLARATION, THREE READERS. A lab's model recomputes from it when a slider moves, a lesson's
23
+ * derivation is generated from it, and a checker verifies the lesson against it. Today those are
24
+ * three hand-written copies of the same equation and nothing makes them agree.
25
+ *
26
+ * Two properties do the real work:
27
+ *
28
+ * **Units are checked when the relation is DECLARED, not when it is used.** The check is arithmetic
29
+ * on the dimension vector, never on the spelling, so `V: 'cm3'` and `V: 'dm3'` both balance against
30
+ * `n: 'mol'` and `c: 'mol/dm3'`: they are the same dimension and the factors sort themselves out.
31
+ * What does not balance is refused before a single lesson can be written against it:
32
+ *
33
+ * ```ts
34
+ * relation('n = c + V', { n: 'mol', c: 'mol/dm3', V: 'dm3' });
35
+ * // Error: cannot add mol/m^3 and m^3
36
+ * ```
37
+ *
38
+ * A wrong relation is worse than no relation, because lessons, labs and the checker would then all
39
+ * agree with each other and all be wrong.
40
+ *
41
+ * **Every number in the working came from the caller.** A derivation generated by `.steps()` cannot
42
+ * divide by a mass that is not in the question, because the only values it can substitute are the
43
+ * ones passed in. That is the class of error that shipped into a draft lesson and was caught by
44
+ * reading rather than by any gate.
45
+ *
46
+ * SOLVING IS ISOLATION, NOT ALGEBRA. The unknown must appear exactly once, and the solver peels
47
+ * operations off it one at a time, applying each inverse to the other side. That covers the whole
48
+ * A-level relation table. The moment a relation needs more than that, the scope is wrong and the
49
+ * answer is to declare a second relation. `x` appearing twice is refused with a message that says
50
+ * so, rather than being approximated numerically, because a silent numeric answer to a question the
51
+ * kernel cannot really answer is worse than a refusal.
52
+ *
53
+ * Pure: no React, no pixels.
54
+ */
55
+ /**
56
+ * The dimension of an expression, given a dimension for each variable.
57
+ *
58
+ * `log`, `log10`, `ln` are the one deliberate deviation from strict dimensional analysis. The
59
+ * logarithm of a dimensioned quantity is not defined, yet pH is *defined* as −log[H⁺] with [H⁺] in
60
+ * mol dm⁻³. Chemistry means "the log of the numeric value in the stated unit", so that is what this
61
+ * implements: the argument may carry a dimension, the result is dimensionless. Refusing it would
62
+ * block every acid-base relation in the syllabus; silently pretending the argument is dimensionless
63
+ * would lose the unit on the way in. Declaring the convention is the honest third option.
64
+ */
65
+ function dimOf(node, dims, where) {
66
+ switch (node.type) {
67
+ case "num": return {};
68
+ case "var": {
69
+ const d = dims[node.name];
70
+ if (!d) throw new Error(`${where}: no unit declared for "${node.name}"`);
71
+ return d;
72
+ }
73
+ case "neg": return dimOf(node.arg, dims, where);
74
+ case "binary": {
75
+ const a = dimOf(node.left, dims, where);
76
+ const b = dimOf(node.right, dims, where);
77
+ switch (node.op) {
78
+ case "+":
79
+ case "-":
80
+ if (!sameDim(a, b)) throw new Error(`${where}: cannot ${node.op === "+" ? "add" : "subtract"} ${describeDimension(a)} and ${describeDimension(b)}`);
81
+ return a;
82
+ case "*": return mulDim(a, b);
83
+ case "/": return divDim(a, b);
84
+ case "%": return a;
85
+ case "^":
86
+ if (!isDimensionless(b)) throw new Error(`${where}: an exponent must be dimensionless, got ${describeDimension(b)}`);
87
+ if (node.right.type !== "num") {
88
+ if (isDimensionless(a)) return {};
89
+ throw new Error(`${where}: "${toLatex(node.left)}" is raised to a variable power, so its dimension is not fixed. Declare it dimensionless.`);
90
+ }
91
+ return powDim(a, node.right.value);
92
+ default: return a;
93
+ }
94
+ }
95
+ case "call": {
96
+ const arg = node.args[0] ? dimOf(node.args[0], dims, where) : {};
97
+ if (node.fn === "log" || node.fn === "log10" || node.fn === "log2" || node.fn === "ln") return {};
98
+ if (node.fn === "sqrt") return powDim(arg, .5);
99
+ if (node.fn === "cbrt") return powDim(arg, 1 / 3);
100
+ if (node.fn === "abs") return arg;
101
+ if (!isDimensionless(arg)) throw new Error(`${where}: ${node.fn}() needs a dimensionless argument, got ${describeDimension(arg)}`);
102
+ return {};
103
+ }
104
+ default: return {};
105
+ }
106
+ }
107
+ var Relation = class {
108
+ equation;
109
+ title;
110
+ units;
111
+ variables;
112
+ left;
113
+ right;
114
+ dims;
115
+ isolated = /* @__PURE__ */ new Map();
116
+ constructor(spec) {
117
+ const halves = spec.equation.split("=");
118
+ if (halves.length !== 2) throw new Error(`Relation "${spec.equation}" must have exactly one "=".`);
119
+ this.equation = spec.equation;
120
+ this.title = spec.title ?? spec.equation;
121
+ this.units = spec.units;
122
+ this.left = parse(halves[0]);
123
+ this.right = parse(halves[1]);
124
+ this.dims = {};
125
+ for (const [name, unit] of Object.entries(spec.units)) this.dims[name] = parseUnit(unit).dim;
126
+ const seen = /* @__PURE__ */ new Set();
127
+ const walk = (node) => {
128
+ if (node.type === "var") seen.add(node.name);
129
+ else if (node.type === "neg") walk(node.arg);
130
+ else if (node.type === "binary") {
131
+ walk(node.left);
132
+ walk(node.right);
133
+ } else if (node.type === "call") node.args.forEach(walk);
134
+ };
135
+ walk(this.left);
136
+ walk(this.right);
137
+ for (const name of [
138
+ "e",
139
+ "pi",
140
+ "tau"
141
+ ]) seen.delete(name);
142
+ this.variables = [...seen];
143
+ for (const name of this.variables) if (!(name in this.dims)) throw new Error(`Relation "${spec.equation}": no unit declared for "${name}".`);
144
+ const dl = dimOf(this.left, this.dims, `Relation "${spec.equation}"`);
145
+ const dr = dimOf(this.right, this.dims, `Relation "${spec.equation}"`);
146
+ if (!sameDim(dl, dr)) throw new Error(`Relation "${spec.equation}" does not balance: left is ${describeDimension(dl)}, right is ${describeDimension(dr)}.`);
147
+ }
148
+ /** The declared unit of a variable. */
149
+ unitOf(name) {
150
+ const unit = this.units[name];
151
+ if (!unit) throw new Error(`"${name}" is not a variable of ${this.equation}`);
152
+ return unit;
153
+ }
154
+ /** Numeric values in declared units, converting any Quantity that was passed in. */
155
+ normalise(given) {
156
+ const scope = {};
157
+ for (const [name, value] of Object.entries(given)) {
158
+ if (!this.variables.includes(name)) throw new Error(`"${name}" is not a variable of ${this.equation}`);
159
+ scope[name] = value instanceof Quantity ? value.in(this.unitOf(name)) : value;
160
+ }
161
+ return scope;
162
+ }
163
+ /** The one variable of the relation that `given` does not supply. */
164
+ unknownOf(given) {
165
+ const missing = this.variables.filter((v) => !(v in given));
166
+ if (missing.length === 0) throw new Error(`Nothing to solve for in ${this.equation}: every variable was given.`);
167
+ if (missing.length > 1) throw new Error(`${this.equation} has ${missing.length} unknowns (${missing.join(", ")}); exactly one must be left out.`);
168
+ return missing[0];
169
+ }
170
+ expressionFor(target) {
171
+ const cached = this.isolated.get(target);
172
+ if (cached) return cached;
173
+ const count = occurrences(this.left, target) + occurrences(this.right, target);
174
+ if (count === 0) throw new Error(`"${target}" does not appear in ${this.equation}`);
175
+ if (count > 1) throw new Error(`Cannot solve ${this.equation} for "${target}": it appears ${count} times, and this kernel isolates rather than does algebra. Declare a second relation that already has "${target}" alone.`);
176
+ const expr = isolate(this.left, this.right, target);
177
+ this.isolated.set(target, expr);
178
+ return expr;
179
+ }
180
+ /** Solve for whichever variable was left out of `given`. */
181
+ solve(given) {
182
+ const target = this.unknownOf(given);
183
+ return this.solveFor(target, given);
184
+ }
185
+ /** Solve for a named variable, ignoring any value supplied for it. */
186
+ solveFor(target, given) {
187
+ const scope = this.normalise(given);
188
+ delete scope[target];
189
+ const value = evaluate(this.expressionFor(target), scope);
190
+ if (!Number.isFinite(value)) throw new Error(`Solving ${this.equation} for "${target}" gave ${value}. Check that every other variable was supplied and is in range.`);
191
+ const unit = this.unitOf(target);
192
+ return {
193
+ name: target,
194
+ value,
195
+ unit,
196
+ quantity: qty(value, unit)
197
+ };
198
+ }
199
+ /**
200
+ * The worked substitution, for a lesson's derivation.
201
+ *
202
+ * Three lines: the relation, the relation rearranged for the unknown, and the substitution with
203
+ * the answer. Every number shown came from `given`, which is the property that makes a generated
204
+ * derivation trustworthy in a way a hand-written one is not.
205
+ */
206
+ steps(given, sf = 3) {
207
+ const target = this.unknownOf(given);
208
+ const scope = this.normalise(given);
209
+ const solved = this.solveFor(target, given);
210
+ const expr = this.expressionFor(target);
211
+ const w = calc().step(texOf(this.left, this.right), this.title);
212
+ const rearranged = `${target} = ${toLatex(expr)}`;
213
+ if (rearranged !== texOf(this.left, this.right)) w.step(rearranged, `rearranged for ${target}`);
214
+ const subs = this.variables.filter((v) => v !== target).map((v) => `${v} = ${fmt(scope[v], sf)}\\ \\mathrm{${this.unitOf(v)}}`).join(",\\quad ");
215
+ if (subs) w.step(subs, "the values from the question");
216
+ w.step(`${target} = ${fmt(solved.value, sf)}\\ \\mathrm{${solved.unit}}`, `to ${sf} significant figures`);
217
+ return w.done(solved.value);
218
+ }
219
+ };
220
+ const texOf = (left, right) => `${toLatex(left)} = ${toLatex(right)}`;
221
+ /** Significant figures, without exponent notation for ordinary magnitudes. Shared with every other
222
+ * kernel that shows working, so two derivations of one quantity cannot round differently. */
223
+ const fmt = (value, sf) => texSigFigs(value, sf);
224
+ const relation = (equation, units, title) => new Relation(title === void 0 ? {
225
+ equation,
226
+ units
227
+ } : {
228
+ equation,
229
+ units,
230
+ title
231
+ });
232
+
233
+ //#endregion
234
+ export { Relation, checkDerivation, deriveValue, explain, relation };
@@ -15,6 +15,9 @@ interface SceneProps {
15
15
  /** Author opt-in: start a sim running immediately (ambient motion) instead of
16
16
  * the default "press Play". A SceneDoc with no `meta.sims` is always static. */
17
17
  autoPlay?: boolean;
18
+ /** Optional controlled playback for hosts that provide their own transport. */
19
+ playing?: boolean;
20
+ onPlayingChange?: (playing: boolean) => void;
18
21
  }
19
22
  declare function Scene({
20
23
  doc,
@@ -25,7 +28,9 @@ declare function Scene({
25
28
  height,
26
29
  view,
27
30
  ariaLabel,
28
- autoPlay
31
+ autoPlay,
32
+ playing: playingProp,
33
+ onPlayingChange
29
34
  }: SceneProps): ReactNode;
30
35
  //#endregion
31
36
  export { Scene, SceneProps };
@@ -45,7 +45,7 @@ function autoView(resolved) {
45
45
  yMax: Math.max(...ys) + pad
46
46
  };
47
47
  }
48
- function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes = true, height, view, ariaLabel = "Interactive figure", autoPlay = false }) {
48
+ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes = true, height, view, ariaLabel = "Interactive figure", autoPlay = false, playing: playingProp, onPlayingChange }) {
49
49
  const [overlay, setOverlay] = useState(/* @__PURE__ */ new Map());
50
50
  const prevRef = useRef(void 0);
51
51
  const sims = doc.meta?.sims;
@@ -55,14 +55,22 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
55
55
  const [frame, setFrame] = useState(0);
56
56
  const { ref: rootRef, inView } = useInView();
57
57
  const [done, setDone] = useState(false);
58
- const [playing, setPlaying] = useState(autoPlay);
58
+ const [internalPlaying, setInternalPlaying] = useState(autoPlay);
59
+ const playing = playingProp ?? internalPlaying;
60
+ const setPlaying = (next) => {
61
+ if (playingProp === void 0) setInternalPlaying(next);
62
+ onPlayingChange?.(next);
63
+ };
59
64
  useFrameLoop((f) => {
60
65
  if (!sims?.length || !simStates.current) return;
61
66
  const next = stepSims(sims, simStates.current, Math.min(.05, f.dtMs / 1e3));
62
67
  simStates.current = next.states;
63
68
  simScalars.current = next.scalars;
64
69
  setFrame((n) => (n + 1) % 1e6);
65
- if (next.done) setDone(true);
70
+ if (next.done) {
71
+ setDone(true);
72
+ setPlaying(false);
73
+ }
66
74
  }, { running: !!sims?.length && inView && playing && !done });
67
75
  const merged = useMemo(() => {
68
76
  const scalars = simScalars.current;
@@ -95,7 +103,7 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
95
103
  const hasSims = !!sims?.length;
96
104
  return /* @__PURE__ */ jsxs("div", {
97
105
  ref: rootRef,
98
- style: { position: "relative" },
106
+ className: "stage-scene",
99
107
  children: [
100
108
  /* @__PURE__ */ jsxs(Stage, {
101
109
  view: v,
@@ -114,30 +122,9 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
114
122
  type: "button",
115
123
  onClick: () => setPlaying(true),
116
124
  "aria-label": "Play simulation",
117
- style: {
118
- position: "absolute",
119
- inset: 0,
120
- display: "flex",
121
- alignItems: "center",
122
- justifyContent: "center",
123
- background: "color-mix(in oklab, var(--stage-bg) 38%, transparent)",
124
- border: "none",
125
- borderRadius: 12,
126
- cursor: "pointer"
127
- },
125
+ className: "stage-scene__play-overlay",
128
126
  children: /* @__PURE__ */ jsx("span", {
129
- style: {
130
- display: "inline-flex",
131
- alignItems: "center",
132
- gap: 8,
133
- padding: "11px 26px",
134
- borderRadius: 999,
135
- background: "var(--stage-accent)",
136
- color: "#fff",
137
- fontWeight: 800,
138
- fontSize: 16,
139
- boxShadow: "0 6px 18px rgba(0,0,0,.28)"
140
- },
127
+ className: "stage-scene__play-label",
141
128
  children: "▶ Play"
142
129
  })
143
130
  }),
@@ -145,19 +132,7 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
145
132
  type: "button",
146
133
  onClick: () => setPlaying(false),
147
134
  "aria-label": "Pause simulation",
148
- style: {
149
- position: "absolute",
150
- top: 10,
151
- right: 10,
152
- width: 34,
153
- height: 34,
154
- borderRadius: 999,
155
- border: "1px solid var(--stage-grid)",
156
- background: "color-mix(in oklab, var(--stage-bg) 80%, transparent)",
157
- color: "var(--stage-fg)",
158
- cursor: "pointer",
159
- fontSize: 13
160
- },
135
+ className: "stage-scene__pause",
161
136
  children: "⏸"
162
137
  })
163
138
  ]
@@ -1,7 +1,7 @@
1
1
  import { vec } from "../core/vec.mjs";
2
2
  import { isCircleVal, isLineVal, isVec2, numOf } from "./types.mjs";
3
3
  import { getAsset } from "./assets.mjs";
4
- import { compileExpr } from "../math/index.mjs";
4
+ import { compileExpr } from "../math/expr.mjs";
5
5
 
6
6
  //#region src/scene/evaluators.ts
7
7
  /**
@@ -1,36 +1,61 @@
1
+ import { MATERIALS } from "../thermo/calorimetry.mjs";
2
+
1
3
  //#region src/sim/thermal.ts
4
+ /**
5
+ * `thermal`, calorimetry: pour heat into a substance and watch its temperature
6
+ * and phase evolve. The one core behind the whole heat-&-temperature module.
7
+ *
8
+ * A substance has a specific heat in EACH phase (solid/liquid/gas) and a latent
9
+ * heat for each transition (fusion at the melting point, vaporisation at the
10
+ * boiling point). Pouring in power P for time dt adds q = P·dt of energy; that
11
+ * energy climbs an ENERGY LADDER of segments:
12
+ *
13
+ * • heating a phase → temperature RISES, q = m·c·Δθ (a sloped run, slope 1/mc)
14
+ * • a phase change → temperature is FLAT, q = m·L (a horizontal plateau)
15
+ *
16
+ * So plotting temperature against heat-added gives the classic heating curve:
17
+ * sloped rises separated by flat plateaus, and the WIDTH of each segment is the
18
+ * heat that part needs. The marker just walks along that fixed curve as energy
19
+ * accumulates. Pure + deterministic (energy is linear in time at constant power),
20
+ * so it replays as video and unit-tests with no DOM. `powerW` lives in state, so a
21
+ * slider can change the heating rate live; flip it negative to cool back down.
22
+ */
2
23
  const clamp01 = (v) => Math.max(0, Math.min(1, v));
24
+ /**
25
+ * Build a `Substance` from the SI material data in `thermo/calorimetry`.
26
+ *
27
+ * This sim works in GRAMS and J/(g·K) because its whole surface does, and
28
+ * changing that would break every lab driving it. The kernel works in kilograms
29
+ * because the rest of physics does. Rather than keep two copies of water's
30
+ * numbers and let them drift, the conversion happens once, here, and the
31
+ * factor of 1000 appears exactly this many times in the package.
32
+ */
33
+ const fromMaterial = (material, names) => ({
34
+ name: material.name,
35
+ ...names,
36
+ cSolid: material.cSolid / 1e3,
37
+ cLiquid: material.cLiquid / 1e3,
38
+ cGas: material.cGas / 1e3,
39
+ lFusion: material.latentFusion / 1e3,
40
+ lVapor: material.latentVaporisation / 1e3,
41
+ tMelt: material.meltingPointC,
42
+ tBoil: material.boilingPointC
43
+ });
3
44
  /** Real water, the canonical example (note: latent vaporisation ≫ everything,
4
45
  * so the boiling plateau dwarfs the rest, which is exactly the lesson). */
5
- const WATER = {
6
- name: "Water",
46
+ const WATER = fromMaterial(MATERIALS.water, {
7
47
  solidName: "ice",
8
48
  liquidName: "water",
9
49
  gasName: "steam",
10
- cSolid: 2.09,
11
- cLiquid: 4.18,
12
- cGas: 2.01,
13
- lFusion: 334,
14
- lVapor: 2260,
15
- tMelt: 0,
16
- tBoil: 100,
17
50
  color: "var(--stage-accent, #3b82f6)"
18
- };
51
+ });
19
52
  /** Ethanol, lower boiling point, smaller latent heats; a contrasting curve. */
20
- const ETHANOL = {
21
- name: "Ethanol",
53
+ const ETHANOL = fromMaterial(MATERIALS.ethanol, {
22
54
  solidName: "solid",
23
55
  liquidName: "ethanol",
24
56
  gasName: "vapour",
25
- cSolid: 1.2,
26
- cLiquid: 2.44,
27
- cGas: 1.42,
28
- lFusion: 108,
29
- lVapor: 855,
30
- tMelt: -114,
31
- tBoil: 78,
32
57
  color: "var(--stage-accent-2, #8b5cf6)"
33
- };
58
+ });
34
59
  /** Build the energy ladder from `tStart` up to `tMax`, including only the phases
35
60
  * reachable heating upward (a liquid start skips the solid+melting runs). */
36
61
  function buildSegs(sub, mass, tStart, tMax) {
@@ -0,0 +1,207 @@
1
+ //#region src/thermo/calorimetry.d.ts
2
+ /**
3
+ * Calorimetry: heat in, temperature and phase out.
4
+ *
5
+ * THE MODEL. Energy delivered to a body goes into exactly one of two things at
6
+ * any moment, and which one depends only on whether it is sitting at a
7
+ * transition temperature:
8
+ *
9
+ * warming a phase q = m·c·Δθ temperature rises
10
+ * changing phase q = m·L temperature is FLAT
11
+ *
12
+ * Everything here follows from putting those two in a row. A body's ENTHALPY
13
+ * LADDER is the alternating sequence of sloped runs and flat plateaus from a
14
+ * cold datum up to any temperature, and once you have the ladder you can ask
15
+ * either direction: how much heat to reach this temperature, or what
16
+ * temperature this much heat reaches.
17
+ *
18
+ * THE METHOD, and why it is exact. Total enthalpy H(θ) is piecewise LINEAR in
19
+ * temperature, with an upward jump of m·L at each transition. So the mixture
20
+ * problem, find the θ at which the bodies' enthalpies sum to what they started
21
+ * with, is the inversion of a piecewise-linear monotone function. That is
22
+ * solved by walking the brackets and doing one division, with no iteration and
23
+ * no tolerance. Bisection would also work and would be worse: it converges
24
+ * slowly exactly where the interesting answers are, at the plateaus, because
25
+ * there the function is flat in the inverse direction.
26
+ *
27
+ * This matters because it is what lets the solver answer the question a student
28
+ * actually cannot do by hand: mix ice below freezing with water above it, and
29
+ * the outcome is a case analysis (all ice, ice and water at 0 °C, all water)
30
+ * that the algebra hides. Here it falls out of which bracket the answer lands
31
+ * in.
32
+ *
33
+ * UNITS: SI. Mass in kg, specific heat capacity in J/(kg·K), latent heat in
34
+ * J/kg, energy in joules. Temperatures are in DEGREES CELSIUS, because every
35
+ * transition point a lesson names is quoted that way and the differences that
36
+ * appear in m·c·Δθ are identical in either scale.
37
+ *
38
+ * SCOPE. One-dimensional in temperature: no pressure dependence, so the boiling
39
+ * point is a constant rather than a function of the ambient pressure, and
40
+ * sublimation is not modelled. Specific heats are constant within a phase.
41
+ */
42
+ type PhaseName = 'solid' | 'liquid' | 'gas';
43
+ /**
44
+ * A material, as the four numbers that fix its enthalpy ladder plus the three
45
+ * specific heats.
46
+ *
47
+ * `latentFusion` and `latentVaporisation` are per kilogram, so the plateau
48
+ * widths scale with mass exactly as the sloped runs do.
49
+ */
50
+ interface Material {
51
+ name: string;
52
+ /** Specific heat capacity in each phase, J/(kg·K). */
53
+ cSolid: number;
54
+ cLiquid: number;
55
+ cGas: number;
56
+ /** Latent heat of fusion, J/kg. */
57
+ latentFusion: number;
58
+ /** Latent heat of vaporisation, J/kg. */
59
+ latentVaporisation: number;
60
+ /** Melting point, °C. */
61
+ meltingPointC: number;
62
+ /** Boiling point at standard pressure, °C. */
63
+ boilingPointC: number;
64
+ }
65
+ /**
66
+ * Materials that appear in the standard problems.
67
+ *
68
+ * Water's numbers are worth reading as a set: the latent heat of vaporisation
69
+ * is 2.26 MJ/kg against 4.18 kJ/(kg·K) for warming, so boiling a kilogram away
70
+ * costs the same as heating it through 540 °C. That ratio is the whole reason
71
+ * sweating works and the reason a steam burn is worse than a boiling-water
72
+ * burn, and it is visible in the table before any lab draws it.
73
+ */
74
+ declare const MATERIALS: Record<string, Material>;
75
+ /**
76
+ * Heat to warm a mass through a temperature change: q = m·c·Δθ.
77
+ *
78
+ * Δθ is SIGNED, so cooling returns a negative number rather than an absolute
79
+ * one. That is what makes the mixture bookkeeping add up to zero instead of
80
+ * needing a hand-placed minus sign on whichever body happened to be hotter.
81
+ */
82
+ declare function heatToWarmJ(massKg: number, specificHeat: number, deltaT: number): number;
83
+ /** Heat to change the state of a mass at its transition temperature: q = m·L. */
84
+ declare function heatToChangeStateJ(massKg: number, latentHeat: number): number;
85
+ /** The inverse of q = m·c·Δθ, solved for the temperature change. */
86
+ declare function temperatureRiseK(heatJ: number, massKg: number, specificHeat: number): number;
87
+ /** The inverse of q = m·c·Δθ, solved for the specific heat capacity. */
88
+ declare function specificHeatFromJ(heatJ: number, massKg: number, deltaT: number): number;
89
+ /**
90
+ * The water equivalent of a calorimeter, kg: the mass of water with the same
91
+ * heat capacity.
92
+ *
93
+ * Lets a calorimeter be folded into a mixture as if it were more water, which
94
+ * is exactly how the textbook method handles it.
95
+ */
96
+ declare function waterEquivalentKg(massKg: number, specificHeat: number): number;
97
+ /**
98
+ * The enthalpy of a mass of material at temperature θ, J, measured from a datum
99
+ * of absolute zero with the material solid.
100
+ *
101
+ * The datum is arbitrary and cancels out of every difference, which is the only
102
+ * reason it is allowed to be as unphysical as "solid water at −273 °C". What
103
+ * matters is that the SAME datum is used for every body in a mixture.
104
+ *
105
+ * At a transition temperature this returns the value approached from BELOW, so
106
+ * `enthalpyJ(m, water, 0)` is solid ice at 0 °C with none of it melted. The
107
+ * jump of m·L lives between this value and `enthalpyJ` an epsilon higher, and
108
+ * the mixture solver resolves partial melting by landing inside that jump
109
+ * rather than by nudging the temperature.
110
+ */
111
+ declare function enthalpyJ(massKg: number, material: Material, celsius: number): number;
112
+ /** The phase a material is in at a temperature, taking a transition point as the lower phase. */
113
+ declare function phaseAt(material: Material, celsius: number): PhaseName;
114
+ /** One body entering a mixture. */
115
+ interface MixtureBody {
116
+ label: string;
117
+ /** Mass, kg. */
118
+ massKg: number;
119
+ material: Material;
120
+ /** Starting temperature, °C. */
121
+ temperatureC: number;
122
+ }
123
+ interface MixtureBodyResult {
124
+ label: string;
125
+ /** Heat this body GAINED, J. Negative for a body that cooled. */
126
+ heatGainedJ: number;
127
+ startPhase: PhaseName;
128
+ endPhase: PhaseName;
129
+ /**
130
+ * How far through a transition the mixture ended, 0..1, or `null` when it did
131
+ * not settle at a transition temperature.
132
+ *
133
+ * This is the answer to "how much of the ice melted", which is the part the
134
+ * single final temperature cannot tell you: a mixture that settles at 0 °C
135
+ * with a third of the ice melted and one that settles at 0 °C with all of it
136
+ * melted have the same final temperature and different contents.
137
+ *
138
+ * It is a property of the POOL, not of this body, so every body of the same
139
+ * material sitting at that temperature reports the same number. That is not a
140
+ * simplification: ice at 0 °C is ice at 0 °C, and once two lots of it are in
141
+ * the same beaker there is no fact about which lot a given melted gram came
142
+ * from. `endPhase` stays at the lower phase while this is between 0 and 1, so
143
+ * "solid, 0.21" reads as a fifth melted.
144
+ */
145
+ transitionFraction: number | null;
146
+ }
147
+ interface MixtureResult {
148
+ /** The common final temperature, °C. */
149
+ finalTemperatureC: number;
150
+ bodies: MixtureBodyResult[];
151
+ /**
152
+ * The sum of every body's heat change, J.
153
+ *
154
+ * Reported rather than asserted, following the same choice the energy ledger
155
+ * makes: it must be zero to within rounding, and a caller who wants to prove
156
+ * that gets to see the number rather than take it on faith.
157
+ */
158
+ netHeatJ: number;
159
+ /** True when at least one body ended part-way through melting or boiling. */
160
+ partialTransition: boolean;
161
+ }
162
+ /**
163
+ * The equilibrium temperature of bodies placed in a perfectly insulated
164
+ * container, and what state each ends in.
165
+ *
166
+ * Solves ΣH_i(θ) = ΣH_i(θ_i) exactly, by walking the piecewise-linear total
167
+ * enthalpy curve bracket by bracket. The brackets are the distinct transition
168
+ * temperatures of the materials present; inside one, every body is in a fixed
169
+ * phase and the total is linear, so the crossing point is one division.
170
+ *
171
+ * ASSUMES the container itself has no heat capacity. Fold a real calorimeter in
172
+ * as an extra body, or use `waterEquivalentKg` to express it as more water.
173
+ */
174
+ declare function mixtureEquilibrium(bodies: readonly MixtureBody[]): MixtureResult;
175
+ /**
176
+ * The temperature rise when a fraction of a moving body's kinetic energy turns
177
+ * into heat inside it: f·½mv² = m·c·Δθ.
178
+ *
179
+ * The mass cancels, which is the point and the thing students are surprised by:
180
+ * a bullet's temperature rise on impact depends on its SPEED and its material,
181
+ * not on how big it is. `fraction` is how much of the energy stays in the body
182
+ * rather than going into the target, the air, or sound.
183
+ */
184
+ declare function temperatureRiseFromSpeedK(speedMs: number, specificHeat: number, fraction?: number): number;
185
+ /**
186
+ * The temperature rise when a fraction of a falling body's potential energy
187
+ * turns into heat inside it: f·mgh = m·c·Δθ.
188
+ *
189
+ * The mass cancels here too, so the answer depends only on the DROP. This is
190
+ * Joule's paddle-wheel experiment read backwards, and the classic worked
191
+ * example is the waterfall: the 4.2 kJ it takes to warm a kilogram of water by
192
+ * one degree is the same energy as dropping it 428 m.
193
+ */
194
+ declare function temperatureRiseFromDropK(heightM: number, specificHeat: number, fraction?: number, gravity?: number): number;
195
+ /**
196
+ * The mass melted when a fraction of a falling body's potential energy becomes
197
+ * heat in it: f·mgh = m'·L.
198
+ *
199
+ * Unlike the two above, this one keeps the mass, because the answer is a mass.
200
+ */
201
+ declare function massMeltedFromDropKg(massKg: number, heightM: number, latentHeat: number, fraction?: number, gravity?: number): number;
202
+ /** Calories to joules, through the thermochemical definition. */
203
+ declare const caloriesToJoules: (calories: number) => number;
204
+ /** Joules to calories, through the thermochemical definition. */
205
+ declare const joulesToCalories: (joules: number) => number;
206
+ //#endregion
207
+ export { MATERIALS, Material, MixtureBody, MixtureBodyResult, MixtureResult, PhaseName, caloriesToJoules, enthalpyJ, heatToChangeStateJ, heatToWarmJ, joulesToCalories, massMeltedFromDropKg, mixtureEquilibrium, phaseAt, specificHeatFromJ, temperatureRiseFromDropK, temperatureRiseFromSpeedK, temperatureRiseK, waterEquivalentKg };