@classytic/stage 0.3.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 (431) hide show
  1. package/dist/_virtual/_rolldown/runtime.mjs +18 -0
  2. package/dist/atomic/aufbau.d.mts +144 -0
  3. package/dist/atomic/aufbau.mjs +384 -0
  4. package/dist/atomic/elements.d.mts +67 -0
  5. package/dist/atomic/elements.mjs +785 -0
  6. package/dist/atomic/emission.d.mts +112 -0
  7. package/dist/atomic/emission.mjs +179 -0
  8. package/dist/atomic/index.d.mts +8 -0
  9. package/dist/atomic/index.mjs +9 -0
  10. package/dist/atomic/periodic.d.mts +82 -0
  11. package/dist/atomic/periodic.mjs +231 -0
  12. package/dist/atomic/properties.d.mts +100 -0
  13. package/dist/atomic/properties.mjs +574 -0
  14. package/dist/atomic/shells.d.mts +137 -0
  15. package/dist/atomic/shells.mjs +170 -0
  16. package/dist/atomic/trends.d.mts +148 -0
  17. package/dist/atomic/trends.mjs +225 -0
  18. package/dist/chem/chromatography.d.mts +66 -0
  19. package/dist/chem/chromatography.mjs +63 -0
  20. package/dist/chem/complex.d.mts +114 -0
  21. package/dist/chem/complex.mjs +234 -0
  22. package/dist/chem/cycle.d.mts +75 -0
  23. package/dist/chem/cycle.mjs +145 -0
  24. package/dist/chem/equation.d.mts +86 -0
  25. package/dist/chem/equation.mjs +168 -0
  26. package/dist/chem/formula.d.mts +90 -0
  27. package/dist/chem/formula.mjs +201 -0
  28. package/dist/chem/functional-groups.d.mts +31 -0
  29. package/dist/chem/functional-groups.mjs +187 -0
  30. package/dist/chem/hazard.d.mts +94 -0
  31. package/dist/chem/hazard.mjs +329 -0
  32. package/dist/chem/index.d.mts +34 -33
  33. package/dist/chem/index.mjs +34 -28
  34. package/dist/chem/ionic.d.mts +98 -0
  35. package/dist/chem/ionic.mjs +156 -0
  36. package/dist/chem/layout.d.mts +46 -0
  37. package/dist/chem/layout.mjs +342 -0
  38. package/dist/chem/lewis.d.mts +79 -0
  39. package/dist/chem/lewis.mjs +163 -0
  40. package/dist/chem/molecular-orbital.d.mts +114 -0
  41. package/dist/chem/molecular-orbital.mjs +201 -0
  42. package/dist/chem/molecule.d.mts +154 -0
  43. package/dist/chem/molecule.mjs +282 -0
  44. package/dist/chem/nmr.d.mts +153 -0
  45. package/dist/chem/nmr.mjs +165 -0
  46. package/dist/chem/polymer.d.mts +65 -0
  47. package/dist/chem/polymer.mjs +179 -0
  48. package/dist/chem/redox.d.mts +138 -0
  49. package/dist/chem/redox.mjs +328 -0
  50. package/dist/chem/relations.d.mts +183 -0
  51. package/dist/chem/relations.mjs +299 -0
  52. package/dist/chem/skeleton.d.mts +51 -0
  53. package/dist/chem/skeleton.mjs +186 -0
  54. package/dist/chem/solution.d.mts +116 -0
  55. package/dist/chem/solution.mjs +186 -0
  56. package/dist/chem/species.d.mts +47 -0
  57. package/dist/chem/species.mjs +128 -0
  58. package/dist/chem/stereo.d.mts +108 -0
  59. package/dist/chem/stereo.mjs +286 -0
  60. package/dist/chem/stoichiometry.d.mts +95 -0
  61. package/dist/chem/stoichiometry.mjs +145 -0
  62. package/dist/chem/synthesis.d.mts +113 -0
  63. package/dist/chem/synthesis.mjs +281 -0
  64. package/dist/chem/vsepr.d.mts +80 -0
  65. package/dist/chem/vsepr.mjs +247 -0
  66. package/dist/circuit/ac.d.mts +163 -0
  67. package/dist/circuit/ac.mjs +452 -0
  68. package/dist/circuit/dc.d.mts +92 -0
  69. package/dist/circuit/dc.mjs +333 -0
  70. package/dist/circuit/index.d.mts +5 -92
  71. package/dist/circuit/index.mjs +5 -332
  72. package/dist/circuit/network.d.mts +99 -0
  73. package/dist/circuit/network.mjs +440 -0
  74. package/dist/circuit/topology.d.mts +102 -0
  75. package/dist/circuit/topology.mjs +143 -0
  76. package/dist/colour/absorption.d.mts +79 -0
  77. package/dist/colour/absorption.mjs +137 -0
  78. package/dist/colour/index.d.mts +6 -0
  79. package/dist/colour/index.mjs +7 -0
  80. package/dist/colour/name.d.mts +14 -0
  81. package/dist/colour/name.mjs +76 -0
  82. package/dist/colour/observer.d.mts +31 -0
  83. package/dist/colour/observer.mjs +46 -0
  84. package/dist/colour/spectrum.d.mts +61 -0
  85. package/dist/colour/spectrum.mjs +108 -0
  86. package/dist/colour/srgb.d.mts +49 -0
  87. package/dist/colour/srgb.mjs +108 -0
  88. package/dist/core/constants.d.mts +138 -0
  89. package/dist/core/constants.mjs +139 -0
  90. package/dist/core/index.d.mts +3 -1
  91. package/dist/core/index.mjs +3 -1
  92. package/dist/core/vec3.d.mts +94 -0
  93. package/dist/core/vec3.mjs +123 -0
  94. package/dist/core/worked.d.mts +35 -0
  95. package/dist/core/worked.mjs +44 -0
  96. package/dist/electro/cell.d.mts +102 -0
  97. package/dist/electro/cell.mjs +170 -0
  98. package/dist/electro/conductivity.d.mts +157 -0
  99. package/dist/electro/conductivity.mjs +156 -0
  100. package/dist/electro/couples.d.mts +114 -0
  101. package/dist/electro/couples.mjs +334 -0
  102. package/dist/electro/electrolysis.d.mts +96 -0
  103. package/dist/electro/electrolysis.mjs +274 -0
  104. package/dist/electro/faraday.d.mts +150 -0
  105. package/dist/electro/faraday.mjs +118 -0
  106. package/dist/electro/index.d.mts +6 -0
  107. package/dist/electro/index.mjs +7 -0
  108. package/dist/finance/accounting/adjustments.d.mts +17 -0
  109. package/dist/finance/accounting/adjustments.mjs +202 -0
  110. package/dist/finance/accounting/analysis.d.mts +8 -0
  111. package/dist/finance/accounting/analysis.mjs +205 -0
  112. package/dist/finance/accounting/clubs.d.mts +21 -0
  113. package/dist/finance/accounting/clubs.mjs +217 -0
  114. package/dist/finance/accounting/company-statements.d.mts +7 -0
  115. package/dist/finance/accounting/company-statements.mjs +150 -0
  116. package/dist/finance/accounting/harness.d.mts +7 -0
  117. package/dist/finance/accounting/harness.mjs +45 -0
  118. package/dist/finance/accounting/index.d.mts +18 -0
  119. package/dist/finance/accounting/index.mjs +18 -0
  120. package/dist/finance/accounting/journalize.d.mts +16 -0
  121. package/dist/finance/accounting/journalize.mjs +128 -0
  122. package/dist/finance/accounting/manufacturing.d.mts +8 -0
  123. package/dist/finance/accounting/manufacturing.mjs +236 -0
  124. package/dist/finance/accounting/partnership.d.mts +7 -0
  125. package/dist/finance/accounting/partnership.mjs +152 -0
  126. package/dist/finance/accounting/period-case.d.mts +6 -0
  127. package/dist/finance/accounting/period-case.mjs +71 -0
  128. package/dist/finance/accounting/post.d.mts +9 -0
  129. package/dist/finance/accounting/post.mjs +84 -0
  130. package/dist/finance/accounting/precision.d.mts +9 -0
  131. package/dist/finance/accounting/precision.mjs +15 -0
  132. package/dist/finance/accounting/rules.d.mts +21 -0
  133. package/dist/finance/accounting/rules.mjs +37 -0
  134. package/dist/finance/accounting/solve.d.mts +28 -0
  135. package/dist/finance/accounting/solve.mjs +92 -0
  136. package/dist/finance/accounting/statements.d.mts +7 -0
  137. package/dist/finance/accounting/statements.mjs +142 -0
  138. package/dist/finance/accounting/types.d.mts +649 -0
  139. package/dist/finance/accounting/validate.d.mts +7 -0
  140. package/dist/finance/accounting/validate.mjs +67 -0
  141. package/dist/finance/accounting/verification.d.mts +24 -0
  142. package/dist/finance/accounting/verification.mjs +386 -0
  143. package/dist/finance/bizsim.d.mts +21 -39
  144. package/dist/finance/bizsim.mjs +276 -97
  145. package/dist/finance/index.d.mts +20 -43
  146. package/dist/finance/index.mjs +19 -77
  147. package/dist/finance/investment-math.d.mts +7 -0
  148. package/dist/finance/investment-math.mjs +18 -0
  149. package/dist/finance/management-accounting/budgeting.d.mts +7 -0
  150. package/dist/finance/management-accounting/budgeting.mjs +189 -0
  151. package/dist/finance/management-accounting/cash-budget.d.mts +7 -0
  152. package/dist/finance/management-accounting/cash-budget.mjs +154 -0
  153. package/dist/finance/management-accounting/costing.d.mts +8 -0
  154. package/dist/finance/management-accounting/costing.mjs +175 -0
  155. package/dist/finance/management-accounting/decisions.d.mts +8 -0
  156. package/dist/finance/management-accounting/decisions.mjs +164 -0
  157. package/dist/finance/management-accounting/index.d.mts +13 -0
  158. package/dist/finance/management-accounting/index.mjs +13 -0
  159. package/dist/finance/management-accounting/integrated-budget.d.mts +8 -0
  160. package/dist/finance/management-accounting/integrated-budget.mjs +67 -0
  161. package/dist/finance/management-accounting/inventory-costing.d.mts +7 -0
  162. package/dist/finance/management-accounting/inventory-costing.mjs +173 -0
  163. package/dist/finance/management-accounting/investment-appraisal.d.mts +7 -0
  164. package/dist/finance/management-accounting/investment-appraisal.mjs +148 -0
  165. package/dist/finance/management-accounting/management-case.d.mts +8 -0
  166. package/dist/finance/management-accounting/management-case.mjs +91 -0
  167. package/dist/finance/management-accounting/master-budget.d.mts +7 -0
  168. package/dist/finance/management-accounting/master-budget.mjs +137 -0
  169. package/dist/finance/management-accounting/production-costing.d.mts +8 -0
  170. package/dist/finance/management-accounting/production-costing.mjs +214 -0
  171. package/dist/finance/management-accounting/types.d.mts +644 -0
  172. package/dist/finance/management-accounting/variances.d.mts +7 -0
  173. package/dist/finance/management-accounting/variances.mjs +150 -0
  174. package/dist/index.d.mts +6 -6
  175. package/dist/index.mjs +1 -1
  176. package/dist/math/analysis.d.mts +61 -0
  177. package/dist/math/analysis.mjs +187 -0
  178. package/dist/math/answer-check.d.mts +50 -0
  179. package/dist/math/answer-check.mjs +106 -0
  180. package/dist/math/calculus.d.mts +4 -9
  181. package/dist/math/calculus.mjs +216 -7
  182. package/dist/math/complex.d.mts +93 -0
  183. package/dist/math/complex.mjs +239 -0
  184. package/dist/math/expr.d.mts +23 -0
  185. package/dist/math/expr.mjs +36 -0
  186. package/dist/math/index.d.mts +8 -22
  187. package/dist/math/index.mjs +8 -37
  188. package/dist/math/latex.mjs +2 -1
  189. package/dist/math/parse.mjs +1 -1
  190. package/dist/math/poly.d.mts +55 -0
  191. package/dist/math/poly.mjs +273 -0
  192. package/dist/math/rearrange.d.mts +14 -0
  193. package/dist/math/rearrange.mjs +125 -0
  194. package/dist/math/solve.d.mts +21 -0
  195. package/dist/math/solve.mjs +75 -0
  196. package/dist/networking/addressing.d.mts +26 -0
  197. package/dist/networking/addressing.mjs +33 -0
  198. package/dist/networking/cloud/access.d.mts +36 -0
  199. package/dist/networking/cloud/access.mjs +95 -0
  200. package/dist/networking/cloud/containers.d.mts +43 -0
  201. package/dist/networking/cloud/containers.mjs +54 -0
  202. package/dist/networking/cloud/edge.d.mts +17 -0
  203. package/dist/networking/cloud/edge.mjs +31 -0
  204. package/dist/networking/cloud/failure-domains.d.mts +19 -0
  205. package/dist/networking/cloud/failure-domains.mjs +22 -0
  206. package/dist/networking/cloud/geo-routing.d.mts +22 -0
  207. package/dist/networking/cloud/geo-routing.mjs +64 -0
  208. package/dist/networking/cloud/index.d.mts +18 -0
  209. package/dist/networking/cloud/index.mjs +19 -0
  210. package/dist/networking/cloud/inference-reliability.d.mts +30 -0
  211. package/dist/networking/cloud/inference-reliability.mjs +76 -0
  212. package/dist/networking/cloud/inference.d.mts +118 -0
  213. package/dist/networking/cloud/inference.mjs +179 -0
  214. package/dist/networking/cloud/infrastructure.d.mts +45 -0
  215. package/dist/networking/cloud/infrastructure.mjs +96 -0
  216. package/dist/networking/cloud/observability.d.mts +24 -0
  217. package/dist/networking/cloud/observability.mjs +34 -0
  218. package/dist/networking/cloud/orchestration.d.mts +32 -0
  219. package/dist/networking/cloud/orchestration.mjs +38 -0
  220. package/dist/networking/cloud/recovery.d.mts +21 -0
  221. package/dist/networking/cloud/recovery.mjs +27 -0
  222. package/dist/networking/cloud/releases.d.mts +42 -0
  223. package/dist/networking/cloud/releases.mjs +135 -0
  224. package/dist/networking/cloud/scaling.d.mts +25 -0
  225. package/dist/networking/cloud/scaling.mjs +65 -0
  226. package/dist/networking/cloud/secrets.d.mts +39 -0
  227. package/dist/networking/cloud/secrets.mjs +68 -0
  228. package/dist/networking/cloud/server-deployment.d.mts +29 -0
  229. package/dist/networking/cloud/server-deployment.mjs +72 -0
  230. package/dist/networking/cloud/storage.d.mts +16 -0
  231. package/dist/networking/cloud/storage.mjs +58 -0
  232. package/dist/networking/cloud/vpc.d.mts +18 -0
  233. package/dist/networking/cloud/vpc.mjs +100 -0
  234. package/dist/networking/delivery.d.mts +34 -0
  235. package/dist/networking/delivery.mjs +52 -0
  236. package/dist/networking/index.d.mts +47 -0
  237. package/dist/networking/index.mjs +47 -0
  238. package/dist/networking/ipv6.d.mts +28 -0
  239. package/dist/networking/ipv6.mjs +17 -0
  240. package/dist/networking/journeys.d.mts +83 -0
  241. package/dist/networking/journeys.mjs +161 -0
  242. package/dist/networking/models/airtime.d.mts +87 -0
  243. package/dist/networking/models/airtime.mjs +130 -0
  244. package/dist/networking/models/cellular.d.mts +157 -0
  245. package/dist/networking/models/cellular.mjs +252 -0
  246. package/dist/networking/models/encapsulation.d.mts +98 -0
  247. package/dist/networking/models/encapsulation.mjs +132 -0
  248. package/dist/networking/models/handover.d.mts +116 -0
  249. package/dist/networking/models/handover.mjs +209 -0
  250. package/dist/networking/models/handshake.d.mts +90 -0
  251. package/dist/networking/models/handshake.mjs +122 -0
  252. package/dist/networking/models/index.d.mts +14 -0
  253. package/dist/networking/models/index.mjs +15 -0
  254. package/dist/networking/models/media.d.mts +82 -0
  255. package/dist/networking/models/media.mjs +188 -0
  256. package/dist/networking/models/satellite.d.mts +86 -0
  257. package/dist/networking/models/satellite.mjs +121 -0
  258. package/dist/networking/models/spectrum.d.mts +83 -0
  259. package/dist/networking/models/spectrum.mjs +235 -0
  260. package/dist/networking/models/switch.d.mts +80 -0
  261. package/dist/networking/models/switch.mjs +155 -0
  262. package/dist/networking/models/vlan.d.mts +62 -0
  263. package/dist/networking/models/vlan.mjs +100 -0
  264. package/dist/networking/models/vpn.d.mts +66 -0
  265. package/dist/networking/models/vpn.mjs +125 -0
  266. package/dist/networking/models/wifi-privacy.d.mts +82 -0
  267. package/dist/networking/models/wifi-privacy.mjs +130 -0
  268. package/dist/networking/models/wifi.d.mts +240 -0
  269. package/dist/networking/models/wifi.mjs +562 -0
  270. package/dist/networking/nat-traversal.d.mts +24 -0
  271. package/dist/networking/nat-traversal.mjs +38 -0
  272. package/dist/networking/protocols.d.mts +17 -0
  273. package/dist/networking/protocols.mjs +57 -0
  274. package/dist/networking/queue.d.mts +12 -0
  275. package/dist/networking/queue.mjs +18 -0
  276. package/dist/networking/route-learning.d.mts +23 -0
  277. package/dist/networking/route-learning.mjs +43 -0
  278. package/dist/networking/routing.d.mts +11 -0
  279. package/dist/networking/routing.mjs +25 -0
  280. package/dist/networking/signals.d.mts +26 -0
  281. package/dist/networking/signals.mjs +35 -0
  282. package/dist/networking/subnet.d.mts +49 -0
  283. package/dist/networking/subnet.mjs +89 -0
  284. package/dist/networking/tcp-reliability.d.mts +24 -0
  285. package/dist/networking/tcp-reliability.mjs +38 -0
  286. package/dist/networking/topology/contract.d.mts +123 -0
  287. package/dist/networking/topology/index.d.mts +4 -0
  288. package/dist/networking/topology/index.mjs +4 -0
  289. package/dist/networking/topology/presets.d.mts +7 -0
  290. package/dist/networking/topology/presets.mjs +302 -0
  291. package/dist/networking/topology/simulation.d.mts +8 -0
  292. package/dist/networking/topology/simulation.mjs +179 -0
  293. package/dist/networking/wireless.d.mts +11 -0
  294. package/dist/networking/wireless.mjs +17 -0
  295. package/dist/optics/imaging.d.mts +218 -0
  296. package/dist/optics/imaging.mjs +276 -0
  297. package/dist/optics/index.d.mts +7 -0
  298. package/dist/optics/index.mjs +8 -0
  299. package/dist/optics/laws.d.mts +70 -0
  300. package/dist/optics/laws.mjs +195 -0
  301. package/dist/optics/prism.d.mts +148 -0
  302. package/dist/optics/prism.mjs +197 -0
  303. package/dist/optics/refraction.d.mts +201 -0
  304. package/dist/optics/refraction.mjs +270 -0
  305. package/dist/optics/wave-optics.d.mts +171 -0
  306. package/dist/optics/wave-optics.mjs +259 -0
  307. package/dist/physics/ac/index.d.mts +183 -0
  308. package/dist/physics/ac/index.mjs +203 -0
  309. package/dist/physics/dynamics/forces.d.mts +98 -0
  310. package/dist/physics/dynamics/forces.mjs +153 -0
  311. package/dist/physics/dynamics/friction.d.mts +76 -0
  312. package/dist/physics/dynamics/friction.mjs +119 -0
  313. package/dist/physics/dynamics/system.d.mts +68 -0
  314. package/dist/physics/dynamics/system.mjs +125 -0
  315. package/dist/physics/electrostatics/index.d.mts +169 -0
  316. package/dist/physics/electrostatics/index.mjs +249 -0
  317. package/dist/physics/energy/centre-of-mass.d.mts +115 -0
  318. package/dist/physics/energy/centre-of-mass.mjs +180 -0
  319. package/dist/physics/energy/conservation.d.mts +81 -0
  320. package/dist/physics/energy/conservation.mjs +129 -0
  321. package/dist/physics/energy/power.d.mts +90 -0
  322. package/dist/physics/energy/power.mjs +167 -0
  323. package/dist/physics/energy/spring.d.mts +73 -0
  324. package/dist/physics/energy/spring.mjs +129 -0
  325. package/dist/physics/energy/store.d.mts +63 -0
  326. package/dist/physics/energy/store.mjs +108 -0
  327. package/dist/physics/energy/theorem.d.mts +65 -0
  328. package/dist/physics/energy/theorem.mjs +113 -0
  329. package/dist/physics/energy/work.d.mts +114 -0
  330. package/dist/physics/energy/work.mjs +219 -0
  331. package/dist/physics/gravitation/body.d.mts +85 -0
  332. package/dist/physics/gravitation/body.mjs +168 -0
  333. package/dist/physics/gravitation/kepler.d.mts +127 -0
  334. package/dist/physics/gravitation/kepler.mjs +216 -0
  335. package/dist/physics/gravitation/law.d.mts +117 -0
  336. package/dist/physics/gravitation/law.mjs +175 -0
  337. package/dist/physics/gravitation/orbit.d.mts +119 -0
  338. package/dist/physics/gravitation/orbit.mjs +202 -0
  339. package/dist/physics/gravitation/potential.d.mts +114 -0
  340. package/dist/physics/gravitation/potential.mjs +202 -0
  341. package/dist/physics/gravitation/variation.d.mts +97 -0
  342. package/dist/physics/gravitation/variation.mjs +175 -0
  343. package/dist/physics/index.d.mts +35 -0
  344. package/dist/physics/index.mjs +36 -0
  345. package/dist/physics/integral-sweep.d.mts +63 -0
  346. package/dist/physics/integral-sweep.mjs +126 -0
  347. package/dist/physics/kinematics/equations.d.mts +47 -0
  348. package/dist/physics/kinematics/equations.mjs +126 -0
  349. package/dist/physics/kinematics/free-fall.d.mts +53 -0
  350. package/dist/physics/kinematics/free-fall.mjs +71 -0
  351. package/dist/physics/kinematics/given.d.mts +16 -0
  352. package/dist/physics/kinematics/given.mjs +55 -0
  353. package/dist/physics/kinematics/roots.d.mts +41 -0
  354. package/dist/physics/kinematics/roots.mjs +72 -0
  355. package/dist/physics/kinematics/solve.d.mts +52 -0
  356. package/dist/physics/kinematics/solve.mjs +172 -0
  357. package/dist/physics/magnetism/biot-savart.d.mts +161 -0
  358. package/dist/physics/magnetism/biot-savart.mjs +353 -0
  359. package/dist/physics/magnetism/flux.d.mts +94 -0
  360. package/dist/physics/magnetism/flux.mjs +313 -0
  361. package/dist/physics/magnetism/geomagnetism.d.mts +151 -0
  362. package/dist/physics/magnetism/geomagnetism.mjs +184 -0
  363. package/dist/physics/magnetism/index.d.mts +12 -0
  364. package/dist/physics/magnetism/index.mjs +74 -0
  365. package/dist/physics/magnetism/lorentz.d.mts +28 -0
  366. package/dist/physics/magnetism/lorentz.mjs +88 -0
  367. package/dist/physics/magnetism/magnetic-force.d.mts +102 -0
  368. package/dist/physics/magnetism/magnetic-force.mjs +169 -0
  369. package/dist/physics/momentum/collide.d.mts +77 -0
  370. package/dist/physics/momentum/collide.mjs +115 -0
  371. package/dist/physics/momentum/impulse.d.mts +89 -0
  372. package/dist/physics/momentum/impulse.mjs +159 -0
  373. package/dist/physics/momentum/planar.d.mts +60 -0
  374. package/dist/physics/momentum/planar.mjs +110 -0
  375. package/dist/physics/projectile.d.mts +107 -0
  376. package/dist/physics/projectile.mjs +185 -0
  377. package/dist/physics/rotation/angular.d.mts +85 -0
  378. package/dist/physics/rotation/angular.mjs +130 -0
  379. package/dist/physics/rotation/circular.d.mts +79 -0
  380. package/dist/physics/rotation/circular.mjs +131 -0
  381. package/dist/physics/rotation/inertia.d.mts +164 -0
  382. package/dist/physics/rotation/inertia.mjs +332 -0
  383. package/dist/physics/rotation/torque.d.mts +65 -0
  384. package/dist/physics/rotation/torque.mjs +134 -0
  385. package/dist/physics/vectors.d.mts +64 -0
  386. package/dist/physics/vectors.mjs +114 -0
  387. package/dist/quantity/index.d.mts +119 -0
  388. package/dist/quantity/index.mjs +417 -0
  389. package/dist/relation/derive.d.mts +74 -0
  390. package/dist/relation/derive.mjs +73 -0
  391. package/dist/relation/index.d.mts +56 -0
  392. package/dist/relation/index.mjs +234 -0
  393. package/dist/scene/evaluators.mjs +1 -1
  394. package/dist/sim/thermal.mjs +45 -20
  395. package/dist/thermo/calorimetry.d.mts +207 -0
  396. package/dist/thermo/calorimetry.mjs +345 -0
  397. package/dist/thermo/cycles.d.mts +142 -0
  398. package/dist/thermo/cycles.mjs +256 -0
  399. package/dist/thermo/entropy.d.mts +87 -0
  400. package/dist/thermo/entropy.mjs +140 -0
  401. package/dist/thermo/expansion.d.mts +130 -0
  402. package/dist/thermo/expansion.mjs +178 -0
  403. package/dist/thermo/gas.d.mts +197 -0
  404. package/dist/thermo/gas.mjs +279 -0
  405. package/dist/thermo/heat-transfer.d.mts +164 -0
  406. package/dist/thermo/heat-transfer.mjs +217 -0
  407. package/dist/thermo/index.d.mts +9 -82
  408. package/dist/thermo/index.mjs +9 -132
  409. package/dist/thermo/processes.d.mts +95 -0
  410. package/dist/thermo/processes.mjs +154 -0
  411. package/dist/thermo/temperature.d.mts +174 -0
  412. package/dist/thermo/temperature.mjs +236 -0
  413. package/dist/waves/damping.d.mts +224 -0
  414. package/dist/waves/damping.mjs +317 -0
  415. package/dist/waves/doppler.d.mts +72 -0
  416. package/dist/waves/doppler.mjs +125 -0
  417. package/dist/waves/index.d.mts +10 -0
  418. package/dist/waves/index.mjs +10 -0
  419. package/dist/waves/pendulum.d.mts +130 -0
  420. package/dist/waves/pendulum.mjs +211 -0
  421. package/dist/waves/pitch.d.mts +125 -0
  422. package/dist/waves/pitch.mjs +158 -0
  423. package/dist/waves/shm.d.mts +170 -0
  424. package/dist/waves/shm.mjs +279 -0
  425. package/dist/waves/springs.d.mts +103 -0
  426. package/dist/waves/springs.mjs +176 -0
  427. package/dist/waves/superposition.d.mts +124 -0
  428. package/dist/waves/superposition.mjs +196 -0
  429. package/dist/waves/wave.d.mts +154 -0
  430. package/dist/waves/wave.mjs +178 -0
  431. package/package.json +74 -2
