@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,313 @@
1
+ import { add3, basisFor3, dot3, mag3, scale3, sub3, unit3, v3 } from "../../core/vec3.mjs";
2
+ import { fieldAt } from "./biot-savart.mjs";
3
+
4
+ //#region src/physics/magnetism/flux.ts
5
+ /**
6
+ * @classytic/stage/field/flux — magnetic flux, flux linkage and inductance,
7
+ * computed over real geometry.
8
+ *
9
+ * Flux was previously only ever `B A cos(theta)` written inline wherever it was
10
+ * needed, which works for exactly one case: a flat loop in a field that is
11
+ * uniform across it. Every other case a student meets is outside that: a loop
12
+ * beside a straight wire, a small coil inside a big one, a solenoid linking its
13
+ * own turns. Those need the surface integral, and once you can do the integral
14
+ * you get inductance for free, because inductance is nothing but flux linkage
15
+ * per unit current.
16
+ *
17
+ * Phi = integral of B . dA Phi_linkage = N Phi L = N Phi / I
18
+ *
19
+ * ── Two rules for getting a trustworthy number out of this ────────────────────
20
+ * 1. KEEP THE LOOP INSIDE THE WINDING. A filament has no thickness, so the
21
+ * field diverges on it and a flux loop whose edge sits on the winding picks
22
+ * that up: integrating a solenoid out to 0.999 of its radius lands 2.3% low,
23
+ * while 0.9 of the radius is right to a part in a thousand. A real coil is
24
+ * wound with wire of finite thickness, which is exactly what the filament
25
+ * model cannot represent.
26
+ * 2. WIND IT FINELY COMPARED WITH ITS RADIUS. A ring stack with turns spaced a
27
+ * tenth of a radius apart sits about 2.5% below the ideal solenoid formula
28
+ * and stays there however long you make the coil, because the error is
29
+ * radial rather than an end effect. Tighten the spacing to a fortieth of a
30
+ * radius and it converges to within 0.4%.
31
+ *
32
+ * Both are asserted in the tests, so the limits are known rather than found.
33
+ *
34
+ * ── Mutual inductance: drive the LARGER circuit ───────────────────────────────
35
+ * M is reciprocal, but the two ways of computing it are not equally
36
+ * computable. Integrating a small coil's field through a large coplanar loop
37
+ * puts the small winding ON the integration surface, where the integrand
38
+ * diverges and only its principal value is finite. Driving the larger circuit
39
+ * and integrating through the smaller loop keeps the surface clear of every
40
+ * winding, and gives the same M.
41
+ *
42
+ * ── Why the loop is a SHAPE and not a polygon ─────────────────────────────────
43
+ * A rectangle and a circle cover every loop a syllabus draws, and both can be
44
+ * integrated by a product Gauss rule over their own parameters, which converges
45
+ * fast and has no boundary error. A general polygon would need point-in-polygon
46
+ * tests on a grid, which is slower and least accurate exactly at the edge, where
47
+ * a loop near a wire has its steepest field.
48
+ */
49
+ /** Area of the loop, in square metres. */
50
+ const loopArea = (loop) => loop.kind === "circle" ? Math.PI * loop.radiusM ** 2 : loop.widthM * loop.heightM;
51
+ /**
52
+ * Flux through a flat loop in a UNIFORM field: Phi = B A cos(theta).
53
+ *
54
+ * `normalToFieldDeg` is the angle between the loop's normal and the field, the
55
+ * same convention `coilTorqueNm` uses, so the two never disagree about what the
56
+ * angle means. The trap is the same one too: a question usually states the
57
+ * angle from the PLANE, and flux is greatest when the plane is ACROSS the field,
58
+ * which is exactly where the torque is zero.
59
+ */
60
+ function fluxThroughUniform(fieldT, areaM2, normalToFieldDeg) {
61
+ if (areaM2 < 0) throw new RangeError("Area cannot be negative.");
62
+ return fieldT * areaM2 * Math.cos(normalToFieldDeg * Math.PI / 180);
63
+ }
64
+ /** Flux linkage: the flux each turn sees, times the number of turns. */
65
+ function fluxLinkage(turns, fluxWb) {
66
+ if (turns < 0) throw new RangeError("Turns cannot be negative.");
67
+ return turns * fluxWb;
68
+ }
69
+ /**
70
+ * Magnitude of the average induced e.m.f. from Faraday's law.
71
+ *
72
+ * Unsigned on purpose. The direction is Lenz's law, which is a separate
73
+ * statement about what the induced current opposes, and folding a sign in here
74
+ * would let a student produce a direction without ever thinking about the
75
+ * opposition that gives it.
76
+ */
77
+ function inducedEmfV(turns, fluxChangeWb, timeS) {
78
+ if (turns < 0) throw new RangeError("Turns cannot be negative.");
79
+ if (timeS <= 0) throw new RangeError("The time interval must be positive.");
80
+ return turns * Math.abs(fluxChangeWb) / timeS;
81
+ }
82
+ /** How close a sample may come to a current filament before the flux is called divergent. */
83
+ const MIN_LOOP_CLEARANCE_M = 1e-4;
84
+ /**
85
+ * Gauss-Legendre nodes and weights on [-1, 1].
86
+ *
87
+ * Order 8 is right for a field that varies smoothly across the loop. Order 16
88
+ * exists for the case where the SOURCE is much smaller than the loop, which is
89
+ * not a smooth integrand at all: a small coil seen from a large one is close to
90
+ * a point dipole, and a coarse rule simply steps over it.
91
+ */
92
+ const GAUSS = {
93
+ 8: {
94
+ nodes: [
95
+ -.9602898564975363,
96
+ -.7966664774136267,
97
+ -.525532409916329,
98
+ -.1834346424956498,
99
+ .1834346424956498,
100
+ .525532409916329,
101
+ .7966664774136267,
102
+ .9602898564975363
103
+ ],
104
+ weights: [
105
+ .1012285362903763,
106
+ .2223810344533745,
107
+ .3137066458778873,
108
+ .362683783378362,
109
+ .362683783378362,
110
+ .3137066458778873,
111
+ .2223810344533745,
112
+ .1012285362903763
113
+ ]
114
+ },
115
+ 16: {
116
+ nodes: [
117
+ -.9894009349916499,
118
+ -.9445750230732326,
119
+ -.8656312023878318,
120
+ -.755404408355003,
121
+ -.6178762444026438,
122
+ -.4580167776572274,
123
+ -.2816035507792589,
124
+ -.0950125098376374,
125
+ .0950125098376374,
126
+ .2816035507792589,
127
+ .4580167776572274,
128
+ .6178762444026438,
129
+ .755404408355003,
130
+ .8656312023878318,
131
+ .9445750230732326,
132
+ .9894009349916499
133
+ ],
134
+ weights: [
135
+ .0271524594117541,
136
+ .0622535239386479,
137
+ .0951585116824928,
138
+ .1246289712555339,
139
+ .1495959888165767,
140
+ .1691565193950025,
141
+ .1826034150449236,
142
+ .1894506104550685,
143
+ .1894506104550685,
144
+ .1826034150449236,
145
+ .1691565193950025,
146
+ .1495959888165767,
147
+ .1246289712555339,
148
+ .0951585116824928,
149
+ .0622535239386479,
150
+ .0271524594117541
151
+ ]
152
+ }
153
+ };
154
+ /** Distance from a point to the loop's SURFACE, not merely to its plane. */
155
+ const distanceToSurface = (p, loop, n, u, v) => {
156
+ const d = sub3(p, loop.centre);
157
+ const perp = dot3(d, n);
158
+ const a = dot3(d, u);
159
+ const b = dot3(d, v);
160
+ if (loop.kind === "circle") {
161
+ const over = Math.max(0, Math.hypot(a, b) - loop.radiusM);
162
+ return Math.hypot(perp, over);
163
+ }
164
+ const overU = Math.max(0, Math.abs(a) - loop.widthM / 2);
165
+ const overV = Math.max(0, Math.abs(b) - loop.heightM / 2);
166
+ return Math.sqrt(perp * perp + overU * overU + overV * overV);
167
+ };
168
+ /**
169
+ * Refuse a loop whose surface runs into a current filament.
170
+ *
171
+ * This cannot be done by watching the quadrature samples. A Gauss rule never
172
+ * lands on an endpoint, so it sails straight past a 1/r singularity and returns
173
+ * a finite number for a divergent integral, which is the worst possible
174
+ * failure: a confident wrong answer. Worse, the commonest case is a wire lying
175
+ * IN the plane of the loop and crossing its interior, where the field is
176
+ * antisymmetric and the samples cancel almost exactly.
177
+ *
178
+ * So the geometry is checked instead: every source segment is walked and its
179
+ * distance to the loop's surface taken. A filament touching that surface makes
180
+ * the flux genuinely infinite, and saying so is the only honest answer.
181
+ *
182
+ * This is also why a coil's own winding cannot be its own flux loop. Put the
183
+ * loop just inside the winding radius: a real coil has wire of finite
184
+ * thickness, and the filament model has no way to represent that.
185
+ */
186
+ const assertClear = (sources, loop, n, u, v) => {
187
+ const loopReach = loop.kind === "circle" ? loop.radiusM : Math.hypot(loop.widthM / 2, loop.heightM / 2);
188
+ for (const path of sources) {
189
+ if (path.currentA === 0) continue;
190
+ let minX = Infinity, minY = Infinity, minZ = Infinity;
191
+ let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
192
+ for (const p of path.points) {
193
+ if (p.x < minX) minX = p.x;
194
+ if (p.y < minY) minY = p.y;
195
+ if (p.z < minZ) minZ = p.z;
196
+ if (p.x > maxX) maxX = p.x;
197
+ if (p.y > maxY) maxY = p.y;
198
+ if (p.z > maxZ) maxZ = p.z;
199
+ }
200
+ const centre = v3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
201
+ const pathReach = Math.hypot(maxX - minX, maxY - minY, maxZ - minZ) / 2;
202
+ if (mag3(sub3(centre, loop.centre)) > pathReach + loopReach + 1e-4) continue;
203
+ for (let i = 0; i + 1 < path.points.length; i += 1) {
204
+ const a = path.points[i];
205
+ const b = path.points[i + 1];
206
+ const steps = Math.max(2, Math.min(24, Math.ceil(mag3(sub3(b, a)) / MIN_LOOP_CLEARANCE_M)));
207
+ for (let k = 0; k <= steps; k += 1) if (distanceToSurface(add3(a, scale3(sub3(b, a), k / steps)), loop, n, u, v) < 1e-4) throw new RangeError("A current filament touches the loop surface, where the flux through it diverges. If this is a coil linking its own turns, put the loop just inside the winding radius.");
208
+ }
209
+ }
210
+ };
211
+ function fluxThroughLoop(sources, loop, order = 8) {
212
+ const rule = GAUSS[order];
213
+ if (!rule) throw new RangeError("Unsupported quadrature order.");
214
+ const n = unit3(loop.normal);
215
+ const [u, v] = basisFor3(loop.normal, loop.kind === "rectangle" ? loop.widthAxis : void 0);
216
+ assertClear(sources, loop, n, u, v);
217
+ let total = 0;
218
+ for (let i = 0; i < rule.nodes.length; i += 1) for (let j = 0; j < rule.nodes.length; j += 1) {
219
+ const a = rule.nodes[i];
220
+ const b = rule.nodes[j];
221
+ const w = rule.weights[i] * rule.weights[j];
222
+ let point;
223
+ let jacobian;
224
+ if (loop.kind === "rectangle") {
225
+ point = add3(loop.centre, add3(scale3(u, a * loop.widthM / 2), scale3(v, b * loop.heightM / 2)));
226
+ jacobian = loop.widthM / 2 * (loop.heightM / 2);
227
+ } else {
228
+ const r = loop.radiusM * (a + 1) / 2;
229
+ const theta = Math.PI * (b + 1);
230
+ point = add3(loop.centre, add3(scale3(u, r * Math.cos(theta)), scale3(v, r * Math.sin(theta))));
231
+ jacobian = r * (loop.radiusM / 2) * Math.PI;
232
+ }
233
+ total += w * jacobian * dot3(fieldAt(sources, point).field, n);
234
+ }
235
+ return total;
236
+ }
237
+ /**
238
+ * Self-inductance of a coil, in henries: L = N Phi / I.
239
+ *
240
+ * Measured by driving the coil with a test current and integrating the flux its
241
+ * own field puts through one turn. That the answer does not depend on the test
242
+ * current is the definition of inductance, and it is asserted in the tests
243
+ * rather than assumed.
244
+ */
245
+ function selfInductanceH(coil, oneTurn, turns, testCurrentA = 1, order = 8) {
246
+ if (turns < 1) throw new RangeError("A coil needs at least one turn.");
247
+ if (testCurrentA === 0) throw new RangeError("The test current cannot be zero.");
248
+ return turns * fluxThroughLoop(coil, oneTurn, order) / testCurrentA;
249
+ }
250
+ /**
251
+ * Mutual inductance between two circuits, in henries: M = N2 Phi2 / I1.
252
+ *
253
+ * The primary's field is integrated through one turn of the secondary. The
254
+ * reciprocity of this quantity, that driving either side gives the same M, is a
255
+ * real theorem and a good thing for a lesson to check rather than assert.
256
+ */
257
+ function mutualInductanceH(primary, secondaryTurn, secondaryTurns, primaryCurrentA = 1, order = 8) {
258
+ if (secondaryTurns < 1) throw new RangeError("A secondary needs at least one turn.");
259
+ if (primaryCurrentA === 0) throw new RangeError("The primary current cannot be zero.");
260
+ return secondaryTurns * fluxThroughLoop(primary, secondaryTurn, order) / primaryCurrentA;
261
+ }
262
+ /**
263
+ * Magnitude of the e.m.f. a changing current induces in its OWN circuit:
264
+ * E = L di/dt.
265
+ *
266
+ * The same physics as Faraday's law with the flux written as L I, and worth
267
+ * having separately because a question almost never gives you the flux. It
268
+ * gives you a current that changed and a time it took.
269
+ */
270
+ function emfFromSelfInductance(inductanceH, currentChangeA, timeS) {
271
+ if (inductanceH < 0) throw new RangeError("Inductance cannot be negative.");
272
+ if (timeS <= 0) throw new RangeError("The time interval must be positive.");
273
+ return inductanceH * Math.abs(currentChangeA) / timeS;
274
+ }
275
+ /**
276
+ * Magnitude of the e.m.f. a changing current induces in a NEIGHBOURING circuit:
277
+ * E = M di/dt.
278
+ *
279
+ * Identical in form to the self-inductance case, with M in place of L. That the
280
+ * two are the same equation is the point: a transformer is not a new law, it is
281
+ * this one with the two coils wound on the same core.
282
+ */
283
+ function emfFromMutualInductance(mutualH, currentChangeA, timeS) {
284
+ if (mutualH < 0) throw new RangeError("Mutual inductance cannot be negative.");
285
+ if (timeS <= 0) throw new RangeError("The time interval must be positive.");
286
+ return mutualH * Math.abs(currentChangeA) / timeS;
287
+ }
288
+ /** Energy stored in an inductor's field, in joules: W = 1/2 L I squared. */
289
+ function magneticEnergyJ(inductanceH, currentA) {
290
+ if (inductanceH < 0) throw new RangeError("Inductance cannot be negative.");
291
+ return .5 * inductanceH * currentA * currentA;
292
+ }
293
+ /** Ideal-solenoid self-inductance, for checking the integrated answer: mu0 N^2 A / l. */
294
+ function solenoidInductanceH(turns, areaM2, lengthM) {
295
+ if (lengthM <= 0) throw new RangeError("A solenoid needs a positive length.");
296
+ return 4 * Math.PI * 1e-7 * turns * turns * areaM2 / lengthM;
297
+ }
298
+ /**
299
+ * Flux through a rectangle lying beside a long straight wire, in closed form.
300
+ *
301
+ * Phi = (mu0 I l / 2 pi) ln(far / near)
302
+ *
303
+ * A standard exam result, and the best available check on the surface integral:
304
+ * the field varies as 1/r right across the loop, so nothing about this case is
305
+ * forgiving of a sloppy quadrature rule.
306
+ */
307
+ function fluxBesideWire(currentA, lengthM, nearM, farM) {
308
+ if (nearM <= 0 || farM <= nearM) throw new RangeError("Need 0 < near < far.");
309
+ return 4 * Math.PI * 1e-7 * currentA * lengthM / (2 * Math.PI) * Math.log(farM / nearM);
310
+ }
311
+
312
+ //#endregion
313
+ export { MIN_LOOP_CLEARANCE_M, emfFromMutualInductance, emfFromSelfInductance, fluxBesideWire, fluxLinkage, fluxThroughLoop, fluxThroughUniform, inducedEmfV, loopArea, magneticEnergyJ, mutualInductanceH, selfInductanceH, solenoidInductanceH };
@@ -0,0 +1,151 @@
1
+ //#region src/physics/magnetism/geomagnetism.d.ts
2
+ /**
3
+ * @classytic/stage/field/geomagnetism — the Earth's magnetic field, as the
4
+ * tilted dipole it very nearly is.
5
+ *
6
+ * A textbook gives dip and declination as numbers to look up. They are not
7
+ * lookups: a single dipole at the centre of the Earth, tilted about 11 degrees
8
+ * from the spin axis, predicts both to within a few degrees almost everywhere,
9
+ * and predicts the field strength to within about 10%. That is worth computing
10
+ * rather than tabulating, because the model explains WHY dip is zero at the
11
+ * magnetic equator and 90 degrees at the pole, and a table never can.
12
+ *
13
+ * ── What this model does not do ────────────────────────────────────────────────
14
+ * The real field has non-dipole parts (roughly 10% of it) from fluid motion in
15
+ * the core, plus crustal magnetism, plus a daily variation from currents in the
16
+ * ionosphere. So a value here can be a few degrees off what a survey measures,
17
+ * and the discrepancy IS the non-dipole field rather than an error. Anything
18
+ * needing survey accuracy wants IGRF, which is a 195-coefficient spherical
19
+ * harmonic model and a different kind of object entirely.
20
+ *
21
+ * ── Two different "magnetic poles" ─────────────────────────────────────────────
22
+ * The GEOMAGNETIC pole is where the best-fit dipole's axis cuts the surface.
23
+ * The magnetic DIP pole is where a dip needle actually stands vertical. They
24
+ * are about 1000 km apart and move differently, because the non-dipole field
25
+ * displaces the second and not the first. This module uses the geomagnetic pole,
26
+ * which is the only one a dipole model has.
27
+ */
28
+ /**
29
+ * The Earth's dipole moment, in A m^2 (IGRF epoch 2020, and falling slowly).
30
+ *
31
+ * The same quantity as a coil's N I A. A current loop of one turn enclosing the
32
+ * Earth's cross-section would need about 2 billion amperes to match it.
33
+ */
34
+ declare const EARTH_DIPOLE_MOMENT_AM2 = 7.75e+22;
35
+ /** Mean radius of the Earth, in metres. */
36
+ declare const EARTH_RADIUS_M = 6371000;
37
+ /** Where the best-fit dipole axis leaves the northern hemisphere (IGRF 2020). */
38
+ declare const GEOMAGNETIC_NORTH_POLE: {
39
+ readonly latitudeDeg: 80.65;
40
+ readonly longitudeDeg: -72.68;
41
+ };
42
+ interface FieldComponents {
43
+ /** Horizontal component, in tesla. The part a plotting compass follows. */
44
+ horizontalT: number;
45
+ /** Vertical component, in tesla. Positive downward, as convention has it. */
46
+ verticalT: number;
47
+ /** Total field, in tesla. */
48
+ totalT: number;
49
+ /** Angle below the horizontal, in degrees. Also called inclination. */
50
+ dipDeg: number;
51
+ }
52
+ /** Resolve a known total field and dip angle into horizontal and vertical parts. */
53
+ declare function earthComponents(totalT: number, dipDeg: number): FieldComponents;
54
+ /** Recover the total field and dip angle from the two measured components. */
55
+ declare function earthFromComponents(horizontalT: number, verticalT: number): FieldComponents;
56
+ /**
57
+ * Apparent dip measured in a vertical plane turned `bearingDeg` out of the
58
+ * magnetic meridian: tan(dip') = tan(dip) / cos(bearing).
59
+ *
60
+ * Turning the dip circle only shortens the horizontal component it can see; the
61
+ * vertical component is unchanged because it lies in every vertical plane. So
62
+ * the needle always appears to dip MORE, never less, and at 90 degrees the
63
+ * horizontal component vanishes entirely and the needle stands upright wherever
64
+ * you are.
65
+ */
66
+ declare function apparentDipDeg(trueDipDeg: number, bearingDeg: number): number;
67
+ /**
68
+ * Recover the true dip from two apparent dips measured in perpendicular planes.
69
+ *
70
+ * This is the actual field method, and it is better than it looks: it needs no
71
+ * knowledge of where the magnetic meridian is, which is the hard thing to find.
72
+ * Turning the circle through a right angle and measuring twice is enough,
73
+ * because cot^2 adds.
74
+ */
75
+ declare function trueDipFromTwo(apparent1Deg: number, apparent2Deg: number): number;
76
+ interface DipoleField extends FieldComponents {
77
+ /** Latitude measured from the dipole's own equator, in degrees. */
78
+ magneticLatitudeDeg: number;
79
+ }
80
+ /**
81
+ * The field of a centred dipole at a given magnetic latitude and radius.
82
+ *
83
+ * H = B0 cos(lat) Z = 2 B0 sin(lat) B0 = mu0 m / 4 pi r^3
84
+ *
85
+ * The factor of two on the vertical component is the whole character of a
86
+ * dipole field, and everything else follows from it: the field at the pole is
87
+ * twice the field at the equator, and tan(dip) = 2 tan(latitude).
88
+ */
89
+ declare function dipoleFieldAt(magneticLatitudeDeg: number, radiusM?: number, momentAm2?: number): DipoleField;
90
+ /**
91
+ * The dipole rule relating dip to magnetic latitude: tan(dip) = 2 tan(lat).
92
+ *
93
+ * Worth having on its own because it is the one line of this model a student is
94
+ * ever asked to remember, and because it says the dip reaches 45 degrees at a
95
+ * magnetic latitude of only 26.6 degrees, not 45.
96
+ */
97
+ declare const dipFromMagneticLatitude: (latitudeDeg: number) => number;
98
+ /** The inverse: where on the dipole you must be to see a given dip. */
99
+ declare const magneticLatitudeFromDip: (dipDeg: number) => number;
100
+ interface GeographicPlace {
101
+ latitudeDeg: number;
102
+ /** East positive. */
103
+ longitudeDeg: number;
104
+ label?: string;
105
+ }
106
+ interface LocalField extends DipoleField {
107
+ /**
108
+ * Angle from true north to magnetic north, in degrees, east positive.
109
+ *
110
+ * This is why a compass is not a map. A navigator adds it to a compass
111
+ * bearing to get a true one, and getting the sign wrong puts you off by twice
112
+ * the angle rather than leaving you where you started.
113
+ */
114
+ declinationDeg: number;
115
+ /** Angular distance from the geomagnetic north pole, in degrees. */
116
+ colatitudeDeg: number;
117
+ }
118
+ /**
119
+ * The dipole model's prediction for an actual place on the globe.
120
+ *
121
+ * Two spherical-triangle results do the work. The magnetic latitude is 90
122
+ * degrees minus the angular distance to the geomagnetic pole, and the
123
+ * declination is simply the bearing you would walk to reach that pole, because
124
+ * in a dipole model that bearing IS where the compass points.
125
+ */
126
+ declare function fieldAtPlace(place: GeographicPlace, pole?: GeographicPlace, momentAm2?: number): LocalField;
127
+ /**
128
+ * Turn a compass bearing into a true one.
129
+ *
130
+ * The rule everybody gets backwards once: true = magnetic + declination, with
131
+ * east declination positive. Going the other way, subtract.
132
+ */
133
+ declare const trueBearingDeg: (compassBearingDeg: number, declinationDeg: number) => number;
134
+ declare const compassBearingDeg: (trueBearing: number, declinationDeg: number) => number;
135
+ /**
136
+ * A magnetic field line of a centred dipole, as magnetic latitudes along it.
137
+ *
138
+ * Dipole field lines satisfy r = L cos^2(latitude), where L is how far out the
139
+ * line reaches at the magnetic equator, measured in Earth radii. This is the
140
+ * shape that puts the aurora in a ring rather than over the pole: a line
141
+ * starting at high latitude reaches only a little way out, while one leaving
142
+ * near the equator reaches far into space and is where particles are trapped.
143
+ */
144
+ declare function dipoleFieldLine(shellL: number, points?: number): Array<{
145
+ latitudeDeg: number;
146
+ radiusEarths: number;
147
+ }>;
148
+ /** Where a dipole field line of shell L touches down, in degrees of latitude. */
149
+ declare const footLatitudeDeg: (shellL: number) => number;
150
+ //#endregion
151
+ export { DipoleField, EARTH_DIPOLE_MOMENT_AM2, EARTH_RADIUS_M, FieldComponents, GEOMAGNETIC_NORTH_POLE, GeographicPlace, LocalField, apparentDipDeg, compassBearingDeg, dipFromMagneticLatitude, dipoleFieldAt, dipoleFieldLine, earthComponents, earthFromComponents, fieldAtPlace, footLatitudeDeg, magneticLatitudeFromDip, trueBearingDeg, trueDipFromTwo };
@@ -0,0 +1,184 @@
1
+ //#region src/physics/magnetism/geomagnetism.ts
2
+ /**
3
+ * @classytic/stage/field/geomagnetism — the Earth's magnetic field, as the
4
+ * tilted dipole it very nearly is.
5
+ *
6
+ * A textbook gives dip and declination as numbers to look up. They are not
7
+ * lookups: a single dipole at the centre of the Earth, tilted about 11 degrees
8
+ * from the spin axis, predicts both to within a few degrees almost everywhere,
9
+ * and predicts the field strength to within about 10%. That is worth computing
10
+ * rather than tabulating, because the model explains WHY dip is zero at the
11
+ * magnetic equator and 90 degrees at the pole, and a table never can.
12
+ *
13
+ * ── What this model does not do ────────────────────────────────────────────────
14
+ * The real field has non-dipole parts (roughly 10% of it) from fluid motion in
15
+ * the core, plus crustal magnetism, plus a daily variation from currents in the
16
+ * ionosphere. So a value here can be a few degrees off what a survey measures,
17
+ * and the discrepancy IS the non-dipole field rather than an error. Anything
18
+ * needing survey accuracy wants IGRF, which is a 195-coefficient spherical
19
+ * harmonic model and a different kind of object entirely.
20
+ *
21
+ * ── Two different "magnetic poles" ─────────────────────────────────────────────
22
+ * The GEOMAGNETIC pole is where the best-fit dipole's axis cuts the surface.
23
+ * The magnetic DIP pole is where a dip needle actually stands vertical. They
24
+ * are about 1000 km apart and move differently, because the non-dipole field
25
+ * displaces the second and not the first. This module uses the geomagnetic pole,
26
+ * which is the only one a dipole model has.
27
+ */
28
+ /** Permeability of free space, in T m A^-1. */
29
+ const MU_0 = 4 * Math.PI * 1e-7;
30
+ /**
31
+ * The Earth's dipole moment, in A m^2 (IGRF epoch 2020, and falling slowly).
32
+ *
33
+ * The same quantity as a coil's N I A. A current loop of one turn enclosing the
34
+ * Earth's cross-section would need about 2 billion amperes to match it.
35
+ */
36
+ const EARTH_DIPOLE_MOMENT_AM2 = 775e20;
37
+ /** Mean radius of the Earth, in metres. */
38
+ const EARTH_RADIUS_M = 6371e3;
39
+ /** Where the best-fit dipole axis leaves the northern hemisphere (IGRF 2020). */
40
+ const GEOMAGNETIC_NORTH_POLE = {
41
+ latitudeDeg: 80.65,
42
+ longitudeDeg: -72.68
43
+ };
44
+ const radians = (deg) => deg * Math.PI / 180;
45
+ const degrees = (rad) => rad * 180 / Math.PI;
46
+ /** Resolve a known total field and dip angle into horizontal and vertical parts. */
47
+ function earthComponents(totalT, dipDeg) {
48
+ const dip = radians(dipDeg);
49
+ return {
50
+ horizontalT: totalT * Math.cos(dip),
51
+ verticalT: totalT * Math.sin(dip),
52
+ totalT,
53
+ dipDeg
54
+ };
55
+ }
56
+ /** Recover the total field and dip angle from the two measured components. */
57
+ function earthFromComponents(horizontalT, verticalT) {
58
+ return {
59
+ horizontalT,
60
+ verticalT,
61
+ totalT: Math.hypot(horizontalT, verticalT),
62
+ dipDeg: degrees(Math.atan2(verticalT, horizontalT))
63
+ };
64
+ }
65
+ /**
66
+ * Apparent dip measured in a vertical plane turned `bearingDeg` out of the
67
+ * magnetic meridian: tan(dip') = tan(dip) / cos(bearing).
68
+ *
69
+ * Turning the dip circle only shortens the horizontal component it can see; the
70
+ * vertical component is unchanged because it lies in every vertical plane. So
71
+ * the needle always appears to dip MORE, never less, and at 90 degrees the
72
+ * horizontal component vanishes entirely and the needle stands upright wherever
73
+ * you are.
74
+ */
75
+ function apparentDipDeg(trueDipDeg, bearingDeg) {
76
+ const cos = Math.cos(radians(bearingDeg));
77
+ if (Math.abs(cos) < 1e-12) return 90;
78
+ return degrees(Math.atan(Math.tan(radians(trueDipDeg)) / cos));
79
+ }
80
+ /**
81
+ * Recover the true dip from two apparent dips measured in perpendicular planes.
82
+ *
83
+ * This is the actual field method, and it is better than it looks: it needs no
84
+ * knowledge of where the magnetic meridian is, which is the hard thing to find.
85
+ * Turning the circle through a right angle and measuring twice is enough,
86
+ * because cot^2 adds.
87
+ */
88
+ function trueDipFromTwo(apparent1Deg, apparent2Deg) {
89
+ const c1 = 1 / Math.tan(radians(apparent1Deg));
90
+ const c2 = 1 / Math.tan(radians(apparent2Deg));
91
+ return degrees(Math.atan(1 / Math.sqrt(c1 * c1 + c2 * c2)));
92
+ }
93
+ /**
94
+ * The field of a centred dipole at a given magnetic latitude and radius.
95
+ *
96
+ * H = B0 cos(lat) Z = 2 B0 sin(lat) B0 = mu0 m / 4 pi r^3
97
+ *
98
+ * The factor of two on the vertical component is the whole character of a
99
+ * dipole field, and everything else follows from it: the field at the pole is
100
+ * twice the field at the equator, and tan(dip) = 2 tan(latitude).
101
+ */
102
+ function dipoleFieldAt(magneticLatitudeDeg, radiusM = EARTH_RADIUS_M, momentAm2 = EARTH_DIPOLE_MOMENT_AM2) {
103
+ if (radiusM <= 0) throw new RangeError("Radius must be positive.");
104
+ const equatorialT = MU_0 * momentAm2 / (4 * Math.PI * radiusM ** 3);
105
+ const lat = radians(magneticLatitudeDeg);
106
+ const horizontalT = equatorialT * Math.cos(lat);
107
+ const verticalT = 2 * equatorialT * Math.sin(lat);
108
+ return {
109
+ horizontalT,
110
+ verticalT,
111
+ totalT: equatorialT * Math.sqrt(1 + 3 * Math.sin(lat) ** 2),
112
+ dipDeg: degrees(Math.atan2(verticalT, horizontalT)),
113
+ magneticLatitudeDeg
114
+ };
115
+ }
116
+ /**
117
+ * The dipole rule relating dip to magnetic latitude: tan(dip) = 2 tan(lat).
118
+ *
119
+ * Worth having on its own because it is the one line of this model a student is
120
+ * ever asked to remember, and because it says the dip reaches 45 degrees at a
121
+ * magnetic latitude of only 26.6 degrees, not 45.
122
+ */
123
+ const dipFromMagneticLatitude = (latitudeDeg) => degrees(Math.atan(2 * Math.tan(radians(latitudeDeg))));
124
+ /** The inverse: where on the dipole you must be to see a given dip. */
125
+ const magneticLatitudeFromDip = (dipDeg) => degrees(Math.atan(Math.tan(radians(dipDeg)) / 2));
126
+ /**
127
+ * The dipole model's prediction for an actual place on the globe.
128
+ *
129
+ * Two spherical-triangle results do the work. The magnetic latitude is 90
130
+ * degrees minus the angular distance to the geomagnetic pole, and the
131
+ * declination is simply the bearing you would walk to reach that pole, because
132
+ * in a dipole model that bearing IS where the compass points.
133
+ */
134
+ function fieldAtPlace(place, pole = GEOMAGNETIC_NORTH_POLE, momentAm2 = EARTH_DIPOLE_MOMENT_AM2) {
135
+ const lat = radians(place.latitudeDeg);
136
+ const poleLat = radians(pole.latitudeDeg);
137
+ const deltaLong = radians(pole.longitudeDeg - place.longitudeDeg);
138
+ const cosDistance = Math.sin(lat) * Math.sin(poleLat) + Math.cos(lat) * Math.cos(poleLat) * Math.cos(deltaLong);
139
+ const colatitudeDeg = degrees(Math.acos(Math.min(1, Math.max(-1, cosDistance))));
140
+ const magneticLatitudeDeg = 90 - colatitudeDeg;
141
+ const declinationDeg = degrees(Math.atan2(Math.sin(deltaLong) * Math.cos(poleLat), Math.cos(lat) * Math.sin(poleLat) - Math.sin(lat) * Math.cos(poleLat) * Math.cos(deltaLong)));
142
+ return {
143
+ ...dipoleFieldAt(magneticLatitudeDeg, EARTH_RADIUS_M, momentAm2),
144
+ declinationDeg,
145
+ colatitudeDeg
146
+ };
147
+ }
148
+ /**
149
+ * Turn a compass bearing into a true one.
150
+ *
151
+ * The rule everybody gets backwards once: true = magnetic + declination, with
152
+ * east declination positive. Going the other way, subtract.
153
+ */
154
+ const trueBearingDeg = (compassBearingDeg, declinationDeg) => ((compassBearingDeg + declinationDeg) % 360 + 360) % 360;
155
+ const compassBearingDeg = (trueBearing, declinationDeg) => ((trueBearing - declinationDeg) % 360 + 360) % 360;
156
+ /**
157
+ * A magnetic field line of a centred dipole, as magnetic latitudes along it.
158
+ *
159
+ * Dipole field lines satisfy r = L cos^2(latitude), where L is how far out the
160
+ * line reaches at the magnetic equator, measured in Earth radii. This is the
161
+ * shape that puts the aurora in a ring rather than over the pole: a line
162
+ * starting at high latitude reaches only a little way out, while one leaving
163
+ * near the equator reaches far into space and is where particles are trapped.
164
+ */
165
+ function dipoleFieldLine(shellL, points = 120) {
166
+ if (shellL < 1) throw new RangeError("A field line must reach at least the surface, so L >= 1.");
167
+ const footLat = Math.acos(Math.sqrt(1 / shellL));
168
+ const count = points % 2 === 0 ? points + 1 : points;
169
+ return Array.from({ length: count }, (_, i) => {
170
+ const lat = -footLat + 2 * footLat * i / (count - 1);
171
+ return {
172
+ latitudeDeg: degrees(lat),
173
+ radiusEarths: shellL * Math.cos(lat) ** 2
174
+ };
175
+ });
176
+ }
177
+ /** Where a dipole field line of shell L touches down, in degrees of latitude. */
178
+ const footLatitudeDeg = (shellL) => {
179
+ if (shellL < 1) throw new RangeError("A field line must reach at least the surface, so L >= 1.");
180
+ return degrees(Math.acos(Math.sqrt(1 / shellL)));
181
+ };
182
+
183
+ //#endregion
184
+ export { EARTH_DIPOLE_MOMENT_AM2, EARTH_RADIUS_M, GEOMAGNETIC_NORTH_POLE, apparentDipDeg, compassBearingDeg, dipFromMagneticLatitude, dipoleFieldAt, dipoleFieldLine, earthComponents, earthFromComponents, fieldAtPlace, footLatitudeDeg, magneticLatitudeFromDip, trueBearingDeg, trueDipFromTwo };
@@ -0,0 +1,12 @@
1
+ import { CurrentPath, FieldContribution, FieldResult, MIN_DISTANCE_M, MU_0, Streamline, StreamlineOptions, arcCentreT, circularArc, coilAxisT, coilCentreT, fieldAt, finiteWireT, flatCoil, helmholtzPair, longWire, longWireT, pathField, rectangularLoop, segmentField, solenoid, solenoidRings, solenoidT, straightWire, toroidT, traceFieldLine, traceFullFieldLine } from "./biot-savart.mjs";
2
+ import { FluxLoop, MIN_LOOP_CLEARANCE_M, emfFromMutualInductance, emfFromSelfInductance, fluxBesideWire, fluxLinkage, fluxThroughLoop, fluxThroughUniform, inducedEmfV, loopArea, magneticEnergyJ, mutualInductanceH, selfInductanceH, solenoidInductanceH } from "./flux.mjs";
3
+ import { ForceOnPathOptions, PathForce, WireForce, angleFromPlaneDeg, coilTorqueNm, forceOnPath, forcePerMetreOnWire, magneticMomentAm2, momentOfLoop, parallelWireForce, torqueFromMoment } from "./magnetic-force.mjs";
4
+ import { DipoleField, EARTH_DIPOLE_MOMENT_AM2, EARTH_RADIUS_M, FieldComponents, GEOMAGNETIC_NORTH_POLE, GeographicPlace, LocalField, apparentDipDeg, compassBearingDeg, dipFromMagneticLatitude, dipoleFieldAt, dipoleFieldLine, earthComponents, earthFromComponents, fieldAtPlace, footLatitudeDeg, magneticLatitudeFromDip, trueBearingDeg, trueDipFromTwo } from "./geomagnetism.mjs";
5
+ import { SpatialFieldMode, fieldsFor, lorentzForce3D, traceLorentz } from "./lorentz.mjs";
6
+
7
+ //#region src/physics/magnetism/index.d.ts
8
+ declare namespace index_d_exports {
9
+ export { CurrentPath, DipoleField, EARTH_DIPOLE_MOMENT_AM2, EARTH_RADIUS_M, FieldComponents, FieldContribution, FieldResult, FluxLoop, ForceOnPathOptions, GEOMAGNETIC_NORTH_POLE, GeographicPlace, LocalField, MIN_DISTANCE_M, MIN_LOOP_CLEARANCE_M, MU_0, PathForce, SpatialFieldMode, Streamline, StreamlineOptions, WireForce, angleFromPlaneDeg, apparentDipDeg, arcCentreT, circularArc, coilAxisT, coilCentreT, coilTorqueNm, compassBearingDeg, dipFromMagneticLatitude, dipoleFieldAt, dipoleFieldLine, earthComponents, earthFromComponents, emfFromMutualInductance, emfFromSelfInductance, fieldAt, fieldAtPlace, fieldsFor, finiteWireT, flatCoil, fluxBesideWire, fluxLinkage, fluxThroughLoop, fluxThroughUniform, footLatitudeDeg, forceOnPath, forcePerMetreOnWire, helmholtzPair, inducedEmfV, longWire, longWireT, loopArea, lorentzForce3D, magneticEnergyJ, magneticLatitudeFromDip, magneticMomentAm2, momentOfLoop, mutualInductanceH, parallelWireForce, pathField, rectangularLoop, segmentField, selfInductanceH, solenoid, solenoidInductanceH, solenoidRings, solenoidT, straightWire, toroidT, torqueFromMoment, traceFieldLine, traceFullFieldLine, traceLorentz, trueBearingDeg, trueDipFromTwo };
10
+ }
11
+ //#endregion
12
+ export { CurrentPath, DipoleField, EARTH_DIPOLE_MOMENT_AM2, EARTH_RADIUS_M, FieldComponents, FieldContribution, FieldResult, FluxLoop, ForceOnPathOptions, GEOMAGNETIC_NORTH_POLE, GeographicPlace, LocalField, MIN_DISTANCE_M, MIN_LOOP_CLEARANCE_M, MU_0, PathForce, SpatialFieldMode, Streamline, StreamlineOptions, WireForce, angleFromPlaneDeg, apparentDipDeg, arcCentreT, circularArc, coilAxisT, coilCentreT, coilTorqueNm, compassBearingDeg, dipFromMagneticLatitude, dipoleFieldAt, dipoleFieldLine, earthComponents, earthFromComponents, emfFromMutualInductance, emfFromSelfInductance, fieldAt, fieldAtPlace, fieldsFor, finiteWireT, flatCoil, fluxBesideWire, fluxLinkage, fluxThroughLoop, fluxThroughUniform, footLatitudeDeg, forceOnPath, forcePerMetreOnWire, helmholtzPair, index_d_exports, inducedEmfV, longWire, longWireT, loopArea, lorentzForce3D, magneticEnergyJ, magneticLatitudeFromDip, magneticMomentAm2, momentOfLoop, mutualInductanceH, parallelWireForce, pathField, rectangularLoop, segmentField, selfInductanceH, solenoid, solenoidInductanceH, solenoidRings, solenoidT, straightWire, toroidT, torqueFromMoment, traceFieldLine, traceFullFieldLine, traceLorentz, trueBearingDeg, trueDipFromTwo };