@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,98 @@
1
+ import { Worked } from "../../core/worked.mjs";
2
+ import { G_EARTH } from "../kinematics/free-fall.mjs";
3
+
4
+ //#region src/physics/dynamics/forces.d.ts
5
+ /** A force, stored as components so adding is addition. */
6
+ interface Force {
7
+ readonly label: string;
8
+ readonly x: number;
9
+ readonly y: number;
10
+ }
11
+ /** `force('push', 20, 30)`: 20 N at 30° above the horizontal. */
12
+ declare function force(label: string, magnitude: number, angleDeg: number): Force;
13
+ /** A force given directly by its components, for one already resolved. */
14
+ declare const forceXY: (label: string, x: number, y: number) => Force;
15
+ /** The weight of a mass: `mg`, straight down. */
16
+ declare const weight: (mass: number, g?: number) => Force;
17
+ interface Resultant {
18
+ readonly x: number;
19
+ readonly y: number;
20
+ readonly magnitude: number;
21
+ /** Above the horizontal, degrees. */
22
+ readonly angleDeg: number;
23
+ }
24
+ /**
25
+ * Add forces by adding their components.
26
+ *
27
+ * Worth stating plainly because it is the thing that makes force problems tractable and it is
28
+ * routinely obscured: there is no special rule for three forces or for forces at awkward angles.
29
+ * You resolve, you add the two columns, and you are done.
30
+ */
31
+ declare function resultant(forces: readonly Force[]): Resultant;
32
+ /**
33
+ * Is this set of forces in equilibrium?
34
+ *
35
+ * The tolerance is relative to the largest force present, not absolute. A 1 mN imbalance is nothing
36
+ * among meganewtons and is everything among micronewtons, and a fixed epsilon gets one of those
37
+ * two cases wrong.
38
+ */
39
+ declare function isBalanced(forces: readonly Force[], tolerance?: number): boolean;
40
+ /** The worked resultant, set out as the two columns a mark scheme wants to see. */
41
+ declare function resultantSteps(forces: readonly Force[], sf?: number): Worked;
42
+ /**
43
+ * Lami's theorem: three forces in equilibrium satisfy `P/sin α = Q/sin β = R/sin γ`, where each
44
+ * angle is the one OPPOSITE its force, meaning the angle between the other two.
45
+ *
46
+ * The "opposite" is the whole theorem and is where it goes wrong, so the parameter names say it.
47
+ * Given two forces and the angles, the third follows.
48
+ *
49
+ * It is not a new law. Three forces in equilibrium form a closed triangle, and Lami is the sine
50
+ * rule for that triangle. Knowing that is worth more than knowing the formula, because the triangle
51
+ * also tells you the DIRECTION, which the formula does not.
52
+ */
53
+ declare function lami(known: {
54
+ force: number;
55
+ oppositeAngleDeg: number;
56
+ }, wantedOppositeAngleDeg: number, sf?: number): {
57
+ value: number;
58
+ worked: Worked;
59
+ };
60
+ /**
61
+ * A weight held by a string and pulled sideways: the tension and the angle the string makes.
62
+ *
63
+ * Their standard picture, and the reason it is here rather than left to `resultant` is that the
64
+ * answer people want is the ANGLE, which comes out of the ratio rather than out of the sum.
65
+ */
66
+ declare function stringWithSideForce(mass: number, sideForce: number, g?: number, sf?: number): {
67
+ tension: number;
68
+ angleFromVerticalDeg: number;
69
+ worked: Worked;
70
+ };
71
+ interface LiftResult {
72
+ /** The push of the floor on the body: what a scale in the lift would read, in newtons. */
73
+ readonly reaction: number;
74
+ /** The unchanged, actual weight. */
75
+ readonly trueWeight: number;
76
+ /** `reaction / trueWeight`. Above 1 is heavy, below 1 is light, 0 is free fall. */
77
+ readonly factor: number;
78
+ readonly worked: Worked;
79
+ }
80
+ /**
81
+ * What the floor of an accelerating lift pushes back with.
82
+ *
83
+ * `acceleration` is SIGNED: positive is upwards, whatever the lift is currently doing. That is the
84
+ * whole simplification. A lift moving up and slowing down has a downward acceleration and the
85
+ * passenger feels light, which is the case the two printed rules `g + f` and `g − f` leave students
86
+ * unable to handle, because they are indexed on the direction of travel rather than on the
87
+ * direction of the acceleration.
88
+ */
89
+ declare function apparentWeight(mass: number, acceleration: number, g?: number, sf?: number): LiftResult;
90
+ /**
91
+ * The acceleration a body must have for the supporting force to be a given value.
92
+ *
93
+ * The inverse question, and a real one: a rope that can take 306 N holding a 45 kg boy means he has
94
+ * to slide DOWN with at least a certain acceleration, or the rope breaks.
95
+ */
96
+ declare function accelerationForSupport(mass: number, support: number, g?: number): number;
97
+ //#endregion
98
+ export { Force, LiftResult, Resultant, accelerationForSupport, apparentWeight, force, forceXY, isBalanced, lami, resultant, resultantSteps, stringWithSideForce, weight };
@@ -0,0 +1,153 @@
1
+ import { calc, texSigFigs } from "../../core/worked.mjs";
2
+ import { compose, resolve, toDegrees, toRadians } from "../vectors.mjs";
3
+ import { G_EARTH } from "../kinematics/free-fall.mjs";
4
+
5
+ //#region src/physics/dynamics/forces.ts
6
+ /**
7
+ * Forces as vectors: adding them up, recognising balance, and the lift.
8
+ *
9
+ * Nothing here is new physics. It is `vectors` with the bookkeeping that force problems need, kept
10
+ * separate because the questions asked of a set of forces are specific: what is the resultant, is
11
+ * it zero, and if the body is accelerating, what does the floor push back with.
12
+ *
13
+ * ONE IDEA RUNS THROUGH ALL OF IT. A body's weight never changes when a lift accelerates. What
14
+ * changes is the REACTION, the push of the floor on the feet, and that is what a scale reads and
15
+ * what a person feels. Textbooks print `a = g + f` and `a = g − f` as two rules for two cases, and
16
+ * students then cannot say which to use when the lift is moving up but slowing down. There is one
17
+ * rule, `R − W = ma` with `a` signed, and the two printed cases are it with the sign filled in.
18
+ *
19
+ * Pure: no React, no pixels.
20
+ */
21
+ /** `force('push', 20, 30)`: 20 N at 30° above the horizontal. */
22
+ function force(label, magnitude, angleDeg) {
23
+ const { x, y } = resolve(magnitude, angleDeg);
24
+ return {
25
+ label,
26
+ x,
27
+ y
28
+ };
29
+ }
30
+ /** A force given directly by its components, for one already resolved. */
31
+ const forceXY = (label, x, y) => ({
32
+ label,
33
+ x,
34
+ y
35
+ });
36
+ /** The weight of a mass: `mg`, straight down. */
37
+ const weight = (mass, g = G_EARTH) => ({
38
+ label: "weight",
39
+ x: 0,
40
+ y: -mass * g
41
+ });
42
+ /**
43
+ * Add forces by adding their components.
44
+ *
45
+ * Worth stating plainly because it is the thing that makes force problems tractable and it is
46
+ * routinely obscured: there is no special rule for three forces or for forces at awkward angles.
47
+ * You resolve, you add the two columns, and you are done.
48
+ */
49
+ function resultant(forces) {
50
+ let x = 0;
51
+ let y = 0;
52
+ for (const f of forces) {
53
+ x += f.x;
54
+ y += f.y;
55
+ }
56
+ const polar = compose(x, y);
57
+ return {
58
+ x,
59
+ y,
60
+ magnitude: polar.magnitude,
61
+ angleDeg: polar.angleDeg
62
+ };
63
+ }
64
+ /**
65
+ * Is this set of forces in equilibrium?
66
+ *
67
+ * The tolerance is relative to the largest force present, not absolute. A 1 mN imbalance is nothing
68
+ * among meganewtons and is everything among micronewtons, and a fixed epsilon gets one of those
69
+ * two cases wrong.
70
+ */
71
+ function isBalanced(forces, tolerance = 1e-9) {
72
+ const r = resultant(forces);
73
+ const largest = forces.reduce((m, f) => Math.max(m, Math.hypot(f.x, f.y)), 0);
74
+ return r.magnitude <= Math.max(largest, 1) * tolerance;
75
+ }
76
+ /** The worked resultant, set out as the two columns a mark scheme wants to see. */
77
+ function resultantSteps(forces, sf = 3) {
78
+ const r = resultant(forces);
79
+ const w = calc().step("F_x = \\sum F\\cos\\theta, \\quad F_y = \\sum F\\sin\\theta", "resolve every force into the same two directions, then add each column");
80
+ for (const f of forces) w.step(`${f.label}: (${texSigFigs(f.x, sf)},\\; ${texSigFigs(f.y, sf)})\\ \\mathrm{N}`);
81
+ w.step(`\\sum F = (${texSigFigs(r.x, sf)},\\; ${texSigFigs(r.y, sf)})\\ \\mathrm{N}`, "the two totals");
82
+ w.step(`|F| = \\sqrt{F_x^2 + F_y^2} = ${texSigFigs(r.magnitude, sf)}\\ \\mathrm{N}`, `at ${texSigFigs(r.angleDeg, sf)}° to the horizontal`);
83
+ return w.done(r.magnitude);
84
+ }
85
+ /**
86
+ * Lami's theorem: three forces in equilibrium satisfy `P/sin α = Q/sin β = R/sin γ`, where each
87
+ * angle is the one OPPOSITE its force, meaning the angle between the other two.
88
+ *
89
+ * The "opposite" is the whole theorem and is where it goes wrong, so the parameter names say it.
90
+ * Given two forces and the angles, the third follows.
91
+ *
92
+ * It is not a new law. Three forces in equilibrium form a closed triangle, and Lami is the sine
93
+ * rule for that triangle. Knowing that is worth more than knowing the formula, because the triangle
94
+ * also tells you the DIRECTION, which the formula does not.
95
+ */
96
+ function lami(known, wantedOppositeAngleDeg, sf = 3) {
97
+ const sinKnown = Math.sin(toRadians(known.oppositeAngleDeg));
98
+ if (Math.abs(sinKnown) < 1e-12) throw new Error("Lami needs the angle opposite the known force, and that angle cannot be 0° or 180°: the three forces would be parallel and could not close into a triangle.");
99
+ const value = known.force * Math.sin(toRadians(wantedOppositeAngleDeg)) / sinKnown;
100
+ return {
101
+ value,
102
+ worked: calc().step("\\frac{P}{\\sin\\alpha} = \\frac{Q}{\\sin\\beta} = \\frac{R}{\\sin\\gamma}", "each angle is the one BETWEEN THE OTHER TWO forces, not the one at the force itself").step(`Q = P\\,\\frac{\\sin\\beta}{\\sin\\alpha} = ${texSigFigs(known.force, sf)} \\times \\frac{\\sin ${texSigFigs(wantedOppositeAngleDeg, sf)}^\\circ}{\\sin ${texSigFigs(known.oppositeAngleDeg, sf)}^\\circ}`).step(`Q = ${texSigFigs(value, sf)}\\ \\mathrm{N}`, "it is the sine rule for the triangle the three forces close into").done(value)
103
+ };
104
+ }
105
+ /**
106
+ * A weight held by a string and pulled sideways: the tension and the angle the string makes.
107
+ *
108
+ * Their standard picture, and the reason it is here rather than left to `resultant` is that the
109
+ * answer people want is the ANGLE, which comes out of the ratio rather than out of the sum.
110
+ */
111
+ function stringWithSideForce(mass, sideForce, g = G_EARTH, sf = 3) {
112
+ const w = mass * g;
113
+ const tension = Math.hypot(w, sideForce);
114
+ const angle = toDegrees(Math.atan2(sideForce, w));
115
+ return {
116
+ tension,
117
+ angleFromVerticalDeg: angle,
118
+ worked: calc().step("T\\cos\\theta = mg, \\quad T\\sin\\theta = F", "the string must hold the weight up AND balance the sideways pull").step(`\\tan\\theta = \\frac{F}{mg} = \\frac{${texSigFigs(sideForce, sf)}}{${texSigFigs(w, sf)}}`, "dividing kills T, which is why the angle comes first").step(`\\theta = ${texSigFigs(angle, sf)}^\\circ`, "measured from the vertical").step(`T = \\sqrt{(mg)^2 + F^2} = ${texSigFigs(tension, sf)}\\ \\mathrm{N}`).done(tension)
119
+ };
120
+ }
121
+ /**
122
+ * What the floor of an accelerating lift pushes back with.
123
+ *
124
+ * `acceleration` is SIGNED: positive is upwards, whatever the lift is currently doing. That is the
125
+ * whole simplification. A lift moving up and slowing down has a downward acceleration and the
126
+ * passenger feels light, which is the case the two printed rules `g + f` and `g − f` leave students
127
+ * unable to handle, because they are indexed on the direction of travel rather than on the
128
+ * direction of the acceleration.
129
+ */
130
+ function apparentWeight(mass, acceleration, g = G_EARTH, sf = 3) {
131
+ const trueWeight = mass * g;
132
+ const reaction = mass * (g + acceleration);
133
+ if (reaction < 0) throw new Error(`A downward acceleration of ${-acceleration} m/s² is greater than g, so the floor cannot pull the body down. It leaves the floor rather than pressing on it with a negative force.`);
134
+ const worked = calc().step("R - W = ma", "one equation, with a signed upwards. The two rules printed for \"going up\" and \"going down\" are this with the sign already filled in").step(`R = m(g + a) = ${texSigFigs(mass, sf)}(${texSigFigs(g, sf)} + ${texSigFigs(acceleration, sf)})`).step(`R = ${texSigFigs(reaction, sf)}\\ \\mathrm{N}`, acceleration > 0 ? "more than the weight: accelerating upwards, so the floor must both hold the body up and speed it up" : acceleration < 0 ? "less than the weight: accelerating downwards, so the floor holds up less than all of it" : "exactly the weight: no acceleration, so the lift being in motion changes nothing").step(`W = mg = ${texSigFigs(trueWeight, sf)}\\ \\mathrm{N}`, "the weight itself never changed. Only the reaction did, and that is what a scale reads").done(reaction);
135
+ return {
136
+ reaction,
137
+ trueWeight,
138
+ factor: reaction / trueWeight,
139
+ worked
140
+ };
141
+ }
142
+ /**
143
+ * The acceleration a body must have for the supporting force to be a given value.
144
+ *
145
+ * The inverse question, and a real one: a rope that can take 306 N holding a 45 kg boy means he has
146
+ * to slide DOWN with at least a certain acceleration, or the rope breaks.
147
+ */
148
+ function accelerationForSupport(mass, support, g = G_EARTH) {
149
+ return support / mass - g;
150
+ }
151
+
152
+ //#endregion
153
+ export { accelerationForSupport, apparentWeight, force, forceXY, isBalanced, lami, resultant, resultantSteps, stringWithSideForce, weight };
@@ -0,0 +1,76 @@
1
+ import { Worked } from "../../core/worked.mjs";
2
+
3
+ //#region src/physics/dynamics/friction.d.ts
4
+ interface AppliedForce {
5
+ readonly magnitude: number;
6
+ /**
7
+ * Above the SURFACE, in degrees, so 0 is along the slope. Positive lifts the body away from the
8
+ * surface (a pull), negative presses it in (a push). Measured from the surface rather than from
9
+ * the horizontal because it is the surface that friction cares about.
10
+ */
11
+ readonly angleDeg: number;
12
+ }
13
+ interface FrictionSetup {
14
+ readonly mass: number;
15
+ /** Coefficient of STATIC friction: sets the limit before it moves. */
16
+ readonly muStatic: number;
17
+ /** Coefficient of KINETIC friction. Defaults to `muStatic`; in reality it is usually smaller. */
18
+ readonly muKinetic?: number;
19
+ /** Slope angle above the horizontal. Zero is a flat surface. */
20
+ readonly inclineDeg?: number;
21
+ readonly applied?: AppliedForce;
22
+ readonly g?: number;
23
+ }
24
+ interface FrictionResult {
25
+ /** The push of the surface, perpendicular to it. Not `mg` unless the surface is flat and nothing pulls. */
26
+ readonly normal: number;
27
+ /** The force along the slope trying to move it: gravity's component plus any applied force. */
28
+ readonly driving: number;
29
+ /** `μs × N`: the most friction that is available before it slips. */
30
+ readonly limiting: number;
31
+ /** Whether the driving force beat the limit. */
32
+ readonly moves: boolean;
33
+ /** The friction that is ACTUALLY acting, which equals `driving` while it is still. */
34
+ readonly friction: number;
35
+ /** Along the slope, positive in the direction `driving` points. Zero while it is still. */
36
+ readonly acceleration: number;
37
+ readonly worked: Worked;
38
+ }
39
+ /**
40
+ * Work out whether it moves, and then how fast it picks up speed.
41
+ *
42
+ * ```ts
43
+ * frictionOn({ mass: 4, muStatic: 0.10, inclineDeg: 30 }); // a = 4.05 m/s²
44
+ * frictionOn({ mass: 4, muStatic: 0.80, inclineDeg: 30 }); // still: 0.8 > tan 30°
45
+ * ```
46
+ */
47
+ declare function frictionOn(setup: FrictionSetup, sf?: number): FrictionResult;
48
+ /**
49
+ * The angle of friction, `λ = arctan μ`, which is the same number as the angle of repose.
50
+ *
51
+ * They are given two names and taught in two places, and they are one fact: tilt a surface until a
52
+ * body just slips, and `mg sin θ = μ mg cos θ`, so `tan θ = μ` and the mass cancels. A steel block
53
+ * and a feather of the same material slip at the same angle, which is a good thing to make a
54
+ * learner predict before they see it.
55
+ */
56
+ declare function angleOfFriction(mu: number, sf?: number): {
57
+ degrees: number;
58
+ worked: Worked;
59
+ };
60
+ /** The angle of repose IS the angle of friction. Named separately because the syllabus is. */
61
+ declare const angleOfRepose: typeof angleOfFriction;
62
+ /** `38.66°` as `38° 40'`, because that is how the answer is printed in this market's papers. */
63
+ declare function degreesAndMinutes(degrees: number): string;
64
+ /**
65
+ * Which is easier, pulling or pushing at the same angle and force?
66
+ *
67
+ * Returns both so a lab can put them side by side. The answer is always pulling, and the reason is
68
+ * the normal force rather than anything about the direction of travel, which is why asking a
69
+ * learner to predict it is a good question.
70
+ */
71
+ declare function pullVersusPush(setup: Omit<FrictionSetup, 'applied'>, magnitude: number, angleDeg: number): {
72
+ pull: FrictionResult;
73
+ push: FrictionResult;
74
+ };
75
+ //#endregion
76
+ export { AppliedForce, FrictionResult, FrictionSetup, angleOfFriction, angleOfRepose, degreesAndMinutes, frictionOn, pullVersusPush };
@@ -0,0 +1,119 @@
1
+ import { calc, texSigFigs } from "../../core/worked.mjs";
2
+ import { toDegrees, toRadians } from "../vectors.mjs";
3
+ import { G_EARTH } from "../kinematics/free-fall.mjs";
4
+
5
+ //#region src/physics/dynamics/friction.ts
6
+ /**
7
+ * Friction, including the decision every friction question turns on and most solutions skip.
8
+ *
9
+ * STATIC FRICTION IS A RANGE, NOT A VALUE. `F ≤ μsR` is an inequality, and friction takes whatever
10
+ * value it needs to keep the body still, up to that limit. `F = μR` is only true at the instant of
11
+ * slipping. A block pushed with 3 N that needs 3 N to stay put experiences 3 N of friction, not
12
+ * `μsR`, and a solution that writes `μsR` there has computed the wrong number and will usually
13
+ * still get a plausible-looking answer.
14
+ *
15
+ * So this kernel does the decision FIRST and the arithmetic second: work out what is driving the
16
+ * body, work out the largest friction available, compare them, and only then compute. That order
17
+ * is the method, and {@link frictionOn} reports the comparison as part of its result so a lesson
18
+ * can show it and a lab can visualise it as a bar that fills up and then gives way.
19
+ *
20
+ * THE OTHER THING THAT GETS MISSED: a force applied at an angle changes the NORMAL force, and so
21
+ * changes friction. Pulling upwards at an angle lightens the body and helps twice over; pushing
22
+ * downwards at an angle presses it into the surface and fights itself. This is why a suitcase is
23
+ * easier to pull than to push, and it falls straight out of the equations rather than needing to be
24
+ * remembered separately.
25
+ *
26
+ * Pure: no React, no pixels.
27
+ */
28
+ /**
29
+ * Work out whether it moves, and then how fast it picks up speed.
30
+ *
31
+ * ```ts
32
+ * frictionOn({ mass: 4, muStatic: 0.10, inclineDeg: 30 }); // a = 4.05 m/s²
33
+ * frictionOn({ mass: 4, muStatic: 0.80, inclineDeg: 30 }); // still: 0.8 > tan 30°
34
+ * ```
35
+ */
36
+ function frictionOn(setup, sf = 3) {
37
+ const g = setup.g ?? 9.8;
38
+ const muKinetic = setup.muKinetic ?? setup.muStatic;
39
+ const slope = toRadians(setup.inclineDeg ?? 0);
40
+ const weight = setup.mass * g;
41
+ const applied = setup.applied;
42
+ const phi = toRadians(applied?.angleDeg ?? 0);
43
+ const appliedAlong = applied ? applied.magnitude * Math.cos(phi) : 0;
44
+ const appliedAway = applied ? applied.magnitude * Math.sin(phi) : 0;
45
+ const normal = weight * Math.cos(slope) - appliedAway;
46
+ if (normal <= 0) throw new Error(`The applied force lifts the body clear of the surface (normal force would be ${normal.toPrecision(3)} N). There is no friction because there is no contact; this is a projectile question now.`);
47
+ const driving = weight * Math.sin(slope) + appliedAlong;
48
+ const limiting = setup.muStatic * normal;
49
+ const moves = Math.abs(driving) > limiting;
50
+ const kinetic = muKinetic * normal;
51
+ const friction = moves ? kinetic : Math.abs(driving);
52
+ const acceleration = moves ? (Math.abs(driving) - kinetic) / setup.mass : 0;
53
+ const direction = driving < 0 ? -1 : 1;
54
+ const w = calc().step("R = mg\\cos\\theta" + (applied ? " - F\\sin\\phi" : ""), applied ? "the normal force first, because friction depends on it and the applied force changes it" : "the normal force first, because friction depends on it").step(`R = ${texSigFigs(normal, sf)}\\ \\mathrm{N}`).step("F_{\\text{max}} = \\mu_s R", "the MOST friction available. Static friction is an inequality, F ≤ μsR, not an equation").step(`F_{\\text{max}} = ${texSigFigs(setup.muStatic, sf)} \\times ${texSigFigs(normal, sf)} = ${texSigFigs(limiting, sf)}\\ \\mathrm{N}`).step(`\\text{driving} = ${texSigFigs(Math.abs(driving), sf)}\\ \\mathrm{N} \\;${moves ? ">" : "\\leq"}\\; ${texSigFigs(limiting, sf)}\\ \\mathrm{N}`, moves ? "the drive beats the limit, so it slips and friction drops to its kinetic value" : "the limit is not reached, so friction is exactly as large as it needs to be and no larger");
55
+ if (moves) {
56
+ w.step(`ma = ${texSigFigs(Math.abs(driving), sf)} - \\mu_k R = ${texSigFigs(Math.abs(driving), sf)} - ${texSigFigs(kinetic, sf)}`);
57
+ w.step(`a = ${texSigFigs(acceleration * direction, sf)}\\ \\mathrm{m\\,s^{-2}}`, `to ${sf} significant figures`);
58
+ } else w.step(`f = ${texSigFigs(friction, sf)}\\ \\mathrm{N}, \\quad a = 0`, "friction matches the drive exactly. Writing μsR here would be the wrong number");
59
+ return {
60
+ normal,
61
+ driving,
62
+ limiting,
63
+ moves,
64
+ friction,
65
+ acceleration: acceleration * direction,
66
+ worked: w.done(acceleration * direction)
67
+ };
68
+ }
69
+ /**
70
+ * The angle of friction, `λ = arctan μ`, which is the same number as the angle of repose.
71
+ *
72
+ * They are given two names and taught in two places, and they are one fact: tilt a surface until a
73
+ * body just slips, and `mg sin θ = μ mg cos θ`, so `tan θ = μ` and the mass cancels. A steel block
74
+ * and a feather of the same material slip at the same angle, which is a good thing to make a
75
+ * learner predict before they see it.
76
+ */
77
+ function angleOfFriction(mu, sf = 3) {
78
+ const degrees = toDegrees(Math.atan(mu));
79
+ return {
80
+ degrees,
81
+ worked: calc().step("mg\\sin\\theta = \\mu\\, mg\\cos\\theta", "at the angle where it is just about to slip").step("\\tan\\theta = \\mu", "the mass cancels, so the angle does not depend on how heavy it is").step(`\\theta = \\arctan ${texSigFigs(mu, sf)} = ${texSigFigs(degrees, sf)}^\\circ`, `which is ${degreesAndMinutes(degrees)}`).done(degrees)
82
+ };
83
+ }
84
+ /** The angle of repose IS the angle of friction. Named separately because the syllabus is. */
85
+ const angleOfRepose = angleOfFriction;
86
+ /** `38.66°` as `38° 40'`, because that is how the answer is printed in this market's papers. */
87
+ function degreesAndMinutes(degrees) {
88
+ const whole = Math.floor(degrees);
89
+ const minutes = Math.round((degrees - whole) * 60);
90
+ return minutes === 60 ? `${whole + 1}° 0'` : `${whole}° ${minutes}'`;
91
+ }
92
+ /**
93
+ * Which is easier, pulling or pushing at the same angle and force?
94
+ *
95
+ * Returns both so a lab can put them side by side. The answer is always pulling, and the reason is
96
+ * the normal force rather than anything about the direction of travel, which is why asking a
97
+ * learner to predict it is a good question.
98
+ */
99
+ function pullVersusPush(setup, magnitude, angleDeg) {
100
+ return {
101
+ pull: frictionOn({
102
+ ...setup,
103
+ applied: {
104
+ magnitude,
105
+ angleDeg: Math.abs(angleDeg)
106
+ }
107
+ }),
108
+ push: frictionOn({
109
+ ...setup,
110
+ applied: {
111
+ magnitude,
112
+ angleDeg: -Math.abs(angleDeg)
113
+ }
114
+ })
115
+ };
116
+ }
117
+
118
+ //#endregion
119
+ export { angleOfFriction, angleOfRepose, degreesAndMinutes, frictionOn, pullVersusPush };
@@ -0,0 +1,68 @@
1
+ import { Worked } from "../../core/worked.mjs";
2
+
3
+ //#region src/physics/dynamics/system.d.ts
4
+ interface LinkedBody {
5
+ readonly label: string;
6
+ readonly mass: number;
7
+ /**
8
+ * The force along this body's direction of motion that does NOT come from a string, signed so
9
+ * that positive drives the system the way you have chosen to call forwards.
10
+ *
11
+ * A hanging mass pulling the system along has `drive = +mg`. A hanging mass on the other side of
12
+ * a pulley, being lifted, has `drive = -mg`. A block on a level table being pushed has
13
+ * `drive = +F`. That single signed number is the whole difference between the textbook's separate
14
+ * cases.
15
+ */
16
+ readonly drive: number;
17
+ /** A resisting force, always opposing motion whichever way it goes. Friction, drag, a brake. */
18
+ readonly resist?: number;
19
+ }
20
+ interface SystemResult {
21
+ /** Positive in the direction you called forwards. */
22
+ readonly acceleration: number;
23
+ readonly moving: boolean;
24
+ readonly totalMass: number;
25
+ readonly totalDrive: number;
26
+ readonly totalResist: number;
27
+ /**
28
+ * The tension in each link: `tensions[i]` joins `bodies[i]` to `bodies[i+1]`, so there is one
29
+ * fewer than there are bodies.
30
+ *
31
+ * Null when the system does not move. That is not a gap, it is the truth: with several bodies
32
+ * held still by friction, how much friction each one contributes is not determined by the
33
+ * equations of motion, and a single number here would be an invention. A stuck system is
34
+ * analysed one body at a time.
35
+ */
36
+ readonly tensions: readonly number[] | null;
37
+ readonly worked: Worked;
38
+ }
39
+ /**
40
+ * Solve a chain of bodies joined by inextensible strings.
41
+ *
42
+ * Order matters: `bodies[0]` is at the back and `bodies[n-1]` at the front, in the direction you
43
+ * called forwards, because that is what decides which side of each string is which.
44
+ */
45
+ declare function linkedSystem(bodies: readonly LinkedBody[], sf?: number): SystemResult;
46
+ interface AtwoodResult extends SystemResult {
47
+ /** The single tension in the string. */
48
+ readonly tension: number;
49
+ /**
50
+ * What the pulley's support has to hold, which is `2T` and NOT the combined weight.
51
+ *
52
+ * It is less, because the system is accelerating: some of the weight is going into the motion
53
+ * rather than into the support. A good prediction question, because almost everyone guesses
54
+ * `(m₁ + m₂)g` first.
55
+ */
56
+ readonly pulleyLoad: number;
57
+ }
58
+ /**
59
+ * Two masses over a frictionless pulley.
60
+ *
61
+ * Not a special case, and deliberately implemented by delegating rather than by its own formula:
62
+ * forwards is "the heavier one descends", so gravity drives one body and hinders the other, and
63
+ * that is the only declaration needed. If this had its own `(m₁−m₂)g/(m₁+m₂)` written out, that
64
+ * formula could drift away from the general solver and nothing would notice.
65
+ */
66
+ declare function atwood(m1: number, m2: number, g?: number, sf?: number): AtwoodResult;
67
+ //#endregion
68
+ export { AtwoodResult, LinkedBody, SystemResult, atwood, linkedSystem };
@@ -0,0 +1,125 @@
1
+ import { calc, texSigFigs } from "../../core/worked.mjs";
2
+ import { G_EARTH } from "../kinematics/free-fall.mjs";
3
+
4
+ //#region src/physics/dynamics/system.ts
5
+ /**
6
+ * Bodies joined by strings: one acceleration, and the tension in every link.
7
+ *
8
+ * THE METHOD IS THE POINT, and it is one trick used twice.
9
+ *
10
+ * First, add the equations for all the bodies together. Every tension appears twice with opposite
11
+ * signs, because the string pulls the body in front backwards exactly as hard as it pulls the body
12
+ * behind forwards, so they all cancel and what is left is
13
+ *
14
+ * a = (total driving force) / (total mass)
15
+ *
16
+ * with no tensions in it at all. That is why the acceleration of a loaded train is easy and the
17
+ * coupling forces are not.
18
+ *
19
+ * Second, to get one tension, draw a line through the system at that string and write the equation
20
+ * for everything on one side of the line. Only that one tension crosses the line, so it is the only
21
+ * unknown left.
22
+ *
23
+ * WHAT THIS COVERS, from one declaration: blocks in a row being pushed, a train and its carriages,
24
+ * a mass on a table pulled by a hanging mass, and an Atwood machine. Those are four separate
25
+ * "types" in a textbook, and the only thing that differs between them is which bodies have gravity
26
+ * helping and which have it hindering. A body's `drive` says so, and the rest is identical.
27
+ *
28
+ * Pure: no React, no pixels.
29
+ */
30
+ /**
31
+ * Solve a chain of bodies joined by inextensible strings.
32
+ *
33
+ * Order matters: `bodies[0]` is at the back and `bodies[n-1]` at the front, in the direction you
34
+ * called forwards, because that is what decides which side of each string is which.
35
+ */
36
+ function linkedSystem(bodies, sf = 3) {
37
+ if (bodies.length === 0) throw new Error("A system needs at least one body.");
38
+ const totalMass = bodies.reduce((m, b) => m + b.mass, 0);
39
+ if (totalMass <= 0) throw new Error("Total mass must be positive.");
40
+ const totalDrive = bodies.reduce((f, b) => f + b.drive, 0);
41
+ const totalResist = bodies.reduce((f, b) => f + Math.abs(b.resist ?? 0), 0);
42
+ const moving = Math.abs(totalDrive) > totalResist;
43
+ const direction = totalDrive < 0 ? -1 : 1;
44
+ const acceleration = moving ? direction * (Math.abs(totalDrive) - totalResist) / totalMass : 0;
45
+ const w = calc().step("a = \\frac{\\sum F}{\\sum m}", "add every body’s equation: each tension appears twice with opposite signs and cancels").step(`a = \\frac{${texSigFigs(totalDrive, sf)}${totalResist ? ` - ${texSigFigs(totalResist, sf)}` : ""}}{${texSigFigs(totalMass, sf)}}`, bodies.map((b) => `${b.label} ${texSigFigs(b.drive, sf)} N`).join(", "));
46
+ if (!moving) {
47
+ /**
48
+ * A stationary system's tensions are determinate ONLY when nothing is resisting.
49
+ *
50
+ * With friction in it, how much each surface contributes is not fixed by the equations of
51
+ * motion: two blocks can share the load any number of ways and every split balances. Reporting
52
+ * a number there would be an invention, so it stays null.
53
+ *
54
+ * With no friction, "not moving" means the drives cancel, and each cut still has exactly one
55
+ * unknown crossing it. Two equal masses over a pulley are the everyday case, and refusing to
56
+ * give their `T = mg` would push the special case out to every caller.
57
+ */
58
+ const determinate = totalResist === 0;
59
+ const held = determinate ? bodies.slice(0, -1).map((_, i) => {
60
+ const ahead = bodies.slice(i + 1);
61
+ return Math.abs(-ahead.reduce((f, b) => f + b.drive, 0));
62
+ }) : null;
63
+ w.step("\\text{driving} \\leq \\text{resisting} \\;\\Rightarrow\\; a = 0", determinate ? "it does not move, so each body is in balance and the string simply holds the load" : "it does not move, and with friction sharing the load the tensions need a body-by-body analysis rather than this one");
64
+ return {
65
+ acceleration: 0,
66
+ moving: false,
67
+ totalMass,
68
+ totalDrive,
69
+ totalResist,
70
+ tensions: held,
71
+ worked: w.done(0)
72
+ };
73
+ }
74
+ w.step(`a = ${texSigFigs(acceleration, sf)}\\ \\mathrm{m\\,s^{-2}}`, "the same for every body, because the string does not stretch");
75
+ const tensions = [];
76
+ for (let i = 0; i < bodies.length - 1; i++) {
77
+ const ahead = bodies.slice(i + 1);
78
+ const massAhead = ahead.reduce((m, b) => m + b.mass, 0);
79
+ const driveAhead = ahead.reduce((f, b) => f + b.drive, 0);
80
+ const resistAhead = ahead.reduce((f, b) => f + Math.abs(b.resist ?? 0), 0);
81
+ const signed = massAhead * acceleration - driveAhead + direction * resistAhead;
82
+ const tension = Math.abs(signed);
83
+ tensions.push(tension);
84
+ w.step(`T_{${i + 1}} = ${texSigFigs(massAhead, sf)}a - ${texSigFigs(driveAhead, sf)} = ${texSigFigs(tension, sf)}\\ \\mathrm{N}`, `cut the string between ${bodies[i].label} and ${bodies[i + 1].label}, then use only what is beyond the cut: only this one tension crosses it${signed < 0 ? `, and it pulls ${bodies[i + 1].label} backwards` : ""}`);
85
+ }
86
+ return {
87
+ acceleration,
88
+ moving: true,
89
+ totalMass,
90
+ totalDrive,
91
+ totalResist,
92
+ tensions,
93
+ worked: w.done(acceleration)
94
+ };
95
+ }
96
+ /**
97
+ * Two masses over a frictionless pulley.
98
+ *
99
+ * Not a special case, and deliberately implemented by delegating rather than by its own formula:
100
+ * forwards is "the heavier one descends", so gravity drives one body and hinders the other, and
101
+ * that is the only declaration needed. If this had its own `(m₁−m₂)g/(m₁+m₂)` written out, that
102
+ * formula could drift away from the general solver and nothing would notice.
103
+ */
104
+ function atwood(m1, m2, g = G_EARTH, sf = 3) {
105
+ const heavy = Math.max(m1, m2);
106
+ const light = Math.min(m1, m2);
107
+ const result = linkedSystem([{
108
+ label: `descending mass ${heavy} kg`,
109
+ mass: heavy,
110
+ drive: heavy * g
111
+ }, {
112
+ label: `rising mass ${light} kg`,
113
+ mass: light,
114
+ drive: -light * g
115
+ }], sf);
116
+ const tension = result.tensions?.[0] ?? light * g;
117
+ return {
118
+ ...result,
119
+ tension,
120
+ pulleyLoad: 2 * tension
121
+ };
122
+ }
123
+
124
+ //#endregion
125
+ export { atwood, linkedSystem };