@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,281 @@
1
+ //#region src/chem/synthesis.ts
2
+ /** What a learner calls the group. The name is chemistry; where it sits on a map is not. */
3
+ const GROUP_LABELS = {
4
+ alkane: "alkane",
5
+ alkene: "alkene",
6
+ halogenoalkane: "halogenoalkane",
7
+ alcohol: "alcohol",
8
+ nitrile: "nitrile",
9
+ aldehyde: "aldehyde",
10
+ ketone: "ketone",
11
+ acid: "carboxylic acid",
12
+ ester: "ester",
13
+ "acyl-chloride": "acyl chloride",
14
+ arene: "arene",
15
+ nitroarene: "nitroarene",
16
+ arylamine: "aromatic amine",
17
+ amine: "amine",
18
+ amide: "amide"
19
+ };
20
+ const ROUTE_EDGES = [
21
+ {
22
+ from: "alkane",
23
+ to: "halogenoalkane",
24
+ reagent: "Cl₂ or Br₂",
25
+ condition: "ultraviolet light"
26
+ },
27
+ {
28
+ from: "alkene",
29
+ to: "halogenoalkane",
30
+ reagent: "HBr",
31
+ condition: "room temperature"
32
+ },
33
+ {
34
+ from: "alkene",
35
+ to: "alcohol",
36
+ reagent: "steam",
37
+ condition: "300 °C, 60 atm, H₃PO₄ catalyst"
38
+ },
39
+ {
40
+ from: "halogenoalkane",
41
+ to: "alcohol",
42
+ reagent: "NaOH",
43
+ condition: "aqueous, warm"
44
+ },
45
+ {
46
+ from: "halogenoalkane",
47
+ to: "alkene",
48
+ reagent: "NaOH",
49
+ condition: "in ethanol, hot, under reflux"
50
+ },
51
+ {
52
+ from: "halogenoalkane",
53
+ to: "nitrile",
54
+ reagent: "KCN",
55
+ condition: "in ethanol, under reflux",
56
+ changesChain: true
57
+ },
58
+ {
59
+ from: "nitrile",
60
+ to: "acid",
61
+ reagent: "dilute HCl",
62
+ condition: "heat under reflux"
63
+ },
64
+ {
65
+ from: "alcohol",
66
+ to: "alkene",
67
+ reagent: "conc. H₂SO₄",
68
+ condition: "170 °C, dehydration"
69
+ },
70
+ {
71
+ from: "alcohol",
72
+ to: "halogenoalkane",
73
+ reagent: "HBr or PCl₅",
74
+ condition: "HBr made in situ from NaBr and conc. H₂SO₄, or PCl₅ at room temperature"
75
+ },
76
+ {
77
+ from: "alcohol",
78
+ to: "aldehyde",
79
+ reagent: "K₂Cr₂O₇ / H⁺",
80
+ condition: "distil the product off as it forms",
81
+ needs: "a primary alcohol"
82
+ },
83
+ {
84
+ from: "alcohol",
85
+ to: "ketone",
86
+ reagent: "K₂Cr₂O₇ / H⁺",
87
+ condition: "heat under reflux",
88
+ needs: "a secondary alcohol"
89
+ },
90
+ {
91
+ from: "aldehyde",
92
+ to: "acid",
93
+ reagent: "K₂Cr₂O₇ / H⁺",
94
+ condition: "heat under reflux"
95
+ },
96
+ {
97
+ from: "aldehyde",
98
+ to: "alcohol",
99
+ reagent: "NaBH₄",
100
+ condition: "in aqueous ethanol"
101
+ },
102
+ {
103
+ from: "ketone",
104
+ to: "alcohol",
105
+ reagent: "NaBH₄",
106
+ condition: "in aqueous ethanol"
107
+ },
108
+ {
109
+ from: "acid",
110
+ to: "ester",
111
+ reagent: "an alcohol",
112
+ condition: "conc. H₂SO₄ catalyst, warm"
113
+ },
114
+ {
115
+ from: "ester",
116
+ to: "acid",
117
+ reagent: "dilute H₂SO₄",
118
+ condition: "heat under reflux"
119
+ },
120
+ {
121
+ from: "acid",
122
+ to: "acyl-chloride",
123
+ reagent: "PCl₅ or SOCl₂",
124
+ condition: "room temperature, in dry conditions"
125
+ },
126
+ {
127
+ from: "acyl-chloride",
128
+ to: "ester",
129
+ reagent: "an alcohol",
130
+ condition: "room temperature, no catalyst, and it goes to completion"
131
+ },
132
+ {
133
+ from: "acyl-chloride",
134
+ to: "acid",
135
+ reagent: "water",
136
+ condition: "room temperature, vigorous, fumes of HCl"
137
+ },
138
+ {
139
+ from: "arene",
140
+ to: "nitroarene",
141
+ reagent: "conc. HNO₃ with conc. H₂SO₄",
142
+ condition: "below 55 °C"
143
+ },
144
+ {
145
+ from: "nitroarene",
146
+ to: "arylamine",
147
+ reagent: "Sn with conc. HCl, then NaOH(aq)",
148
+ condition: "reflux, then make alkaline to free the amine"
149
+ },
150
+ {
151
+ from: "nitrile",
152
+ to: "amine",
153
+ reagent: "LiAlH₄, or H₂ with a nickel catalyst",
154
+ condition: "LiAlH₄ in dry ether, or H₂ at high temperature and pressure"
155
+ },
156
+ {
157
+ from: "acyl-chloride",
158
+ to: "amide",
159
+ reagent: "NH₃ or a primary amine",
160
+ condition: "room temperature, two moles of the amine"
161
+ },
162
+ {
163
+ from: "arylamine",
164
+ to: "amide",
165
+ reagent: "an acyl chloride",
166
+ condition: "room temperature"
167
+ }
168
+ ];
169
+ /** Every group the map knows, in the order the type declares them. */
170
+ const GROUP_NODES = Object.keys(GROUP_LABELS);
171
+ /** Roads leaving a group. */
172
+ const edgesFrom = (id) => ROUTE_EDGES.filter((edge) => edge.from === id);
173
+ /** The road between two groups, if there is one. */
174
+ const edgeBetween = (from, to) => ROUTE_EDGES.find((edge) => edge.from === from && edge.to === to);
175
+ /** The roads along a path of groups, so a route can be read back as an answer. */
176
+ function edgesAlong(path) {
177
+ const out = [];
178
+ for (let i = 0; i < path.length - 1; i += 1) {
179
+ const edge = edgeBetween(path[i], path[i + 1]);
180
+ if (edge) out.push(edge);
181
+ }
182
+ return out;
183
+ }
184
+ /**
185
+ * Shortest path length between two groups, by breadth-first search.
186
+ *
187
+ * `undefined` when no route exists, which is a real answer rather than a failure: a learner
188
+ * hunting for a reaction that does not exist is exactly what a route map is for.
189
+ */
190
+ function shortestSteps(from, to) {
191
+ if (from === to) return 0;
192
+ const seen = new Set([from]);
193
+ let frontier = [from];
194
+ let depth = 0;
195
+ while (frontier.length) {
196
+ depth += 1;
197
+ const next = [];
198
+ for (const node of frontier) for (const edge of edgesFrom(node)) {
199
+ if (edge.to === to) return depth;
200
+ if (seen.has(edge.to)) continue;
201
+ seen.add(edge.to);
202
+ next.push(edge.to);
203
+ }
204
+ frontier = next;
205
+ }
206
+ }
207
+ const asRoute = (path) => {
208
+ const steps = edgesAlong(path);
209
+ return {
210
+ path,
211
+ steps,
212
+ changesChain: steps.some((s) => s.changesChain === true)
213
+ };
214
+ };
215
+ /**
216
+ * One shortest route, with the reagent and condition for every step.
217
+ *
218
+ * This is the difference between a distance and an answer. A lesson claiming "alkene to carboxylic
219
+ * acid in three steps" can now be checked against the route the map actually gives, and the steps
220
+ * it returns are the marks.
221
+ *
222
+ * ## Why a plain breadth-first search was not enough
223
+ *
224
+ * Alkene to carboxylic acid has TWO routes of three steps. One hydrates and oxidises twice and
225
+ * keeps the carbon skeleton. The other goes through the nitrile, and a nitrile road adds a carbon,
226
+ * so it arrives at an acid ONE CARBON LONGER than the target. A search that returned whichever it
227
+ * reached first would hand a learner the wrong molecule half the time, and would do it silently,
228
+ * because both routes are three steps and both are real chemistry.
229
+ *
230
+ * So among equally short routes the chain-preserving one wins. A question that wants the longer
231
+ * chain says so, and {@link allRoutes} still has both.
232
+ */
233
+ function findRoute(from, to) {
234
+ if (from === to) return asRoute([from]);
235
+ const depth = shortestSteps(from, to);
236
+ if (depth === void 0) return void 0;
237
+ return allRoutes(from, to, depth).filter((r) => r.steps.length === depth).sort((a, b) => Number(a.changesChain) - Number(b.changesChain))[0];
238
+ }
239
+ /**
240
+ * Every route from one group to another within a step budget, shortest first.
241
+ *
242
+ * Exists because the shortest route is not always the best one, which is the single most arguable
243
+ * point in the topic. Esterifying an acid directly is one step and an equilibrium that stops well
244
+ * short; going via the acyl chloride is two steps and goes to completion. Both routes have to be
245
+ * on the table for that to be a judgement rather than a rule.
246
+ *
247
+ * `maxSteps` is capped at six, because the map's diameter is smaller than that and an uncapped
248
+ * enumeration on a cyclic graph does not terminate usefully.
249
+ */
250
+ function allRoutes(from, to, maxSteps = 4) {
251
+ const limit = Math.min(maxSteps, 6);
252
+ const found = [];
253
+ const walk = (path, visited) => {
254
+ const head = path[path.length - 1];
255
+ if (head === to && path.length > 1) {
256
+ found.push(asRoute([...path]));
257
+ return;
258
+ }
259
+ if (path.length - 1 >= limit) return;
260
+ for (const edge of edgesFrom(head)) {
261
+ if (visited.has(edge.to)) continue;
262
+ visited.add(edge.to);
263
+ path.push(edge.to);
264
+ walk(path, visited);
265
+ path.pop();
266
+ visited.delete(edge.to);
267
+ }
268
+ };
269
+ walk([from], new Set([from]));
270
+ return found.sort((a, b) => a.steps.length - b.steps.length);
271
+ }
272
+ /**
273
+ * The reactions that change the number of carbon atoms.
274
+ *
275
+ * The most useful fact in the topic and the one that is invisible in a list: to reach a target with
276
+ * more carbons than the starting material, a route MUST pass through one of these.
277
+ */
278
+ const chainChangingEdges = () => ROUTE_EDGES.filter((edge) => edge.changesChain === true);
279
+
280
+ //#endregion
281
+ export { GROUP_LABELS, GROUP_NODES, ROUTE_EDGES, allRoutes, chainChangingEdges, edgeBetween, edgesAlong, edgesFrom, findRoute, shortestSteps };
@@ -0,0 +1,80 @@
1
+ import { Centre, CentreOptions } from "./species.mjs";
2
+ import { LewisCount } from "./lewis.mjs";
3
+ import { Vec3 } from "../core/vec3.mjs";
4
+
5
+ //#region src/chem/vsepr.d.ts
6
+ /** The arrangement of ALL regions, lone pairs included. */
7
+ type ElectronGeometry = 'linear' | 'trigonal planar' | 'tetrahedral' | 'trigonal bipyramidal' | 'octahedral';
8
+ /**
9
+ * The directions the regions point, for each steric number.
10
+ *
11
+ * Real coordinates, so the same numbers serve a flat diagram and a three-dimensional one. The
12
+ * tetrahedron is built from alternate corners of a cube, which is where its angle comes from.
13
+ */
14
+ declare function directionsFor(stericNumber: number): Vec3[];
15
+ /**
16
+ * The angle between two directions, in DEGREES.
17
+ *
18
+ * Degrees rather than radians because every bond angle a chemist quotes is in
19
+ * degrees. The kernel's `angleBetween3` returns radians, as a maths kernel
20
+ * should; this is the one conversion, in the one place the unit is a
21
+ * convention of the subject rather than of the arithmetic.
22
+ */
23
+ declare const angleBetween: (a: Vec3, b: Vec3) => number;
24
+ /**
25
+ * Choose which positions the lone pairs take, by least repulsion.
26
+ *
27
+ * Every combination is tried and ranked on the count of close contacts, worst kind first: lone pair
28
+ * against lone pair, then lone pair against bond pair, then bond against bond. The first arrangement
29
+ * that beats the rest is returned, which is how the equatorial rule and the opposite-corners rule
30
+ * fall out instead of being stated.
31
+ */
32
+ declare function placeLonePairs(stericNumber: number, lonePairs: number): number[];
33
+ /** One direction out of the central atom: either a bond to something, or a lone pair. */
34
+ interface Region {
35
+ readonly direction: Vec3;
36
+ readonly kind: 'bond' | 'lone pair';
37
+ /** The element at the far end, for a bond. */
38
+ readonly element?: string;
39
+ }
40
+ interface MolecularShape {
41
+ readonly centre: Centre;
42
+ readonly count: LewisCount;
43
+ /** The arrangement of every region, lone pairs included. */
44
+ readonly electronGeometry: ElectronGeometry;
45
+ /** The shape, which is the arrangement with the lone pairs removed. */
46
+ readonly shape: string;
47
+ /** Hybridisation implied by the steric number. */
48
+ readonly hybridisation: string;
49
+ /**
50
+ * The angle the arrangement would have with no lone pairs, in degrees.
51
+ *
52
+ * Geometry, so it is exact: 109.47 for a tetrahedron, not 109.5. Two values where the arrangement
53
+ * has two distinct angles, as a trigonal bipyramid does.
54
+ */
55
+ readonly idealAngle: readonly number[];
56
+ /** Every direction out of the central atom, bonds and lone pairs together. */
57
+ readonly regions: readonly Region[];
58
+ /** Just the bonds, which is what the shape is named after. */
59
+ readonly bondDirections: readonly Vec3[];
60
+ }
61
+ /**
62
+ * Work out the shape of a species written as a formula.
63
+ *
64
+ * `shapeOf('XeF4')`, `shapeOf('NH4+')`, `shapeOf('SO4^2-')`, `shapeOf('SF4')`.
65
+ */
66
+ declare function shapeOf(formula: string, options?: CentreOptions): MolecularShape;
67
+ /**
68
+ * Bond angles that have been measured, in degrees.
69
+ *
70
+ * Kept apart from the ideal angles because they are a different kind of thing. VSEPR predicts that
71
+ * a lone pair squeezes the remaining angle; it does not predict by how much, and the numbers below
72
+ * are experimental values from standard structural data, not outputs of the model.
73
+ *
74
+ * They are worth having because the pattern in them is the lesson: ammonia at 107.0 and phosphine
75
+ * at 93.5 are the same shape with the same number of lone pairs, and the angle collapses down the
76
+ * group, which is something the model alone would never suggest.
77
+ */
78
+ declare const OBSERVED_ANGLES: Readonly<Record<string, number>>;
79
+ //#endregion
80
+ export { ElectronGeometry, MolecularShape, OBSERVED_ANGLES, Region, angleBetween, directionsFor, placeLonePairs, shapeOf };
@@ -0,0 +1,247 @@
1
+ import { angleBetween3, unit3, v3 } from "../core/vec3.mjs";
2
+ import { parseCentre } from "./species.mjs";
3
+ import { lewisCount } from "./lewis.mjs";
4
+
5
+ //#region src/chem/vsepr.ts
6
+ /**
7
+ * The shape of a molecule, from the electron budget.
8
+ *
9
+ * ## One idea, applied consistently
10
+ *
11
+ * Regions of electron density around an atom get as far from each other as they can. Count the
12
+ * regions (that is the steric number) and their arrangement follows: two go opposite, three make a
13
+ * triangle, four a tetrahedron, five a trigonal bipyramid, six an octahedron. The SHAPE is then
14
+ * what is left when the lone pairs are made invisible, because a shape is the positions of atoms
15
+ * and a lone pair is not an atom. That is the whole of VSEPR, and it is why ammonia and methane
16
+ * have the same arrangement and different shapes.
17
+ *
18
+ * ## Where the lone pairs sit is DERIVED, not looked up
19
+ *
20
+ * Textbooks state that a lone pair in a trigonal bipyramid goes equatorial, and that two lone pairs
21
+ * in an octahedron go opposite each other. Both are usually given as facts to remember. They are
22
+ * neither arbitrary nor separate: they are what you get by trying every arrangement and keeping the
23
+ * one with the least repulsion, ranked the way Gillespie ranks it:
24
+ *
25
+ * lone pair / lone pair is worse than
26
+ * lone pair / bond pair is worse than
27
+ * bond pair / bond pair
28
+ *
29
+ * because a lone pair is held by one nucleus instead of two and so spreads out further. Only the
30
+ * near-90-degree contacts matter; anything at 120 degrees or more is already far enough away to
31
+ * ignore, which is why the comparison is a count and not an integral.
32
+ *
33
+ * {@link placeLonePairs} does that search. So a lone pair in a trigonal bipyramid comes out
34
+ * equatorial here because an equatorial position has two neighbours at 90 degrees and an axial one
35
+ * has three, and a learner can be shown the losing arrangement beside the winning one.
36
+ *
37
+ * ## Ideal angles are geometry; real angles are measurements
38
+ *
39
+ * The tetrahedral angle is arccos(-1/3), which is exact and is computed. The 104.5 degrees in water
40
+ * is a measurement, and no amount of geometry will produce it, because how far a lone pair pushes is
41
+ * not something the model predicts. The two are kept apart: {@link Geometry.idealAngle} is derived,
42
+ * and {@link OBSERVED_ANGLES} is data with its provenance stated.
43
+ *
44
+ * Pure: no React, no pixels.
45
+ */
46
+ const ELECTRON_GEOMETRY = {
47
+ 2: "linear",
48
+ 3: "trigonal planar",
49
+ 4: "tetrahedral",
50
+ 5: "trigonal bipyramidal",
51
+ 6: "octahedral"
52
+ };
53
+ const HYBRIDISATION = {
54
+ 2: "sp",
55
+ 3: "sp²",
56
+ 4: "sp³",
57
+ 5: "sp³d",
58
+ 6: "sp³d²"
59
+ };
60
+ /** The shape, which is the arrangement with the lone pairs taken away. */
61
+ const SHAPES = {
62
+ "2,0": "linear",
63
+ "3,0": "trigonal planar",
64
+ "3,1": "bent",
65
+ "4,0": "tetrahedral",
66
+ "4,1": "trigonal pyramidal",
67
+ "4,2": "bent",
68
+ "5,0": "trigonal bipyramidal",
69
+ "5,1": "seesaw",
70
+ "5,2": "T-shaped",
71
+ "5,3": "linear",
72
+ "6,0": "octahedral",
73
+ "6,1": "square pyramidal",
74
+ "6,2": "square planar",
75
+ "6,3": "T-shaped"
76
+ };
77
+ const unit = (x, y, z) => unit3(v3(x, y, z));
78
+ /**
79
+ * The directions the regions point, for each steric number.
80
+ *
81
+ * Real coordinates, so the same numbers serve a flat diagram and a three-dimensional one. The
82
+ * tetrahedron is built from alternate corners of a cube, which is where its angle comes from.
83
+ */
84
+ function directionsFor(stericNumber) {
85
+ switch (stericNumber) {
86
+ case 2: return [unit(1, 0, 0), unit(-1, 0, 0)];
87
+ case 3: return [
88
+ 0,
89
+ 1,
90
+ 2
91
+ ].map((k) => unit(Math.cos(2 * Math.PI * k / 3), Math.sin(2 * Math.PI * k / 3), 0));
92
+ case 4: return [
93
+ unit(1, 1, 1),
94
+ unit(1, -1, -1),
95
+ unit(-1, 1, -1),
96
+ unit(-1, -1, 1)
97
+ ];
98
+ case 5: return [
99
+ ...[
100
+ 0,
101
+ 1,
102
+ 2
103
+ ].map((k) => unit(Math.cos(2 * Math.PI * k / 3), Math.sin(2 * Math.PI * k / 3), 0)),
104
+ unit(0, 0, 1),
105
+ unit(0, 0, -1)
106
+ ];
107
+ case 6: return [
108
+ unit(1, 0, 0),
109
+ unit(-1, 0, 0),
110
+ unit(0, 1, 0),
111
+ unit(0, -1, 0),
112
+ unit(0, 0, 1),
113
+ unit(0, 0, -1)
114
+ ];
115
+ default: throw new Error(`steric number ${stericNumber} is outside the range this engine describes (2 to 6)`);
116
+ }
117
+ }
118
+ /**
119
+ * The angle between two directions, in DEGREES.
120
+ *
121
+ * Degrees rather than radians because every bond angle a chemist quotes is in
122
+ * degrees. The kernel's `angleBetween3` returns radians, as a maths kernel
123
+ * should; this is the one conversion, in the one place the unit is a
124
+ * convention of the subject rather than of the arithmetic.
125
+ */
126
+ const angleBetween = (a, b) => angleBetween3(a, b) * 180 / Math.PI;
127
+ /** Contacts close enough to matter. Anything at 120 degrees or more is already far apart. */
128
+ const CLOSE = 100;
129
+ /**
130
+ * Choose which positions the lone pairs take, by least repulsion.
131
+ *
132
+ * Every combination is tried and ranked on the count of close contacts, worst kind first: lone pair
133
+ * against lone pair, then lone pair against bond pair, then bond against bond. The first arrangement
134
+ * that beats the rest is returned, which is how the equatorial rule and the opposite-corners rule
135
+ * fall out instead of being stated.
136
+ */
137
+ function placeLonePairs(stericNumber, lonePairs) {
138
+ const directions = directionsFor(stericNumber);
139
+ if (lonePairs === 0) return [];
140
+ if (lonePairs > stericNumber) throw new Error(`cannot place ${lonePairs} lone pairs in ${stericNumber} positions`);
141
+ const score = (chosen) => {
142
+ const isLone = (i) => chosen.includes(i);
143
+ let loneLone = 0;
144
+ let loneBond = 0;
145
+ let bondBond = 0;
146
+ for (let i = 0; i < directions.length; i += 1) for (let j = i + 1; j < directions.length; j += 1) {
147
+ if (angleBetween(directions[i], directions[j]) >= CLOSE) continue;
148
+ const lones = Number(isLone(i)) + Number(isLone(j));
149
+ if (lones === 2) loneLone += 1;
150
+ else if (lones === 1) loneBond += 1;
151
+ else bondBond += 1;
152
+ }
153
+ return [
154
+ loneLone,
155
+ loneBond,
156
+ bondBond
157
+ ];
158
+ };
159
+ const combinations = [];
160
+ const build = (start, picked) => {
161
+ if (picked.length === lonePairs) {
162
+ combinations.push([...picked]);
163
+ return;
164
+ }
165
+ for (let i = start; i < stericNumber; i += 1) build(i + 1, [...picked, i]);
166
+ };
167
+ build(0, []);
168
+ let best = combinations[0];
169
+ let bestScore = score(best);
170
+ for (const candidate of combinations.slice(1)) {
171
+ const candidateScore = score(candidate);
172
+ const better = candidateScore.findIndex((value, k) => value !== bestScore[k]);
173
+ if (better !== -1 && candidateScore[better] < bestScore[better]) {
174
+ best = candidate;
175
+ bestScore = candidateScore;
176
+ }
177
+ }
178
+ return best;
179
+ }
180
+ /** Ideal angles for an arrangement, computed from the directions themselves. */
181
+ function idealAnglesFor(stericNumber) {
182
+ const directions = directionsFor(stericNumber);
183
+ const angles = /* @__PURE__ */ new Set();
184
+ for (let i = 0; i < directions.length; i += 1) for (let j = i + 1; j < directions.length; j += 1) angles.add(Math.round(angleBetween(directions[i], directions[j]) * 100) / 100);
185
+ return [...angles].sort((a, b) => a - b).filter((a) => a < 180 || angles.size === 1);
186
+ }
187
+ /**
188
+ * Work out the shape of a species written as a formula.
189
+ *
190
+ * `shapeOf('XeF4')`, `shapeOf('NH4+')`, `shapeOf('SO4^2-')`, `shapeOf('SF4')`.
191
+ */
192
+ function shapeOf(formula, options = {}) {
193
+ const centre = parseCentre(formula, options);
194
+ const count = lewisCount(centre);
195
+ if (count.impossible) throw new Error(count.impossible);
196
+ const { stericNumber, lonePairs } = count;
197
+ const electronGeometry = ELECTRON_GEOMETRY[stericNumber];
198
+ const shape = SHAPES[`${stericNumber},${lonePairs}`];
199
+ if (!electronGeometry || !shape) throw new Error(`${formula} works out to ${stericNumber} regions with ${lonePairs} lone pairs, which is outside the range this engine describes`);
200
+ const directions = directionsFor(stericNumber);
201
+ const lonePositions = new Set(placeLonePairs(stericNumber, lonePairs));
202
+ const bondedElements = centre.bonded.flatMap((l) => Array(l.count).fill(l.element));
203
+ let next = 0;
204
+ const regions = directions.map((direction, index) => lonePositions.has(index) ? {
205
+ direction,
206
+ kind: "lone pair"
207
+ } : {
208
+ direction,
209
+ kind: "bond",
210
+ element: bondedElements[next++] ?? centre.central
211
+ });
212
+ return {
213
+ centre,
214
+ count,
215
+ electronGeometry,
216
+ shape,
217
+ hybridisation: HYBRIDISATION[stericNumber],
218
+ idealAngle: idealAnglesFor(stericNumber),
219
+ regions,
220
+ bondDirections: regions.filter((r) => r.kind === "bond").map((r) => r.direction)
221
+ };
222
+ }
223
+ /**
224
+ * Bond angles that have been measured, in degrees.
225
+ *
226
+ * Kept apart from the ideal angles because they are a different kind of thing. VSEPR predicts that
227
+ * a lone pair squeezes the remaining angle; it does not predict by how much, and the numbers below
228
+ * are experimental values from standard structural data, not outputs of the model.
229
+ *
230
+ * They are worth having because the pattern in them is the lesson: ammonia at 107.0 and phosphine
231
+ * at 93.5 are the same shape with the same number of lone pairs, and the angle collapses down the
232
+ * group, which is something the model alone would never suggest.
233
+ */
234
+ const OBSERVED_ANGLES = {
235
+ CH4: 109.5,
236
+ NH3: 107,
237
+ H2O: 104.5,
238
+ PH3: 93.5,
239
+ H2S: 92.1,
240
+ NF3: 102.2,
241
+ SO2: 119,
242
+ BF3: 120,
243
+ CO2: 180
244
+ };
245
+
246
+ //#endregion
247
+ export { OBSERVED_ANGLES, angleBetween, directionsFor, placeLonePairs, shapeOf };