@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,154 @@
1
+ //#region src/chem/molecule.d.ts
2
+ /**
3
+ * molecule, a structure as a graph, so the rest can be worked out rather than declared.
4
+ *
5
+ * WHAT THIS IS FOR. Every chemistry lab and lesson we have declares its molecules as DRAWINGS: a
6
+ * chain of vertices with positions, a formula typed in as a string, a functional group named as a
7
+ * list of vertex indices. Nothing about that can be checked. A formula string can disagree with the
8
+ * structure beside it, a group can be named that the structure does not contain, and an isomer list
9
+ * is only as complete as whoever typed it.
10
+ *
11
+ * A graph fixes all of it at once. Declare the heavy atoms and the bonds, and the molecular formula,
12
+ * the relative molecular mass, the degrees of unsaturation, the functional groups and the polymer
13
+ * repeat unit all follow by computation.
14
+ *
15
+ * ## Hydrogens are implicit, and that is the point
16
+ *
17
+ * An author declares carbon, oxygen and nitrogen and never hydrogen, exactly as a skeletal formula
18
+ * is drawn. The hydrogen count comes from valence: four bonds to a carbon, three to a nitrogen, two
19
+ * to an oxygen, one to a halogen. So a structure cannot be declared with a hydrogen count that
20
+ * contradicts its own bonds, because the count is not declared at all.
21
+ *
22
+ * ## Why SMILES
23
+ *
24
+ * Because it is the notation the whole of chemistry already uses, so `CCO` is ethanol here and in
25
+ * every database a learner will ever meet, and because a molecule written as one short string is a
26
+ * molecule an author will actually declare correctly. The subset parsed here is the one A level
27
+ * needs: the organic elements, single, double, triple and aromatic bonds, branches, and ring
28
+ * closures.
29
+ *
30
+ * What is deliberately NOT here: stereochemistry, charges, isotopes and explicit bracket atoms.
31
+ * Those belong in the topics that use them, and a parser that accepted them without the rest of the
32
+ * kernel understanding them would be a promise it could not keep.
33
+ *
34
+ * Pure: no React, no pixels.
35
+ */
36
+ /** The elements A level organic chemistry uses. */
37
+ type Element = 'C' | 'N' | 'O' | 'S' | 'P' | 'F' | 'Cl' | 'Br' | 'I' | 'H';
38
+ /** Relative atomic masses, to the precision a data booklet prints. */
39
+ declare const ATOMIC_MASS: Readonly<Record<Element, number>>;
40
+ interface Atom {
41
+ readonly id: number;
42
+ readonly element: Element;
43
+ /** Part of an aromatic ring, written lowercase in SMILES. */
44
+ readonly aromatic: boolean;
45
+ /**
46
+ * Bonds this atom makes to something OUTSIDE the structure, so they take no hydrogen.
47
+ *
48
+ * An attachment point. A polymer repeat unit has two, one to the unit on each side, and without
49
+ * them the repeat unit of poly(ethene) comes out as C2H6: opening the double bond frees a bond on
50
+ * each carbon, and if nothing claims it a hydrogen does. The unit is CH2-CH2, not ethane.
51
+ */
52
+ readonly openValence?: number;
53
+ /**
54
+ * Formal charge on this atom: +1 on the nitrogen of an ammonium ion, -1 on a carboxylate oxygen.
55
+ *
56
+ * It changes how many bonds the atom makes, which is the reason it has to live on the graph
57
+ * rather than being noted alongside it. A nitrogen makes three bonds and an ammonium nitrogen
58
+ * makes four, and a kernel that did not know the charge would report ammonium as NH3 with a
59
+ * spare bond it could not explain.
60
+ */
61
+ readonly charge?: number;
62
+ /**
63
+ * Hydrogens stated outright rather than worked out from the valence.
64
+ *
65
+ * Set only by a bracket atom, where SMILES requires the count to be written. Everywhere else the
66
+ * count is computed, which is what stops a structure disagreeing with its own bonds.
67
+ */
68
+ readonly explicitHydrogens?: number;
69
+ }
70
+ interface Bond {
71
+ readonly a: number;
72
+ readonly b: number;
73
+ readonly order: 1 | 2 | 3;
74
+ /** An aromatic bond counts as one and a half when valence is totted up. */
75
+ readonly aromatic: boolean;
76
+ }
77
+ interface Structure {
78
+ readonly atoms: readonly Atom[];
79
+ readonly bonds: readonly Bond[];
80
+ }
81
+ /**
82
+ * Parse the SMILES subset A level needs.
83
+ *
84
+ * `CCO` ethanol, `CC(=O)C` propanone, `c1ccccc1` benzene, `CC(=O)OCC` ethyl ethanoate.
85
+ *
86
+ * Refuses anything it does not fully understand rather than guessing, because a parser that
87
+ * silently drops a feature produces a molecule that is not the one the author wrote, and every
88
+ * number computed from it afterwards is then confidently wrong.
89
+ */
90
+ declare function parseSmiles(smiles: string): Structure;
91
+ /** Bonds touching an atom. */
92
+ declare const bondsAt: (structure: Structure, id: number) => Bond[];
93
+ /** The atom on the other end of a bond. */
94
+ declare const otherEnd: (bond: Bond, id: number) => number;
95
+ /** Atoms directly bonded to this one, excluding any explicit hydrogens. */
96
+ declare const neighbours: (structure: Structure, id: number) => Atom[];
97
+ /** How many bonds an atom has used, counting an aromatic bond as one and a half. */
98
+ declare const bondOrderSum: (structure: Structure, id: number) => number;
99
+ /**
100
+ * Hydrogens on an atom, from what its valence has left over.
101
+ *
102
+ * Never declared, which is what stops a structure disagreeing with its own formula. An aromatic
103
+ * carbon in benzene has two aromatic bonds worth 1.5 each, leaving one hydrogen, which is right.
104
+ */
105
+ /**
106
+ * Bonds an atom can make, once its charge is taken into account.
107
+ *
108
+ * The two directions are not symmetrical, and the reason is what the charge did to the electrons.
109
+ *
110
+ * An atom with a lone pair to spare, nitrogen or oxygen or sulfur, makes an EXTRA bond when it
111
+ * loses an electron, because the lone pair becomes a bonding pair: ammonium's nitrogen makes four
112
+ * bonds, not three. Gaining one costs it a bond instead, which is why a carboxylate oxygen makes
113
+ * only one.
114
+ *
115
+ * Carbon has no lone pair to donate, so it loses a bond whichever way it is charged. A carbocation
116
+ * and a carbanion both make three.
117
+ */
118
+ declare function chargedValence(element: Element, charge?: number): number;
119
+ declare function implicitHydrogens(structure: Structure, id: number): number;
120
+ /** Total formal charge on a structure: the charge of the ion it represents. */
121
+ declare const totalCharge: (structure: Structure) => number;
122
+ /** Every element in the structure, counted, hydrogens included. */
123
+ declare function atomCounts(structure: Structure): Record<string, number>;
124
+ /**
125
+ * The molecular formula, in Hill order: carbon, then hydrogen, then everything else alphabetically.
126
+ *
127
+ * Hill order is what a database and a mark scheme both use, so C2H6O and never H6C2O or OC2H6.
128
+ */
129
+ declare function formatCounts(counts: Readonly<Record<string, number>>): string;
130
+ declare function molecularFormula(structure: Structure): string;
131
+ /** Relative molecular mass, from the same counts. */
132
+ declare const relativeMass: (structure: Structure) => number;
133
+ /**
134
+ * Degrees of unsaturation: how many rings plus double-bond equivalents the formula demands.
135
+ *
136
+ * Worth computing because it is the first thing to do with a molecular formula in a structure
137
+ * determination, and because it is the check that catches a proposed structure that cannot exist:
138
+ * a formula with zero degrees cannot contain a C=O however well the rest of the evidence fits.
139
+ *
140
+ * Oxygen does not enter the count, which surprises people every time, and the reason is that adding
141
+ * an oxygen into a chain uses two bonds and supplies two.
142
+ */
143
+ declare function degreesOfUnsaturation(structure: Structure): number;
144
+ /** Atoms in the structure that are carbon. */
145
+ declare const carbonCount: (structure: Structure) => number;
146
+ /**
147
+ * Rings, by the cycle rank of the graph: bonds minus atoms plus connected pieces.
148
+ *
149
+ * Counts independent rings rather than every closed loop, which is the number a chemist means by
150
+ * "how many rings", and naphthalene comes out as two rather than three.
151
+ */
152
+ declare function ringCount(structure: Structure): number;
153
+ //#endregion
154
+ export { ATOMIC_MASS, Atom, Bond, Element, Structure, atomCounts, bondOrderSum, bondsAt, carbonCount, chargedValence, degreesOfUnsaturation, formatCounts, implicitHydrogens, molecularFormula, neighbours, otherEnd, parseSmiles, relativeMass, ringCount, totalCharge };
@@ -0,0 +1,282 @@
1
+ //#region src/chem/molecule.ts
2
+ /** Bonds a valence shell can make, before any is used. */
3
+ const VALENCE = {
4
+ C: 4,
5
+ N: 3,
6
+ O: 2,
7
+ S: 2,
8
+ P: 3,
9
+ F: 1,
10
+ Cl: 1,
11
+ Br: 1,
12
+ I: 1,
13
+ H: 1
14
+ };
15
+ /** Relative atomic masses, to the precision a data booklet prints. */
16
+ const ATOMIC_MASS = {
17
+ H: 1,
18
+ C: 12,
19
+ N: 14,
20
+ O: 16,
21
+ F: 19,
22
+ P: 31,
23
+ S: 32.1,
24
+ Cl: 35.5,
25
+ Br: 79.9,
26
+ I: 126.9
27
+ };
28
+ const TWO_LETTER = new Set(["Cl", "Br"]);
29
+ /**
30
+ * Parse the SMILES subset A level needs.
31
+ *
32
+ * `CCO` ethanol, `CC(=O)C` propanone, `c1ccccc1` benzene, `CC(=O)OCC` ethyl ethanoate.
33
+ *
34
+ * Refuses anything it does not fully understand rather than guessing, because a parser that
35
+ * silently drops a feature produces a molecule that is not the one the author wrote, and every
36
+ * number computed from it afterwards is then confidently wrong.
37
+ */
38
+ function parseSmiles(smiles) {
39
+ const atoms = [];
40
+ const bonds = [];
41
+ const branchStack = [];
42
+ const ringOpen = /* @__PURE__ */ new Map();
43
+ let previous;
44
+ let pendingOrder;
45
+ let pendingAromatic = false;
46
+ const addAtom = (element, aromatic, extra = {}) => {
47
+ const id = atoms.length;
48
+ atoms.push({
49
+ id,
50
+ element,
51
+ aromatic,
52
+ ...extra
53
+ });
54
+ if (previous !== void 0) {
55
+ const order = pendingOrder ?? 1;
56
+ const arom = pendingAromatic || aromatic && atoms[previous].aromatic && pendingOrder === void 0;
57
+ bonds.push({
58
+ a: previous,
59
+ b: id,
60
+ order,
61
+ aromatic: arom
62
+ });
63
+ }
64
+ previous = id;
65
+ pendingOrder = void 0;
66
+ pendingAromatic = false;
67
+ return id;
68
+ };
69
+ let i = 0;
70
+ while (i < smiles.length) {
71
+ const ch = smiles[i];
72
+ if (ch === "[") {
73
+ const close = smiles.indexOf("]", i);
74
+ if (close === -1) throw new Error(`"${smiles}": a bracket atom is never closed.`);
75
+ const inside = smiles.slice(i + 1, close);
76
+ const match = /^([A-Za-z][a-z]?)(?:H(\d*))?(?:(\+|-)(\d*))?$/.exec(inside);
77
+ if (!match) throw new Error(`"${smiles}": cannot read the bracket atom "[${inside}]". Write it as [element], [elementH count] or [elementH count charge], for example [NH4+] or [O-].`);
78
+ const symbol = match[1];
79
+ const aromatic = symbol[0] === symbol[0].toLowerCase() && symbol.length === 1;
80
+ const element = aromatic ? symbol.toUpperCase() : symbol;
81
+ if (!(element in VALENCE)) throw new Error(`"${smiles}": "${element}" is not an element this parser knows.`);
82
+ const explicitHydrogens = match[2] === void 0 ? 0 : match[2] === "" ? 1 : Number(match[2]);
83
+ const size = match[4] ? Number(match[4]) : 1;
84
+ const charge = match[3] === void 0 ? 0 : match[3] === "-" ? -size : size;
85
+ addAtom(element, aromatic, {
86
+ explicitHydrogens,
87
+ ...charge === 0 ? {} : { charge }
88
+ });
89
+ i = close + 1;
90
+ continue;
91
+ }
92
+ if (ch === "(") {
93
+ if (previous === void 0) throw new Error(`"${smiles}": a branch cannot open the string.`);
94
+ branchStack.push(previous);
95
+ i += 1;
96
+ continue;
97
+ }
98
+ if (ch === ")") {
99
+ const back = branchStack.pop();
100
+ if (back === void 0) throw new Error(`"${smiles}": a branch was closed that never opened.`);
101
+ previous = back;
102
+ i += 1;
103
+ continue;
104
+ }
105
+ if (ch === "-" || ch === "=" || ch === "#" || ch === ":") {
106
+ pendingOrder = ch === "=" ? 2 : ch === "#" ? 3 : 1;
107
+ pendingAromatic = ch === ":";
108
+ i += 1;
109
+ continue;
110
+ }
111
+ if (ch >= "1" && ch <= "9") {
112
+ if (previous === void 0) throw new Error(`"${smiles}": a ring closure needs an atom first.`);
113
+ const open = ringOpen.get(ch);
114
+ if (open) {
115
+ ringOpen.delete(ch);
116
+ const order = pendingOrder ?? open.order;
117
+ const arom = pendingAromatic || open.aromatic || atoms[open.atom].aromatic && atoms[previous].aromatic;
118
+ bonds.push({
119
+ a: open.atom,
120
+ b: previous,
121
+ order,
122
+ aromatic: arom
123
+ });
124
+ } else ringOpen.set(ch, {
125
+ atom: previous,
126
+ order: pendingOrder ?? 1,
127
+ aromatic: pendingAromatic
128
+ });
129
+ pendingOrder = void 0;
130
+ pendingAromatic = false;
131
+ i += 1;
132
+ continue;
133
+ }
134
+ const two = smiles.slice(i, i + 2);
135
+ if (TWO_LETTER.has(two)) {
136
+ addAtom(two, false);
137
+ i += 2;
138
+ continue;
139
+ }
140
+ const upper = ch.toUpperCase();
141
+ if (upper in VALENCE) {
142
+ addAtom(upper, ch === ch.toLowerCase() && ch !== ch.toUpperCase());
143
+ i += 1;
144
+ continue;
145
+ }
146
+ throw new Error(`"${smiles}": "${ch}" is outside the SMILES subset this kernel parses.`);
147
+ }
148
+ if (branchStack.length) throw new Error(`"${smiles}": a branch was opened and never closed.`);
149
+ if (ringOpen.size) throw new Error(`"${smiles}": a ring was opened and never closed.`);
150
+ return {
151
+ atoms,
152
+ bonds
153
+ };
154
+ }
155
+ /** Bonds touching an atom. */
156
+ const bondsAt = (structure, id) => structure.bonds.filter((b) => b.a === id || b.b === id);
157
+ /** The atom on the other end of a bond. */
158
+ const otherEnd = (bond, id) => bond.a === id ? bond.b : bond.a;
159
+ /** Atoms directly bonded to this one, excluding any explicit hydrogens. */
160
+ const neighbours = (structure, id) => bondsAt(structure, id).map((b) => structure.atoms[otherEnd(b, id)]);
161
+ /** How many bonds an atom has used, counting an aromatic bond as one and a half. */
162
+ const bondOrderSum = (structure, id) => bondsAt(structure, id).reduce((n, b) => n + (b.aromatic ? 1.5 : b.order), 0);
163
+ /**
164
+ * Hydrogens on an atom, from what its valence has left over.
165
+ *
166
+ * Never declared, which is what stops a structure disagreeing with its own formula. An aromatic
167
+ * carbon in benzene has two aromatic bonds worth 1.5 each, leaving one hydrogen, which is right.
168
+ */
169
+ /**
170
+ * Bonds an atom can make, once its charge is taken into account.
171
+ *
172
+ * The two directions are not symmetrical, and the reason is what the charge did to the electrons.
173
+ *
174
+ * An atom with a lone pair to spare, nitrogen or oxygen or sulfur, makes an EXTRA bond when it
175
+ * loses an electron, because the lone pair becomes a bonding pair: ammonium's nitrogen makes four
176
+ * bonds, not three. Gaining one costs it a bond instead, which is why a carboxylate oxygen makes
177
+ * only one.
178
+ *
179
+ * Carbon has no lone pair to donate, so it loses a bond whichever way it is charged. A carbocation
180
+ * and a carbanion both make three.
181
+ */
182
+ function chargedValence(element, charge = 0) {
183
+ const base = VALENCE[element];
184
+ if (charge === 0) return base;
185
+ if (element === "C") return base - Math.abs(charge);
186
+ return base + charge;
187
+ }
188
+ function implicitHydrogens(structure, id) {
189
+ const atom = structure.atoms[id];
190
+ if (!atom) throw new Error(`No atom ${id} in this structure.`);
191
+ if (atom.element === "H") return 0;
192
+ if (atom.explicitHydrogens !== void 0) return atom.explicitHydrogens;
193
+ const used = bondOrderSum(structure, id) + (atom.openValence ?? 0);
194
+ const valence = chargedValence(atom.element, atom.charge);
195
+ const left = valence - used;
196
+ if (left < -.01) throw new Error(`Atom ${id} (${atom.element}${atom.charge ? atom.charge > 0 ? "+" : "-" : ""}) has ${used} bonds but a valence of ${valence}.`);
197
+ return Math.max(0, Math.round(left));
198
+ }
199
+ /** Total formal charge on a structure: the charge of the ion it represents. */
200
+ const totalCharge = (structure) => structure.atoms.reduce((sum, atom) => sum + (atom.charge ?? 0), 0);
201
+ /** Every element in the structure, counted, hydrogens included. */
202
+ function atomCounts(structure) {
203
+ const counts = {};
204
+ for (const atom of structure.atoms) {
205
+ counts[atom.element] = (counts[atom.element] ?? 0) + 1;
206
+ const h = implicitHydrogens(structure, atom.id);
207
+ if (h > 0) counts.H = (counts.H ?? 0) + h;
208
+ }
209
+ return counts;
210
+ }
211
+ /**
212
+ * The molecular formula, in Hill order: carbon, then hydrogen, then everything else alphabetically.
213
+ *
214
+ * Hill order is what a database and a mark scheme both use, so C2H6O and never H6C2O or OC2H6.
215
+ */
216
+ function formatCounts(counts) {
217
+ const rest = Object.keys(counts).filter((e) => e !== "C" && e !== "H" && counts[e] > 0).sort();
218
+ return [
219
+ ...counts.C ? ["C"] : [],
220
+ ...counts.H ? ["H"] : [],
221
+ ...rest
222
+ ].map((e) => `${e}${counts[e] > 1 ? counts[e] : ""}`).join("");
223
+ }
224
+ function molecularFormula(structure) {
225
+ return formatCounts(atomCounts(structure));
226
+ }
227
+ /** Relative molecular mass, from the same counts. */
228
+ const relativeMass = (structure) => {
229
+ const counts = atomCounts(structure);
230
+ const total = Object.entries(counts).reduce((sum, [element, n]) => sum + ATOMIC_MASS[element] * n, 0);
231
+ return Math.round(total * 10) / 10;
232
+ };
233
+ /**
234
+ * Degrees of unsaturation: how many rings plus double-bond equivalents the formula demands.
235
+ *
236
+ * Worth computing because it is the first thing to do with a molecular formula in a structure
237
+ * determination, and because it is the check that catches a proposed structure that cannot exist:
238
+ * a formula with zero degrees cannot contain a C=O however well the rest of the evidence fits.
239
+ *
240
+ * Oxygen does not enter the count, which surprises people every time, and the reason is that adding
241
+ * an oxygen into a chain uses two bonds and supplies two.
242
+ */
243
+ function degreesOfUnsaturation(structure) {
244
+ const c = atomCounts(structure);
245
+ const carbon = c.C ?? 0;
246
+ const hydrogen = c.H ?? 0;
247
+ const nitrogen = c.N ?? 0;
248
+ const halogen = (c.F ?? 0) + (c.Cl ?? 0) + (c.Br ?? 0) + (c.I ?? 0);
249
+ return (2 * carbon + 2 + nitrogen - hydrogen - halogen) / 2;
250
+ }
251
+ /** Atoms in the structure that are carbon. */
252
+ const carbonCount = (structure) => structure.atoms.filter((a) => a.element === "C").length;
253
+ /**
254
+ * Rings, by the cycle rank of the graph: bonds minus atoms plus connected pieces.
255
+ *
256
+ * Counts independent rings rather than every closed loop, which is the number a chemist means by
257
+ * "how many rings", and naphthalene comes out as two rather than three.
258
+ */
259
+ function ringCount(structure) {
260
+ const seen = /* @__PURE__ */ new Set();
261
+ let pieces = 0;
262
+ for (const atom of structure.atoms) {
263
+ if (seen.has(atom.id)) continue;
264
+ pieces += 1;
265
+ const stack = [atom.id];
266
+ seen.add(atom.id);
267
+ while (stack.length) {
268
+ const at = stack.pop();
269
+ for (const b of bondsAt(structure, at)) {
270
+ const next = otherEnd(b, at);
271
+ if (!seen.has(next)) {
272
+ seen.add(next);
273
+ stack.push(next);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ return structure.bonds.length - structure.atoms.length + pieces;
279
+ }
280
+
281
+ //#endregion
282
+ export { ATOMIC_MASS, atomCounts, bondOrderSum, bondsAt, carbonCount, chargedValence, degreesOfUnsaturation, formatCounts, implicitHydrogens, molecularFormula, neighbours, otherEnd, parseSmiles, relativeMass, ringCount, totalCharge };
@@ -0,0 +1,153 @@
1
+ //#region src/chem/nmr.d.ts
2
+ /**
3
+ * nmr, a proton spectrum predicted from the environments in a molecule.
4
+ *
5
+ * WHAT THIS IS FOR. An NMR question is four questions wearing one coat, and a learner who has been
6
+ * shown a finished spectrum cannot take it apart again:
7
+ *
8
+ * how many signals? one per proton ENVIRONMENT
9
+ * where is each one? its chemical shift, from what the protons are attached to
10
+ * how tall is each one? the integration, in proportion to the number of protons
11
+ * how many lines in it? n + 1, where n counts the protons on the neighbouring carbons
12
+ *
13
+ * All four are computed here from the same declaration, so a lesson can change one environment and
14
+ * watch the right things move. A spectrum drawn by hand can only assert the answer.
15
+ *
16
+ * ## Why the environments are declared and not derived
17
+ *
18
+ * Working out which protons are equivalent means perceiving the symmetry of a structure, which is a
19
+ * real problem and not a 9701 one. What IS 9701, and what is done here, is everything that follows
20
+ * once the environments are known. Declaring them is also how a question is actually set: "the three
21
+ * environments in ethanol" is the given, and the spectrum is the answer.
22
+ *
23
+ * The one thing a declaration must not do is let the two halves disagree, so {@link predictNmr}
24
+ * refuses a molecule whose neighbour counts are impossible for the environments it lists.
25
+ *
26
+ * ## Splitting is Pascal's triangle, and that is worth computing
27
+ *
28
+ * n + 1 gives the number of lines. Their relative heights are the binomial coefficients, so a
29
+ * quartet is 1:3:3:1 and not four equal lines. Drawing four equal lines is the commonest way a
30
+ * textbook figure is wrong, and it is wrong in a way that matters: the outer lines of a large
31
+ * multiplet vanish into the baseline, which is why a septet is often reported as a quintet.
32
+ *
33
+ * Pure: no React, no pixels.
34
+ */
35
+ /** Where a proton environment absorbs, from the ranges a data booklet prints. */
36
+ interface ShiftRange {
37
+ readonly from: number;
38
+ readonly to: number;
39
+ /** What the protons are attached to, as the booklet describes it. */
40
+ readonly environment: string;
41
+ }
42
+ /**
43
+ * Chemical shift ranges, in ppm, as the 9701 data booklet gives them.
44
+ *
45
+ * Ranges, never single values, because that is what the booklet prints and because a learner who
46
+ * memorises "2.2" for a ketone cannot place a shift of 2.4. The question is always which RANGE a
47
+ * peak falls in.
48
+ */
49
+ declare const SHIFT_RANGES: Readonly<Record<string, ShiftRange>>;
50
+ type ShiftKey = keyof typeof SHIFT_RANGES;
51
+ /** One set of protons that the machine cannot tell apart. */
52
+ interface ProtonEnvironment {
53
+ /** What a learner would point at, e.g. 'the CH3 of the ethyl group'. */
54
+ readonly label: string;
55
+ /** How many protons are in this environment. Sets the integration. */
56
+ readonly protons: number;
57
+ /** Protons on the ADJACENT carbons. Sets the splitting, through n + 1. */
58
+ readonly neighbours: number;
59
+ /** Which range of the data booklet this environment falls in. */
60
+ readonly shift: ShiftKey;
61
+ /**
62
+ * Where in that range the peak actually sits, in ppm.
63
+ *
64
+ * Given rather than guessed, because a range is what the booklet prints and a spectrum needs a
65
+ * position. {@link predictNmr} refuses a value outside its own declared range.
66
+ */
67
+ readonly ppm: number;
68
+ /**
69
+ * Does this proton exchange with D2O?
70
+ *
71
+ * The O-H and N-H protons do, which is the whole point of the D2O experiment: the peak vanishes.
72
+ * It is also why they are not split and do not split their neighbours.
73
+ */
74
+ readonly exchangeable?: boolean;
75
+ }
76
+ interface NmrMolecule {
77
+ readonly name: string;
78
+ readonly formula: string;
79
+ readonly environments: readonly ProtonEnvironment[];
80
+ }
81
+ /** The name for a number of lines. Beyond five, a spectrum is reported as a multiplet. */
82
+ declare const MULTIPLICITY_NAMES: readonly ["singlet", "doublet", "triplet", "quartet", "quintet", "sextet", "septet"];
83
+ type Multiplicity = (typeof MULTIPLICITY_NAMES)[number] | 'multiplet';
84
+ /**
85
+ * The n + 1 rule.
86
+ *
87
+ * An exchangeable proton is always a singlet and never splits anything, because it swaps between
88
+ * molecules faster than the machine can see it.
89
+ */
90
+ declare function multiplicityOf(neighbours: number, exchangeable?: boolean): Multiplicity;
91
+ /**
92
+ * Relative heights of the lines in a multiplet: a row of Pascal's triangle.
93
+ *
94
+ * A quartet is 1:3:3:1, not four equal lines. It matters because the outer lines of a large
95
+ * multiplet sink into the baseline, which is why a septet is so often reported as a quintet.
96
+ */
97
+ declare function splittingPattern(lines: number): number[];
98
+ /**
99
+ * Integration as the smallest whole-number ratio.
100
+ *
101
+ * A spectrum reports 2:3, not 4:6, because the integration gives the RATIO of protons and never
102
+ * their number. That distinction is the one an exam question turns on: a 2:3 ratio fits both
103
+ * C2H5-OH and C4H10O2, and the molecular formula is what settles it.
104
+ */
105
+ declare function integrationRatio(environments: readonly ProtonEnvironment[]): number[];
106
+ /** One signal in the predicted spectrum. */
107
+ interface NmrPeak {
108
+ readonly label: string;
109
+ readonly ppm: number;
110
+ readonly protons: number;
111
+ /** This peak's share of the total integration, as a whole number. */
112
+ readonly integration: number;
113
+ readonly multiplicity: Multiplicity;
114
+ /** Number of lines, which is n + 1. */
115
+ readonly lines: number;
116
+ /** Relative height of each line, from Pascal's triangle. */
117
+ readonly pattern: readonly number[];
118
+ readonly range: ShiftRange;
119
+ readonly exchangeable: boolean;
120
+ }
121
+ interface NmrSpectrum {
122
+ readonly molecule: NmrMolecule;
123
+ /** Downfield first, which is how a spectrum is printed: high ppm on the left. */
124
+ readonly peaks: readonly NmrPeak[];
125
+ /** Total protons, which must match the molecular formula. */
126
+ readonly totalProtons: number;
127
+ }
128
+ /**
129
+ * Predict the spectrum.
130
+ *
131
+ * Refuses a declaration that contradicts itself rather than drawing a spectrum nobody could have
132
+ * measured: a peak outside the range it claims, or an exchangeable proton given neighbours to
133
+ * couple to. Both are ways a hand-authored figure goes quietly wrong.
134
+ */
135
+ declare function predictNmr(molecule: NmrMolecule): NmrSpectrum;
136
+ /**
137
+ * The spectrum after a drop of D2O, with every exchangeable peak gone.
138
+ *
139
+ * The experiment exists to answer one question: is that broad singlet an O-H? Running it is how a
140
+ * learner tells an alcohol from an ether without any other evidence, and doing it as a computation
141
+ * rather than a second hand-drawn figure means the two spectra cannot disagree.
142
+ */
143
+ declare const afterD2O: (spectrum: NmrSpectrum) => NmrSpectrum;
144
+ /**
145
+ * The line positions of one peak, in ppm, given the coupling constant.
146
+ *
147
+ * `J` is in hertz and a spectrum is in ppm, so the conversion needs the spectrometer frequency.
148
+ * That is not a detail to hide: it is why the same molecule gives the same splitting PATTERN on
149
+ * every machine while the lines sit closer together on a stronger one.
150
+ */
151
+ declare function linePositions(peak: NmrPeak, jHz?: number, spectrometerMHz?: number): number[];
152
+ //#endregion
153
+ export { MULTIPLICITY_NAMES, Multiplicity, NmrMolecule, NmrPeak, NmrSpectrum, ProtonEnvironment, SHIFT_RANGES, ShiftKey, ShiftRange, afterD2O, integrationRatio, linePositions, multiplicityOf, predictNmr, splittingPattern };