@@ -0,0 +1,156 @@
1
+ import { parseFormula } from "./formula.mjs";
2
+
3
+ //#region src/chem/ionic.ts
4
+ /**
5
+ * Ions, and the formula two of them make.
6
+ *
7
+ * ## The one rule, which is usually taught as a trick
8
+ *
9
+ * "Write the formula of aluminium oxide" is answered in most classrooms by the crossover trick:
10
+ * write the charges, swap them over, drop the signs. It gives Al2O3, and it gives the wrong answer
11
+ * for magnesium oxide, where crossing over 2 and 2 gives Mg2O2 and the compound is MgO. The trick
12
+ * then acquires a patch: "and cancel down if you can".
13
+ *
14
+ * There is no trick. A compound has no overall charge, so the positive and negative charges in it
15
+ * must cancel exactly, and the formula is the SMALLEST whole number of each ion that makes them.
16
+ * That is a lowest common multiple, and it gets magnesium oxide right without a patch because the
17
+ * smallest multiple of 2 that is also a multiple of 2 is 2, not 4.
18
+ *
19
+ * {@link neutralRatio} computes it that way, so the reasoning a learner is shown is the reasoning
20
+ * the engine uses.
21
+ *
22
+ * ## Charges are data, and the ones worth knowing are here
23
+ *
24
+ * Which charge an ion carries is not derivable for a transition metal, and only roughly derivable
25
+ * from the group for everything else. So the common ones are listed, with their names, because
26
+ * "what is the formula of iron(III) sulfate" needs both the charge and the fact that sulfate is one
27
+ * unit that keeps its brackets.
28
+ *
29
+ * Pure: no React, no pixels.
30
+ */
31
+ const ion = (formula, charge, name) => ({
32
+ formula,
33
+ charge,
34
+ name,
35
+ polyatomic: parseFormula(formula).reduce((total, e) => total + e.count, 0) > 1
36
+ });
37
+ /** The cations a syllabus expects to be known. */
38
+ const CATIONS = [
39
+ ion("H", 1, "hydrogen"),
40
+ ion("Li", 1, "lithium"),
41
+ ion("Na", 1, "sodium"),
42
+ ion("K", 1, "potassium"),
43
+ ion("Ag", 1, "silver"),
44
+ ion("NH4", 1, "ammonium"),
45
+ ion("Mg", 2, "magnesium"),
46
+ ion("Ca", 2, "calcium"),
47
+ ion("Ba", 2, "barium"),
48
+ ion("Zn", 2, "zinc"),
49
+ ion("Cu", 2, "copper(II)"),
50
+ ion("Fe", 2, "iron(II)"),
51
+ ion("Pb", 2, "lead(II)"),
52
+ ion("Al", 3, "aluminium"),
53
+ ion("Cr", 3, "chromium(III)")
54
+ ];
55
+ /** Iron and copper each appear twice, so the ion has to be named rather than the element. */
56
+ const IRON_III = ion("Fe", 3, "iron(III)");
57
+ const COPPER_I = ion("Cu", 1, "copper(I)");
58
+ /** The anions a syllabus expects to be known. */
59
+ const ANIONS = [
60
+ ion("F", -1, "fluoride"),
61
+ ion("Cl", -1, "chloride"),
62
+ ion("Br", -1, "bromide"),
63
+ ion("I", -1, "iodide"),
64
+ ion("OH", -1, "hydroxide"),
65
+ ion("NO3", -1, "nitrate"),
66
+ ion("HCO3", -1, "hydrogencarbonate"),
67
+ ion("O", -2, "oxide"),
68
+ ion("S", -2, "sulfide"),
69
+ ion("SO4", -2, "sulfate"),
70
+ ion("CO3", -2, "carbonate"),
71
+ ion("PO4", -3, "phosphate")
72
+ ];
73
+ /** Every ion listed here, cations and anions together. */
74
+ const IONS = [
75
+ ...CATIONS,
76
+ IRON_III,
77
+ COPPER_I,
78
+ ...ANIONS
79
+ ];
80
+ /**
81
+ * Find an ion by the name it takes in a compound.
82
+ *
83
+ * Matched case-insensitively on BOTH sides. Lowercasing only the input looked right and failed for
84
+ * every ion whose name carries a Roman numeral, since "iron(iii)" never matches "iron(III)".
85
+ */
86
+ const ionByName = (name) => {
87
+ const wanted = name.trim().toLowerCase();
88
+ return IONS.find((i) => i.name.toLowerCase() === wanted);
89
+ };
90
+ const gcd = (a, b) => b === 0 ? Math.abs(a) : gcd(b, a % b);
91
+ /**
92
+ * How many of each ion make a compound with no overall charge, in the smallest whole numbers.
93
+ *
94
+ * The lowest common multiple of the two charge sizes, divided by each. Magnesium and oxide both
95
+ * carry two, their lowest common multiple is two, and one of each is the answer: MgO. The crossover
96
+ * trick would have given Mg2O2 and needed a separate rule to cancel it down.
97
+ */
98
+ function neutralRatio(cationCharge, anionCharge) {
99
+ if (cationCharge <= 0) throw new Error(`a cation carries a positive charge, not ${cationCharge}`);
100
+ if (anionCharge >= 0) throw new Error(`an anion carries a negative charge, not ${anionCharge}`);
101
+ const size = Math.abs(anionCharge);
102
+ const lcm = cationCharge * size / gcd(cationCharge, size);
103
+ return [lcm / cationCharge, lcm / size];
104
+ }
105
+ /** One ion written out with its count, bracketed when it needs to be. */
106
+ function writeIon(part, count) {
107
+ if (count === 1) return part.formula;
108
+ return part.polyatomic ? `(${part.formula})${count}` : `${part.formula}${count}`;
109
+ }
110
+ /**
111
+ * The compound two ions form.
112
+ *
113
+ * `ionicFormula(aluminium, oxide)` gives Al2O3; `ionicFormula(magnesium, oxide)` gives MgO, not
114
+ * Mg2O2; `ionicFormula(calcium, nitrate)` gives Ca(NO3)2 with the brackets that stop it being read
115
+ * as thirty-two oxygens.
116
+ */
117
+ function ionicCompound(cation, anion) {
118
+ const [cationCount, anionCount] = neutralRatio(cation.charge, anion.charge);
119
+ return {
120
+ formula: `${writeIon(cation, cationCount)}${writeIon(anion, anionCount)}`,
121
+ name: `${cation.name} ${anion.name}`,
122
+ cation,
123
+ anion,
124
+ cationCount,
125
+ anionCount
126
+ };
127
+ }
128
+ /** The same, by name: `compoundOf('calcium', 'carbonate')`. */
129
+ function compoundOf(cationName, anionName) {
130
+ const cation = ionByName(cationName);
131
+ const anion = ionByName(anionName);
132
+ if (!cation) throw new Error(`no ion named "${cationName}" in this table`);
133
+ if (!anion) throw new Error(`no ion named "${anionName}" in this table`);
134
+ if (cation.charge <= 0) throw new Error(`"${cationName}" is not a cation`);
135
+ if (anion.charge >= 0) throw new Error(`"${anionName}" is not an anion`);
136
+ return ionicCompound(cation, anion);
137
+ }
138
+ /**
139
+ * Whether a proposed pair of counts makes a neutral compound, and whether it is the simplest one.
140
+ *
141
+ * Both answers matter and they are different. Mg2O2 IS neutral and is not a formula anyone writes,
142
+ * so a learner offering it has understood the charge balance and not the "smallest" part, and
143
+ * telling them only that it is wrong would hide which half they have.
144
+ */
145
+ function assessCounts(cation, anion, cationCount, anionCount) {
146
+ const totalCharge = cation.charge * cationCount + anion.charge * anionCount;
147
+ const [wantCation, wantAnion] = neutralRatio(cation.charge, anion.charge);
148
+ return {
149
+ totalCharge,
150
+ neutral: totalCharge === 0,
151
+ simplest: cationCount === wantCation && anionCount === wantAnion
152
+ };
153
+ }
154
+
155
+ //#endregion
156
+ export { ANIONS, CATIONS, IONS, assessCounts, compoundOf, ionByName, ionicCompound, neutralRatio, writeIon };
@@ -0,0 +1,46 @@
1
+ import { Structure } from "./molecule.mjs";
2
+
3
+ //#region src/chem/layout.d.ts
4
+ interface Point {
5
+ readonly x: number;
6
+ readonly y: number;
7
+ }
8
+ /**
9
+ * Where every atom goes, in bond lengths, with y downward.
10
+ *
11
+ * A disconnected structure lays each piece out and offsets it, so nothing overlaps: that happens
12
+ * when a reaction has produced a small molecule alongside the main one.
13
+ */
14
+ declare function layout(structure: Structure): Map<number, Point>;
15
+ interface LaidOut {
16
+ readonly positions: ReadonlyMap<number, Point>;
17
+ /** Corners of the drawing, in bond lengths, so a caller can centre and scale it. */
18
+ readonly bounds: {
19
+ readonly minX: number;
20
+ readonly minY: number;
21
+ readonly maxX: number;
22
+ readonly maxY: number;
23
+ };
24
+ /** Ring systems, in bonded order, so an aromatic circle can be drawn in the right place. */
25
+ readonly rings: readonly (readonly number[])[];
26
+ }
27
+ /** The layout plus everything a figure needs to place it on a page. */
28
+ declare function layoutOf(structure: Structure): LaidOut;
29
+ /** The centre of a ring, for the delocalisation circle. */
30
+ declare function ringCentre(ring: readonly number[], positions: ReadonlyMap<number, Point>): Point;
31
+ /**
32
+ * Several free directions at an atom, for drawing its hydrogens in a displayed formula.
33
+ *
34
+ * Returned widest-gap first and spread apart, so a CH2 puts its two hydrogens on opposite sides
35
+ * rather than side by side where they would touch.
36
+ */
37
+ declare function freeDirections(structure: Structure, id: number, positions: ReadonlyMap<number, Point>, count: number): Point[];
38
+ /**
39
+ * A free direction at an atom, for drawing an open valence or an implicit-hydrogen label.
40
+ *
41
+ * Returns the lattice direction furthest from every bond the atom already has, which is where a
42
+ * chemist would put it.
43
+ */
44
+ declare function freeDirection(structure: Structure, id: number, positions: ReadonlyMap<number, Point>): Point;
45
+ //#endregion
46
+ export { LaidOut, Point, freeDirection, freeDirections, layout, layoutOf, ringCentre };
@@ -0,0 +1,342 @@
1
+ import { bondsAt, otherEnd } from "./molecule.mjs";
2
+ import { ringSystems } from "./skeleton.mjs";
3
+
4
+ //#region src/chem/layout.ts
5
+ /**
6
+ * layout, where every atom of a structure goes on the page.
7
+ *
8
+ * WHAT THIS IS FOR. Drawing a molecule from a graph needs coordinates, and until now the corpus got
9
+ * them by having an author place a chain by hand. That works for a hand-written structure and cannot
10
+ * work for a computed one: a polymer repeat unit is built by the kernel, and nobody is there to
11
+ * place it. It also cannot express the shape that broke the polymer lab, a ring with the chain
12
+ * running THROUGH it, because a hand-placed chain is a chain or a ring and never both.
13
+ *
14
+ * So positions are computed. Coordinates come back in BOND LENGTHS, with y increasing downward to
15
+ * match SVG, and the caller scales and centres them.
16
+ *
17
+ * ## The lattice, and why 30 degrees
18
+ *
19
+ * Every bond points along one of twelve directions, 30 degrees apart. That single choice gives both
20
+ * shapes chemistry needs: a zig-zag chain is bonds alternating at plus and minus 30 degrees from
21
+ * horizontal, which is a 120 degree bond angle, and a hexagon's edges are at 30 plus multiples of
22
+ * 60. A finer lattice would let bonds land at angles no structure drawing uses; a coarser one cannot
23
+ * draw both shapes at once.
24
+ *
25
+ * ## Rings are placed first, and everything else grows off them
26
+ *
27
+ * A ring's geometry is fixed and a chain's is not, so the rings are laid down as regular polygons
28
+ * and the acyclic atoms are then walked outward from whatever is already placed. Doing it the other
29
+ * way round means discovering halfway along a chain that it has to close into a hexagon, and moving
30
+ * everything already placed.
31
+ *
32
+ * ## How a direction is chosen
33
+ *
34
+ * From the direction the walk arrived, turn 60 degrees, alternating which way so a chain zig-zags
35
+ * instead of curling into a circle. If that position is occupied, try the other way, then wider
36
+ * angles. The occupancy test is what stops a branch being drawn on top of the chain it hangs from,
37
+ * and it is why this is a search rather than a formula.
38
+ *
39
+ * Pure: no React, no pixels.
40
+ */
41
+ /** Twelve directions, 30 degrees apart, as unit vectors. */
42
+ const STEP = Math.PI / 6;
43
+ const DIRECTIONS = Array.from({ length: 12 }, (_, k) => ({
44
+ x: Math.cos(k * STEP),
45
+ y: Math.sin(k * STEP)
46
+ }));
47
+ /** The lattice direction closest to an angle, as an index into {@link DIRECTIONS}. */
48
+ const snap = (angle) => (Math.round(angle / STEP) % 12 + 12) % 12;
49
+ const angleOf = (from, to) => Math.atan2(to.y - from.y, to.x - from.x);
50
+ /** Two atoms are on top of each other if they are closer than this, in bond lengths. */
51
+ const TOO_CLOSE = .55;
52
+ const linked = (structure, id) => bondsAt(structure, id).map((b) => otherEnd(b, id));
53
+ /**
54
+ * How many atoms lie beyond a neighbour, without coming back through this one.
55
+ *
56
+ * Used to decide which way the CHAIN goes. At a branching atom the neighbour leading to the most
57
+ * atoms is the backbone and takes the zig-zag; the small ones hanging off it are substituents and
58
+ * are placed wherever there is room. Without this the order the bonds happen to be declared in
59
+ * decides, so ethyl ethanoate gave its chain direction to the carbonyl oxygen and drew itself
60
+ * vertically with the chain hanging off the side.
61
+ */
62
+ function reachBeyond(structure, from, through) {
63
+ const seen = new Set([from, through]);
64
+ const stack = [through];
65
+ let count = 0;
66
+ while (stack.length) {
67
+ const at = stack.pop();
68
+ count += 1;
69
+ for (const n of linked(structure, at)) {
70
+ if (seen.has(n)) continue;
71
+ seen.add(n);
72
+ stack.push(n);
73
+ }
74
+ }
75
+ return count;
76
+ }
77
+ /**
78
+ * Where every atom goes, in bond lengths, with y downward.
79
+ *
80
+ * A disconnected structure lays each piece out and offsets it, so nothing overlaps: that happens
81
+ * when a reaction has produced a small molecule alongside the main one.
82
+ */
83
+ function layout(structure) {
84
+ const placed = /* @__PURE__ */ new Map();
85
+ const rings = ringSystems(structure);
86
+ const occupied = (p, ignore) => [...placed].some(([id, q]) => id !== ignore && Math.hypot(p.x - q.x, p.y - q.y) < TOO_CLOSE);
87
+ /**
88
+ * Put a ring down as a regular polygon, turned so its substituents run across the page.
89
+ *
90
+ * A bare ring gets a vertex at the top, which is how benzene is printed. A SUBSTITUTED ring is
91
+ * turned so the first substituted vertex sits on the left, which for a para-disubstituted ring
92
+ * puts the other one on the right and lets the chain run horizontally through it. Without that,
93
+ * terephthalic acid came out six bond lengths tall and two wide, with its two carboxyls stacked.
94
+ */
95
+ const placeRing = (ring, centre) => {
96
+ const n = ring.length;
97
+ const radius = 1 / (2 * Math.sin(Math.PI / n));
98
+ const ordered = orderRing(structure, ring);
99
+ const inRing = new Set(ordered);
100
+ const substituted = ordered.findIndex((id) => linked(structure, id).some((nb) => !inRing.has(nb)));
101
+ const turn = substituted >= 0 ? Math.PI - substituted * 2 * Math.PI / n : -Math.PI / 2;
102
+ ordered.forEach((id, i) => {
103
+ const a = turn + i * 2 * Math.PI / n;
104
+ placed.set(id, {
105
+ x: centre.x + radius * Math.cos(a),
106
+ y: centre.y + radius * Math.sin(a)
107
+ });
108
+ });
109
+ };
110
+ if (rings[0]?.length) placeRing(rings[0], {
111
+ x: 0,
112
+ y: 0
113
+ });
114
+ else if (structure.atoms[0]) placed.set(structure.atoms[0].id, {
115
+ x: 0,
116
+ y: 0
117
+ });
118
+ /** Directions already used by bonds at an atom, as angles. */
119
+ const usedAngles = (id) => linked(structure, id).filter((n) => placed.has(n)).map((n) => angleOf(placed.get(id), placed.get(n)));
120
+ /**
121
+ * The direction a bond leaves an atom in.
122
+ *
123
+ * CONTINUING A CHAIN is a reflection, not a rotation. A zig-zag is bonds alternating between plus
124
+ * and minus 30 degrees, which is exactly "mirror the direction you arrived on in the horizontal",
125
+ * so the chain travels along the page instead of turning. Rotating by a fixed 60 degrees each
126
+ * step was the first version and it curls: six carbons came out as a hexagon the molecule does not
127
+ * have, and hexanedioic acid was drawn taller than it was wide.
128
+ *
129
+ * A BRANCH has no direction to reflect, so it takes the lattice direction furthest from every bond
130
+ * the atom already has, which is where a chemist puts it and which is also what keeps a
131
+ * substituent off a ring's own edges.
132
+ */
133
+ const directionFrom = (id, incoming) => {
134
+ const used = usedAngles(id);
135
+ if (incoming !== void 0 && used.length <= 1) {
136
+ const candidate = DIRECTIONS[snap(snap(-incoming) === snap(incoming) ? incoming + STEP : -incoming)];
137
+ if (!occupied({
138
+ x: placed.get(id).x + candidate.x,
139
+ y: placed.get(id).y + candidate.y
140
+ })) return candidate;
141
+ }
142
+ let best;
143
+ let bestGap = -1;
144
+ for (const d of DIRECTIONS) {
145
+ const here = placed.get(id);
146
+ if (occupied({
147
+ x: here.x + d.x,
148
+ y: here.y + d.y
149
+ })) continue;
150
+ const a = Math.atan2(d.y, d.x);
151
+ const gap = used.length ? Math.min(...used.map((u) => Math.abs(Math.atan2(Math.sin(a - u), Math.cos(a - u))))) : Math.PI;
152
+ if (gap > bestGap) {
153
+ bestGap = gap;
154
+ best = d;
155
+ }
156
+ }
157
+ return best ?? DIRECTIONS[0];
158
+ };
159
+ /**
160
+ * Walk outward from everything placed, breadth first, carrying the direction each step arrived on.
161
+ *
162
+ * Breadth first rather than depth first so a long chain is laid down before its branches, which
163
+ * means the branches see the chain already in place and step around it.
164
+ */
165
+ const grow = () => {
166
+ const queue = [...placed.keys()].map((id) => ({
167
+ id,
168
+ incoming: void 0
169
+ }));
170
+ let progress = false;
171
+ const seen = new Set(queue.map((q) => q.id));
172
+ const flat = structure.atoms.length <= 2;
173
+ let seeded = placed.size > 1;
174
+ while (queue.length) {
175
+ const { id, incoming } = queue.shift();
176
+ let continued = false;
177
+ const outward = [...linked(structure, id)].sort((a, b) => reachBeyond(structure, id, b) - reachBeyond(structure, id, a));
178
+ for (const next of outward) {
179
+ if (placed.has(next)) {
180
+ if (!seen.has(next)) {
181
+ seen.add(next);
182
+ queue.push({
183
+ id: next,
184
+ incoming: angleOf(placed.get(id), placed.get(next))
185
+ });
186
+ }
187
+ continue;
188
+ }
189
+ const direction = !seeded ? DIRECTIONS[flat ? 0 : snap(STEP)] : directionFrom(id, continued ? void 0 : incoming);
190
+ seeded = true;
191
+ continued = true;
192
+ const here = placed.get(id);
193
+ placed.set(next, {
194
+ x: here.x + direction.x,
195
+ y: here.y + direction.y
196
+ });
197
+ seen.add(next);
198
+ queue.push({
199
+ id: next,
200
+ incoming: angleOf(here, placed.get(next))
201
+ });
202
+ progress = true;
203
+ }
204
+ }
205
+ return progress;
206
+ };
207
+ let changed = true;
208
+ while (changed) {
209
+ changed = grow();
210
+ for (const ring of rings.slice(1)) {
211
+ if (ring.every((id) => placed.has(id))) continue;
212
+ const anchor = ring.find((id) => placed.has(id));
213
+ if (anchor === void 0) continue;
214
+ const here = placed.get(anchor);
215
+ const away = usedAngles(anchor);
216
+ const outward = away.length ? away.reduce((a, b) => a + b, 0) / away.length + Math.PI : 0;
217
+ const radius = 1 / (2 * Math.sin(Math.PI / ring.length));
218
+ placeRing(ring, {
219
+ x: here.x + radius * Math.cos(outward),
220
+ y: here.y + radius * Math.sin(outward)
221
+ });
222
+ changed = true;
223
+ }
224
+ }
225
+ const missing = structure.atoms.filter((a) => !placed.has(a.id));
226
+ if (missing.length) {
227
+ const right = Math.max(...[...placed.values()].map((p) => p.x), 0) + 2;
228
+ missing.forEach((atom, i) => placed.set(atom.id, {
229
+ x: right + i,
230
+ y: 0
231
+ }));
232
+ }
233
+ return placed;
234
+ }
235
+ /** Put a ring's atoms in bonded order, so a polygon can be walked round. */
236
+ function orderRing(structure, ring) {
237
+ const inRing = new Set(ring);
238
+ const ordered = [ring[0]];
239
+ const seen = new Set(ordered);
240
+ while (ordered.length < ring.length) {
241
+ const at = ordered[ordered.length - 1];
242
+ const next = linked(structure, at).find((n) => inRing.has(n) && !seen.has(n));
243
+ if (next === void 0) break;
244
+ seen.add(next);
245
+ ordered.push(next);
246
+ }
247
+ return ordered;
248
+ }
249
+ /** The layout plus everything a figure needs to place it on a page. */
250
+ function layoutOf(structure) {
251
+ const positions = layout(structure);
252
+ const xs = [...positions.values()].map((p) => p.x);
253
+ const ys = [...positions.values()].map((p) => p.y);
254
+ return {
255
+ positions,
256
+ bounds: {
257
+ minX: Math.min(...xs, 0),
258
+ minY: Math.min(...ys, 0),
259
+ maxX: Math.max(...xs, 0),
260
+ maxY: Math.max(...ys, 0)
261
+ },
262
+ rings: ringSystems(structure).map((ring) => orderRing(structure, ring))
263
+ };
264
+ }
265
+ /** The centre of a ring, for the delocalisation circle. */
266
+ function ringCentre(ring, positions) {
267
+ const pts = ring.map((id) => positions.get(id)).filter(Boolean);
268
+ return {
269
+ x: pts.reduce((s, p) => s + p.x, 0) / pts.length,
270
+ y: pts.reduce((s, p) => s + p.y, 0) / pts.length
271
+ };
272
+ }
273
+ /**
274
+ * Several free directions at an atom, for drawing its hydrogens in a displayed formula.
275
+ *
276
+ * Returned widest-gap first and spread apart, so a CH2 puts its two hydrogens on opposite sides
277
+ * rather than side by side where they would touch.
278
+ */
279
+ function freeDirections(structure, id, positions, count) {
280
+ const here = positions.get(id);
281
+ if (!here || count <= 0) return [];
282
+ const taken = [...linked(structure, id).map((n) => positions.get(n)).filter((p) => Boolean(p)).map((p) => angleOf(here, p))];
283
+ const out = [];
284
+ for (let k = 0; k < count; k += 1) {
285
+ let best = DIRECTIONS[0];
286
+ let bestGap = -1;
287
+ for (const d of DIRECTIONS) {
288
+ const a = Math.atan2(d.y, d.x);
289
+ const gap = taken.length ? Math.min(...taken.map((u) => Math.abs(Math.atan2(Math.sin(a - u), Math.cos(a - u))))) : Math.PI;
290
+ if (gap > bestGap) {
291
+ bestGap = gap;
292
+ best = d;
293
+ }
294
+ }
295
+ out.push(best);
296
+ taken.push(Math.atan2(best.y, best.x));
297
+ }
298
+ return out;
299
+ }
300
+ /**
301
+ * A free direction at an atom, for drawing an open valence or an implicit-hydrogen label.
302
+ *
303
+ * Returns the lattice direction furthest from every bond the atom already has, which is where a
304
+ * chemist would put it.
305
+ */
306
+ function freeDirection(structure, id, positions) {
307
+ const here = positions.get(id);
308
+ if (!here) return {
309
+ x: 1,
310
+ y: 0
311
+ };
312
+ const used = linked(structure, id).map((n) => positions.get(n)).filter((p) => Boolean(p)).map((p) => angleOf(here, p));
313
+ if (used.length === 0) return {
314
+ x: 1,
315
+ y: 0
316
+ };
317
+ const all = [...positions.values()];
318
+ const centroidX = all.reduce((sum, p) => sum + p.x, 0) / all.length;
319
+ const outward = here.x >= centroidX ? {
320
+ x: 1,
321
+ y: 0
322
+ } : {
323
+ x: -1,
324
+ y: 0
325
+ };
326
+ const outwardAngle = Math.atan2(outward.y, outward.x);
327
+ if (!used.some((u) => Math.abs(Math.atan2(Math.sin(outwardAngle - u), Math.cos(outwardAngle - u))) < STEP - 1e-6)) return outward;
328
+ let best = DIRECTIONS[0];
329
+ let bestGap = -1;
330
+ for (const d of DIRECTIONS) {
331
+ const a = Math.atan2(d.y, d.x);
332
+ const gap = Math.min(...used.map((u) => Math.abs(Math.atan2(Math.sin(a - u), Math.cos(a - u)))));
333
+ if (gap > bestGap) {
334
+ bestGap = gap;
335
+ best = d;
336
+ }
337
+ }
338
+ return best;
339
+ }
340
+
341
+ //#endregion
342
+ export { freeDirection, freeDirections, layout, layoutOf, ringCentre };
@@ -0,0 +1,79 @@
1
+ import { Centre } from "./species.mjs";
2
+
3
+ //#region src/chem/lewis.d.ts
4
+ /** Valence electrons of an element, from its configuration. */
5
+ declare function valenceElectronsFor(symbol: string): number;
6
+ interface LewisCount {
7
+ /** Valence electrons of all the atoms, adjusted for the overall charge. */
8
+ readonly totalValenceElectrons: number;
9
+ /** Sigma bonds from the central atom, one per ligand atom. */
10
+ readonly bondingPairs: number;
11
+ /** Lone pairs left on the central atom. */
12
+ readonly lonePairs: number;
13
+ /**
14
+ * Bonding pairs plus lone pairs: the number of regions of electron density.
15
+ *
16
+ * Pi bonds are deliberately NOT counted. A double bond points the same way as the single bond
17
+ * underneath it, so it is one region, which is why carbon dioxide is linear.
18
+ */
19
+ readonly stericNumber: number;
20
+ /**
21
+ * Pi bonds formed by pulling a lone pair off an outer atom into the bond.
22
+ *
23
+ * Derived rather than guessed. Every pi bond moves the central atom's formal charge one step
24
+ * toward zero and the donating atom's one step away from it, so they are added while that trade
25
+ * is an improvement and stopped when it is not. Two guards end the process:
26
+ *
27
+ * the central atom's formal charge reaches zero, so there is nothing left to gain;
28
+ * the donor would end up positively charged while being MORE electronegative than the centre,
29
+ * which is the situation no real structure adopts.
30
+ *
31
+ * That second guard is the whole of the difference between carbon dioxide and boron trifluoride.
32
+ * Carbon starts at +2 with two oxygens at -1, and two pi bonds take all three to zero. Boron
33
+ * starts at 0 already, so no pi bond would improve anything, and one would push fluorine, the most
34
+ * electronegative element there is, to +1.
35
+ */
36
+ readonly piBonds: number;
37
+ /** Bond order to each outer atom, in the order the formula writes them. */
38
+ readonly bondOrders: readonly {
39
+ readonly element: string;
40
+ readonly order: number;
41
+ }[];
42
+ /** Electrons around the central atom, pi bonds included. */
43
+ readonly centralElectrons: number;
44
+ /**
45
+ * How many electrons short of an octet the central atom REMAINS once pi bonding has done what it
46
+ * can.
47
+ *
48
+ * Zero for carbon dioxide, which closes its gap completely. Two for boron trifluoride, which
49
+ * cannot close it at all and is genuinely electron deficient: that is not a limitation of the
50
+ * calculation, it is the reason boron trifluoride is such a strong Lewis acid.
51
+ */
52
+ readonly octetShortfall: number;
53
+ /**
54
+ * Formal charge on the central atom in the structure this record describes.
55
+ *
56
+ * Worth being precise about, because formal charge belongs to a STRUCTURE and not to a species.
57
+ * Carbon dioxide drawn with single bonds gives its carbon +2 and drawn with two double bonds gives
58
+ * it 0, and both are statements about drawings. This is the charge in the pi-bonded structure the
59
+ * rest of this record describes, which is the one a chemist would draw.
60
+ */
61
+ readonly formalCharge: number;
62
+ /** Whether the central atom can hold more than eight, which needs an accessible d subshell. */
63
+ readonly canExpand: boolean;
64
+ /** Set when the count needs more than an octet on an atom that cannot hold one. */
65
+ readonly impossible?: string;
66
+ }
67
+ /**
68
+ * Work out the electron budget of a species.
69
+ *
70
+ * Returns the count rather than a drawing: where the electrons GO on the page is a layout decision,
71
+ * and how many there are is chemistry.
72
+ */
73
+ declare function lewisCount(centre: Centre): LewisCount;
74
+ /** How the central atom's electron count compares with an octet. */
75
+ type OctetStatus = 'short of an octet' | 'an octet' | 'an expanded octet';
76
+ /** Whether the central atom has fewer than eight electrons, eight, or more. */
77
+ declare function octetStatus(count: LewisCount): OctetStatus;
78
+ //#endregion
79
+ export { LewisCount, OctetStatus, lewisCount, octetStatus, valenceElectronsFor };