@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,134 @@
1
+ import { calc, texSigFigs } from "../../core/worked.mjs";
2
+ import { toRadians } from "../vectors.mjs";
3
+
4
+ //#region src/physics/rotation/torque.ts
5
+ /**
6
+ * Torque: the rotational analogue of force, and the couple.
7
+ *
8
+ * THE ANALOGY IS THE POINT, and it is worth stating once rather than rediscovering per topic. Every
9
+ * equation of rotation is a linear one with the quantities swapped:
10
+ *
11
+ * s → θ v → ω a → α m → I F → τ p → L
12
+ *
13
+ * so `F = ma` becomes `τ = Iα`, `p = mv` becomes `L = Iω`, and `½mv²` becomes `½Iω²`. A student who
14
+ * has that mapping has the whole chapter and does not need to memorise a second set of formulas;
15
+ * one who does not has to learn everything twice. {@link ROTATIONAL_ANALOGUE} exists so a lesson
16
+ * can print it as a table.
17
+ *
18
+ * A COUPLE is the case worth separating out: two equal and opposite forces that do not share a line
19
+ * of action. Their resultant FORCE is exactly zero, so the body does not accelerate anywhere, and
20
+ * yet it turns. That is the thing a learner needs to see, because it contradicts the reasonable
21
+ * belief that zero net force means nothing happens. A couple's moment is also the same about EVERY
22
+ * point, which is why a steering wheel turns the same whichever part of it you hold.
23
+ *
24
+ * Pure: no React, no pixels.
25
+ */
26
+ /** The linear quantity and its rotational counterpart, for a lesson that prints the mapping. */
27
+ const ROTATIONAL_ANALOGUE = [
28
+ {
29
+ linear: "displacement s",
30
+ rotational: "angle θ",
31
+ relation: "s = rθ"
32
+ },
33
+ {
34
+ linear: "velocity v",
35
+ rotational: "angular velocity ω",
36
+ relation: "v = rω"
37
+ },
38
+ {
39
+ linear: "acceleration a",
40
+ rotational: "angular acceleration α",
41
+ relation: "a = rα"
42
+ },
43
+ {
44
+ linear: "mass m",
45
+ rotational: "moment of inertia I",
46
+ relation: "I = Σmr²"
47
+ },
48
+ {
49
+ linear: "force F",
50
+ rotational: "torque τ",
51
+ relation: "τ = Iα"
52
+ },
53
+ {
54
+ linear: "momentum p = mv",
55
+ rotational: "angular momentum L = Iω",
56
+ relation: "L = mvr"
57
+ },
58
+ {
59
+ linear: "kinetic energy ½mv²",
60
+ rotational: "rotational KE ½Iω²",
61
+ relation: ""
62
+ }
63
+ ];
64
+ /**
65
+ * The turning effect of one force: `τ = F r sin θ`.
66
+ *
67
+ * `θ` is between the force and the line from the axis to where it acts. Only the PERPENDICULAR
68
+ * component turns anything; a force aimed straight at the hinge has no effect however large it is,
69
+ * which is why you push a door at its edge and at right angles.
70
+ */
71
+ function torque(force, distance, angleDeg = 90, sf = 3) {
72
+ const value = force * distance * Math.sin(toRadians(angleDeg));
73
+ return {
74
+ value,
75
+ worked: calc().step("\\tau = F r \\sin\\theta", "only the component at right angles to the arm turns anything").step(`\\tau = ${texSigFigs(force, sf)} \\times ${texSigFigs(distance, sf)} \\times \\sin ${texSigFigs(angleDeg, sf)}^\\circ = ${texSigFigs(value, sf)}\\ \\mathrm{N\\,m}`, angleDeg % 180 === 0 ? "aimed straight along the arm, so it turns nothing at all however hard you push" : void 0).done(value)
76
+ };
77
+ }
78
+ /**
79
+ * The moment of a couple: `M = F d`, with `d` the perpendicular distance BETWEEN the two forces.
80
+ *
81
+ * Not `F × distance from some axis`, because there is no axis in it. The moment of a couple is the
82
+ * same about every point in the plane, which is the property that makes it a couple rather than two
83
+ * separate moments, and it is why the answer never asks you where the pivot is.
84
+ */
85
+ function couple(force, separation, sf = 3) {
86
+ const value = force * separation;
87
+ return {
88
+ value,
89
+ worked: calc().step("M = F d", "d is the perpendicular distance between the two forces, and the moment is the same about EVERY point").step(`M = ${texSigFigs(force, sf)} \\times ${texSigFigs(separation, sf)} = ${texSigFigs(value, sf)}\\ \\mathrm{N\\,m}`, "the resultant force is zero, so it does not go anywhere. It only turns").done(value)
90
+ };
91
+ }
92
+ /**
93
+ * The force a second couple needs to balance the first.
94
+ *
95
+ * Two couples balance when their moments are equal and opposite, so a wide couple can be held by a
96
+ * small force and a narrow one needs a large force. That is the whole of the rectangular-plate
97
+ * question these chapters set.
98
+ */
99
+ function balancingCouple(known, separation, sf = 3) {
100
+ if (separation <= 0) throw new Error("The second couple needs a positive separation.");
101
+ const force = known.force * known.separation / separation;
102
+ return {
103
+ force,
104
+ worked: calc().step("M_1 = M_2", "in balance the two moments are equal in size and opposite in sense").step(`${texSigFigs(known.force, sf)} \\times ${texSigFigs(known.separation, sf)} = P \\times ${texSigFigs(separation, sf)}`).step(`P = ${texSigFigs(force, sf)}\\ \\mathrm{N}`, "a couple acting over a shorter distance needs a proportionally bigger force").done(force)
105
+ };
106
+ }
107
+ /**
108
+ * `τ = Iα`: Newton's second law, rotated.
109
+ *
110
+ * Give any two and it returns the third. The same torque produces very different accelerations on
111
+ * bodies of the same mass shaped differently, which is the thing moment of inertia is FOR.
112
+ */
113
+ function angularAcceleration(torqueValue, inertia, sf = 3) {
114
+ if (inertia <= 0) throw new Error("Moment of inertia must be positive.");
115
+ const alpha = torqueValue / inertia;
116
+ return {
117
+ alpha,
118
+ worked: calc().step("\\tau = I\\alpha", "F = ma with every quantity replaced by its rotational counterpart").step(`\\alpha = \\frac{${texSigFigs(torqueValue, sf)}}{${texSigFigs(inertia, sf)}} = ${texSigFigs(alpha, sf)}\\ \\mathrm{rad\\,s^{-2}}`, "the same torque spins a hoop far more slowly than a disc of equal mass").done(alpha)
119
+ };
120
+ }
121
+ /**
122
+ * The torque needed to change angular momentum at a given rate: `τ = dL/dt`.
123
+ *
124
+ * The more fundamental statement, and the one that survives when the moment of inertia itself is
125
+ * changing, which `τ = Iα` does not. Supplied as a rate rather than as a derivative because that is
126
+ * how the question arrives: "the angular momentum changes by this much in this time".
127
+ */
128
+ const torqueFromRate = (deltaL, deltaT) => {
129
+ if (deltaT <= 0) throw new Error("The interval must be positive.");
130
+ return deltaL / deltaT;
131
+ };
132
+
133
+ //#endregion
134
+ export { ROTATIONAL_ANALOGUE, angularAcceleration, balancingCouple, couple, torque, torqueFromRate };
@@ -0,0 +1,64 @@
1
+ import { Worked } from "../core/worked.mjs";
2
+
3
+ //#region src/physics/vectors.d.ts
4
+ declare const DEG: number;
5
+ declare const toRadians: (deg: number) => number;
6
+ declare const toDegrees: (rad: number) => number;
7
+ /** A vector as a pair of perpendicular components, in whatever unit the magnitude was in. */
8
+ interface Components {
9
+ /** Along the horizontal. */
10
+ readonly x: number;
11
+ /** Along the vertical, positive UPWARDS. See {@link SignConvention} to reinterpret. */
12
+ readonly y: number;
13
+ }
14
+ /** A vector as a size and a direction. */
15
+ interface Polar {
16
+ readonly magnitude: number;
17
+ /** Above the horizontal, in degrees. Negative points below it. */
18
+ readonly angleDeg: number;
19
+ }
20
+ /**
21
+ * Split a magnitude at an angle above the horizontal into horizontal and vertical parts.
22
+ *
23
+ * Rounding matters here in a way it does not elsewhere: `Math.cos(Math.PI / 2)` is 6.1e-17, not 0,
24
+ * and a horizontal component of 6.1e-17 propagates into a range of 1.2e-16 m, which prints as
25
+ * `1.22 × 10^-16 m` in a derivation a student is reading. A vertical launch has zero range and the
26
+ * working should say zero.
27
+ */
28
+ declare function resolve(magnitude: number, angleDeg: number): Components;
29
+ /** Components back to a magnitude and a direction: the reverse of {@link resolve}. */
30
+ declare function compose(x: number, y: number): Polar;
31
+ /**
32
+ * The working for a resolution, for a lesson that wants to show it rather than assert it.
33
+ *
34
+ * The first line is the definition, not the substitution, because the point being taught is that
35
+ * the components come from a right-angled triangle whose hypotenuse is the vector.
36
+ */
37
+ declare function resolveSteps(magnitude: number, angleDeg: number, unit: string, sf?: number): Worked;
38
+ /** Which vertical direction the numbers in an equation are measured as positive. */
39
+ type Positive = 'up' | 'down';
40
+ /**
41
+ * A declared choice of positive direction, and the signed values that follow from it.
42
+ *
43
+ * Everything a vertical-motion question gives you arrives as a magnitude plus a direction in words
44
+ * ("thrown upwards at 20 m/s from 10 m up, gravity 9.8 downwards"). This turns those words into
45
+ * the signed numbers SUVAT needs, once, in a way that can be shown and flipped.
46
+ */
47
+ declare class SignConvention {
48
+ readonly positive: Positive;
49
+ constructor(positive: Positive);
50
+ /** `+1` when the given direction is the positive one, `−1` when it opposes it. */
51
+ sign(direction: Positive): 1 | -1;
52
+ /** A magnitude, signed for the direction it acts in. */
53
+ signed(magnitude: number, direction: Positive): number;
54
+ /** Gravity's acceleration under this convention: negative if up is positive. */
55
+ gravity(g?: number): number;
56
+ /** The other choice, for a lab that lets a learner flip it. */
57
+ flipped(): SignConvention;
58
+ /** A sentence a lesson can print: "Taking up as positive, g = −9.8 m/s²." */
59
+ describe(g?: number): string;
60
+ }
61
+ declare const upPositive: () => SignConvention;
62
+ declare const downPositive: () => SignConvention;
63
+ //#endregion
64
+ export { Components, DEG, Polar, Positive, SignConvention, compose, downPositive, resolve, resolveSteps, toDegrees, toRadians, upPositive };
@@ -0,0 +1,114 @@
1
+ import { calc, texSigFigs } from "../core/worked.mjs";
2
+
3
+ //#region src/physics/vectors.ts
4
+ /**
5
+ * Resolving a vector into components, and the sign convention that decides what the components
6
+ * mean.
7
+ *
8
+ * Two separate ideas live here and they are usually taught as one, which is why they confuse
9
+ * people.
10
+ *
11
+ * The first is geometry and has no physics in it: a magnitude at an angle is the same thing as a
12
+ * pair of perpendicular components, `u cos θ` along and `u sin θ` across. That is true of a
13
+ * velocity, a force, a displacement, or a ladder against a wall.
14
+ *
15
+ * The second is bookkeeping: before any of those components can go into an equation, somebody has
16
+ * to say which direction counts as positive. Nothing in the world decides this. A ball thrown
17
+ * upwards has `u = +20, a = −9.8` if up is positive and `u = −20, a = +9.8` if down is positive,
18
+ * and BOTH give the same height at the same time, because the answer is a physical fact and the
19
+ * sign is a choice of description. Textbook chapters that print one worked solution per convention
20
+ * (up-positive here, down-positive there) leave students believing there are two physics.
21
+ *
22
+ * So {@link SignConvention} is a first-class object. A lab can let a learner flip it and watch the
23
+ * numbers in the equation change while the trajectory does not move, which is the moment the idea
24
+ * lands.
25
+ *
26
+ * Pure: no React, no pixels.
27
+ */
28
+ const DEG = Math.PI / 180;
29
+ const toRadians = (deg) => deg * DEG;
30
+ const toDegrees = (rad) => rad / DEG;
31
+ /**
32
+ * Split a magnitude at an angle above the horizontal into horizontal and vertical parts.
33
+ *
34
+ * Rounding matters here in a way it does not elsewhere: `Math.cos(Math.PI / 2)` is 6.1e-17, not 0,
35
+ * and a horizontal component of 6.1e-17 propagates into a range of 1.2e-16 m, which prints as
36
+ * `1.22 × 10^-16 m` in a derivation a student is reading. A vertical launch has zero range and the
37
+ * working should say zero.
38
+ */
39
+ function resolve(magnitude, angleDeg) {
40
+ const a = toRadians(angleDeg);
41
+ return {
42
+ x: snap(magnitude * Math.cos(a), magnitude),
43
+ y: snap(magnitude * Math.sin(a), magnitude)
44
+ };
45
+ }
46
+ /** Components back to a magnitude and a direction: the reverse of {@link resolve}. */
47
+ function compose(x, y) {
48
+ return {
49
+ magnitude: Math.hypot(x, y),
50
+ angleDeg: toDegrees(Math.atan2(y, x))
51
+ };
52
+ }
53
+ /**
54
+ * Kill the floating-point dust that trigonometry leaves at the quarter turns.
55
+ *
56
+ * The threshold is RELATIVE to the magnitude being resolved, and that is not a refinement. A fixed
57
+ * 1e-12 works for a vector of size 1 and quietly stops working as the numbers grow: `cos(pi/2)` is
58
+ * 6.1e-17, so a force of 1e6 N at 90 degrees leaves a horizontal component of 6.1e-11, which sails
59
+ * past an absolute cut-off and goes on to report that a perpendicular force did POSITIVE work. That
60
+ * is a visible wrong answer rather than an ugly printout, which is what the absolute version was
61
+ * guarding against. Scaling the threshold makes the guard hold at every size.
62
+ */
63
+ function snap(value, scale = 1) {
64
+ return Math.abs(value) < Math.abs(scale) * 1e-12 ? 0 : value;
65
+ }
66
+ /**
67
+ * The working for a resolution, for a lesson that wants to show it rather than assert it.
68
+ *
69
+ * The first line is the definition, not the substitution, because the point being taught is that
70
+ * the components come from a right-angled triangle whose hypotenuse is the vector.
71
+ */
72
+ function resolveSteps(magnitude, angleDeg, unit, sf = 3) {
73
+ const { x, y } = resolve(magnitude, angleDeg);
74
+ const u = `\\ \\mathrm{${unit}}`;
75
+ return calc().step(`u_x = u\\cos\\theta, \\quad u_y = u\\sin\\theta`, "the vector is the hypotenuse of a right-angled triangle").step(`u = ${texSigFigs(magnitude, sf)}${u}, \\quad \\theta = ${texSigFigs(angleDeg, sf)}^\\circ`).step(`u_x = ${texSigFigs(magnitude, sf)}\\cos ${texSigFigs(angleDeg, sf)}^\\circ = ${texSigFigs(x, sf)}${u}`).step(`u_y = ${texSigFigs(magnitude, sf)}\\sin ${texSigFigs(angleDeg, sf)}^\\circ = ${texSigFigs(y, sf)}${u}`).done(Math.hypot(x, y));
76
+ }
77
+ /**
78
+ * A declared choice of positive direction, and the signed values that follow from it.
79
+ *
80
+ * Everything a vertical-motion question gives you arrives as a magnitude plus a direction in words
81
+ * ("thrown upwards at 20 m/s from 10 m up, gravity 9.8 downwards"). This turns those words into
82
+ * the signed numbers SUVAT needs, once, in a way that can be shown and flipped.
83
+ */
84
+ var SignConvention = class SignConvention {
85
+ positive;
86
+ constructor(positive) {
87
+ this.positive = positive;
88
+ }
89
+ /** `+1` when the given direction is the positive one, `−1` when it opposes it. */
90
+ sign(direction) {
91
+ return direction === this.positive ? 1 : -1;
92
+ }
93
+ /** A magnitude, signed for the direction it acts in. */
94
+ signed(magnitude, direction) {
95
+ return this.sign(direction) * Math.abs(magnitude);
96
+ }
97
+ /** Gravity's acceleration under this convention: negative if up is positive. */
98
+ gravity(g = 9.8) {
99
+ return this.signed(g, "down");
100
+ }
101
+ /** The other choice, for a lab that lets a learner flip it. */
102
+ flipped() {
103
+ return new SignConvention(this.positive === "up" ? "down" : "up");
104
+ }
105
+ /** A sentence a lesson can print: "Taking up as positive, g = −9.8 m/s²." */
106
+ describe(g = 9.8) {
107
+ return `Taking ${this.positive} as positive, g = ${this.gravity(g)} m/s².`;
108
+ }
109
+ };
110
+ const upPositive = () => new SignConvention("up");
111
+ const downPositive = () => new SignConvention("down");
112
+
113
+ //#endregion
114
+ export { DEG, SignConvention, compose, downPositive, resolve, resolveSteps, toDegrees, toRadians, upPositive };
@@ -1,5 +1,4 @@
1
1
  import { StyleProps } from "./props.mjs";
2
- import { ReactNode } from "react";
3
2
 
4
3
  //#region src/primitives/Dot.d.ts
5
4
  interface DotProps extends StyleProps {
@@ -8,20 +7,8 @@ interface DotProps extends StyleProps {
8
7
  /** Pixel radius (never scaled). */
9
8
  r?: number;
10
9
  }
11
- declare function Dot({
12
- x,
13
- y,
14
- r,
15
- color,
16
- opacity
17
- }: DotProps): ReactNode;
10
+ declare const Dot: import("react").NamedExoticComponent<DotProps>;
18
11
  /** A point with a contrast ring (reads clearly over any background). */
19
- declare function Point({
20
- x,
21
- y,
22
- r,
23
- color,
24
- opacity
25
- }: DotProps): ReactNode;
12
+ declare const Point: import("react").NamedExoticComponent<DotProps>;
26
13
  //#endregion
27
14
  export { Dot, DotProps, Point };
@@ -2,10 +2,12 @@
2
2
 
3
3
  import { fmt } from "../core/coords.mjs";
4
4
  import { useCoords } from "../core/context.mjs";
5
+ import { geomPropsEqual } from "./props.mjs";
6
+ import { memo } from "react";
5
7
  import { jsx, jsxs } from "react/jsx-runtime";
6
8
 
7
9
  //#region src/primitives/Dot.tsx
8
- function Dot({ x, y, r = 5, color = "var(--stage-accent)", opacity = 1 }) {
10
+ const Dot = memo(function Dot({ x, y, r = 5, color = "var(--stage-accent)", opacity = 1 }) {
9
11
  const [px, py] = useCoords().toPx(x, y);
10
12
  return /* @__PURE__ */ jsx("circle", {
11
13
  cx: fmt(px),
@@ -14,9 +16,9 @@ function Dot({ x, y, r = 5, color = "var(--stage-accent)", opacity = 1 }) {
14
16
  fill: color,
15
17
  opacity
16
18
  });
17
- }
19
+ }, geomPropsEqual);
18
20
  /** A point with a contrast ring (reads clearly over any background). */
19
- function Point({ x, y, r = 6, color = "var(--stage-accent)", opacity = 1 }) {
21
+ const Point = memo(function Point({ x, y, r = 6, color = "var(--stage-accent)", opacity = 1 }) {
20
22
  const [px, py] = useCoords().toPx(x, y);
21
23
  return /* @__PURE__ */ jsxs("g", {
22
24
  opacity,
@@ -34,7 +36,7 @@ function Point({ x, y, r = 6, color = "var(--stage-accent)", opacity = 1 }) {
34
36
  strokeWidth: 1.5
35
37
  })]
36
38
  });
37
- }
39
+ }, geomPropsEqual);
38
40
 
39
41
  //#endregion
40
42
  export { Dot, Point };
@@ -1,5 +1,3 @@
1
- import { ReactNode } from "react";
2
-
3
1
  //#region src/primitives/Grid.d.ts
4
2
  /** A "nice" step (1,2,5 × 10ⁿ) so ~`target` gridlines span the range. */
5
3
  declare function niceStep(span: number, target?: number): number;
@@ -11,12 +9,35 @@ interface GridProps {
11
9
  stepY?: number;
12
10
  color?: string;
13
11
  }
14
- declare function Grid({
15
- step,
16
- stepX,
17
- stepY,
18
- color
19
- }: GridProps): ReactNode;
12
+ declare const Grid: import("react").NamedExoticComponent<GridProps>;
13
+ /**
14
+ * A spot the axis numbers must leave clear: a point in math coordinates, widened by a half-size and
15
+ * shifted by an offset, both in px. A dot drawn on an axis sits exactly where that axis prints its
16
+ * numbers (an intercept on the y-axis lands on the y tick labels, a focus on the x-axis on the x
17
+ * ones), so without this the number is half-covered by the dot and both read badly.
18
+ */
19
+ interface ClearZone {
20
+ x: number;
21
+ y: number;
22
+ /** Half-width in px (default 10, a dot plus a margin). */
23
+ rx?: number;
24
+ /** Half-height in px (default 10). */
25
+ ry?: number;
26
+ dx?: number;
27
+ dy?: number;
28
+ }
29
+ /** The zone a `<Label>` occupies, from the same props, so a figure can keep the axis numbers off it. */
30
+ declare function clearOfLabel(x: number, y: number, text: string, {
31
+ size,
32
+ anchor,
33
+ dx,
34
+ dy
35
+ }?: {
36
+ size?: number;
37
+ anchor?: 'start' | 'middle' | 'end';
38
+ dx?: number;
39
+ dy?: number;
40
+ }): ClearZone;
20
41
  interface AxesProps {
21
42
  color?: string;
22
43
  ticks?: boolean;
@@ -28,14 +49,9 @@ interface AxesProps {
28
49
  * can READ coordinates off the grid, needed for graphs where the answer is a
29
50
  * point (systems, plotting). Default off (most figures want a clean axis). */
30
51
  labels?: boolean;
52
+ /** Places the numbers must leave clear, such as the dots and labels the figure draws on an axis. */
53
+ keepClear?: readonly ClearZone[];
31
54
  }
32
- declare function Axes({
33
- color,
34
- ticks,
35
- step,
36
- stepX,
37
- stepY,
38
- labels
39
- }: AxesProps): ReactNode;
55
+ declare const Axes: import("react").NamedExoticComponent<AxesProps>;
40
56
  //#endregion
41
- export { Axes, AxesProps, Grid, GridProps, niceStep };
57
+ export { Axes, AxesProps, ClearZone, Grid, GridProps, clearOfLabel, niceStep };
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useCoords } from "../core/context.mjs";
4
+ import { memo } from "react";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
 
6
7
  //#region src/primitives/Grid.tsx
@@ -17,7 +18,7 @@ function tickValues(min, max, step) {
17
18
  for (let v = start; v <= max + step * 1e-6; v += step) out.push(Math.round(v / step) * step);
18
19
  return out;
19
20
  }
20
- function Grid({ step, stepX, stepY, color = "var(--stage-grid)" }) {
21
+ const Grid = memo(function Grid({ step, stepX, stepY, color = "var(--stage-grid)" }) {
21
22
  const c = useCoords();
22
23
  const sx = stepX ?? step ?? niceStep(c.view.xMax - c.view.xMin);
23
24
  const sy = stepY ?? step ?? niceStep(c.view.yMax - c.view.yMin);
@@ -47,20 +48,49 @@ function Grid({ step, stepX, stepY, color = "var(--stage-grid)" }) {
47
48
  }, `gy${y}`));
48
49
  }
49
50
  return /* @__PURE__ */ jsx("g", { children: lines });
51
+ });
52
+ /** Roughly how wide a label renders, in px: digits and letters average a little over half an em. */
53
+ const textWidth = (text, size) => text.length * size * .58;
54
+ /** The zone a `<Label>` occupies, from the same props, so a figure can keep the axis numbers off it. */
55
+ function clearOfLabel(x, y, text, { size = 14, anchor = "middle", dx = 0, dy = 0 } = {}) {
56
+ const w = textWidth(text, size);
57
+ return {
58
+ x,
59
+ y,
60
+ dx: dx + (anchor === "start" ? w / 2 : anchor === "end" ? -w / 2 : 0),
61
+ dy,
62
+ rx: w / 2 + 3,
63
+ ry: size * .5 + 2
64
+ };
50
65
  }
51
66
  const fmtTick = (v) => {
52
67
  if (Number.isInteger(v)) return String(v);
53
68
  const r = Math.round(v * 100) / 100;
54
69
  return String(r);
55
70
  };
56
- function Axes({ color = "var(--stage-axis)", ticks = true, step, stepX, stepY, labels = false }) {
71
+ const hits = (a, b) => a.l < b.r && b.l < a.r && a.t < b.b && b.t < a.b;
72
+ const Axes = memo(function Axes({ color = "var(--stage-axis)", ticks = true, step, stepX, stepY, labels = false, keepClear }) {
57
73
  const c = useCoords();
58
74
  const sx = stepX ?? step ?? niceStep(c.view.xMax - c.view.xMin);
59
75
  const sy = stepY ?? step ?? niceStep(c.view.yMax - c.view.yMin);
60
- const xAxisA = c.toPx(c.view.xMin, 0);
61
- const xAxisB = c.toPx(c.view.xMax, 0);
62
- const yAxisA = c.toPx(0, c.view.yMin);
63
- const yAxisB = c.toPx(0, c.view.yMax);
76
+ /**
77
+ * Where the axes sit when the origin is off-screen.
78
+ *
79
+ * Drawing them at coordinate 0 unconditionally means that a view which does not contain 0 puts
80
+ * the axis line, every tick and every number outside the canvas, so the reader loses the scale
81
+ * entirely and cannot tell what they are looking at. Pin each axis to the nearest edge instead,
82
+ * which is what a plotting library does: the ticks and numbers stay readable, and the axis is
83
+ * still in the right place whenever the origin IS in view.
84
+ */
85
+ const axisY = Math.min(c.view.yMax, Math.max(c.view.yMin, 0));
86
+ const axisX = Math.min(c.view.xMax, Math.max(c.view.xMin, 0));
87
+ const originInView = axisX === 0 && axisY === 0;
88
+ const yLabelsRight = axisX <= c.view.xMin;
89
+ const xLabelsAbove = axisY <= c.view.yMin;
90
+ const xAxisA = c.toPx(c.view.xMin, axisY);
91
+ const xAxisB = c.toPx(c.view.xMax, axisY);
92
+ const yAxisA = c.toPx(axisX, c.view.yMin);
93
+ const yAxisB = c.toPx(axisX, c.view.yMax);
64
94
  const labelStyle = {
65
95
  paintOrder: "stroke",
66
96
  stroke: "var(--stage-bg)",
@@ -93,9 +123,52 @@ function Axes({ color = "var(--stage-axis)", ticks = true, step, stepX, stepY, l
93
123
  strokeWidth: 1.5
94
124
  }, "ay")];
95
125
  if (ticks) {
96
- for (const x of tickValues(c.view.xMin, c.view.xMax, sx)) {
97
- if (Math.abs(x) < 1e-9) continue;
98
- const p = c.toPx(x, 0);
126
+ const xs = tickValues(c.view.xMin, c.view.xMax, sx).filter((x) => !(originInView && Math.abs(x) < 1e-9));
127
+ const ys = tickValues(c.view.yMin, c.view.yMax, sy).filter((y) => !(originInView && Math.abs(y) < 1e-9));
128
+ const origin = c.toPx(0, 0);
129
+ const xLabelAt = (x) => {
130
+ const p = c.toPx(x, axisY);
131
+ return [p[0], p[1] + (xLabelsAbove ? -12 : 14)];
132
+ };
133
+ const yLabelAt = (y) => {
134
+ const p = c.toPx(axisX, y);
135
+ return [p[0] + (yLabelsRight ? 8 : -8), p[1]];
136
+ };
137
+ const yAnchor = yLabelsRight ? "start" : "end";
138
+ const shown = /* @__PURE__ */ new Set();
139
+ if (labels) {
140
+ const taken = (keepClear ?? []).map((z) => {
141
+ const [zx, zy] = c.toPx(z.x, z.y);
142
+ const px = zx + (z.dx ?? 0);
143
+ const py = zy + (z.dy ?? 0);
144
+ const rx = z.rx ?? 10;
145
+ const ry = z.ry ?? 10;
146
+ return {
147
+ l: px - rx,
148
+ r: px + rx,
149
+ t: py - ry,
150
+ b: py + ry
151
+ };
152
+ });
153
+ const place = (key, [px, py], text, anchor) => {
154
+ const w = textWidth(text, 11);
155
+ const l = anchor === "middle" ? px - w / 2 : anchor === "end" ? px - w : px;
156
+ const box = {
157
+ l,
158
+ r: l + w,
159
+ t: py - 5,
160
+ b: py + 5
161
+ };
162
+ if (taken.some((t) => hits(box, t))) return;
163
+ taken.push(box);
164
+ shown.add(key);
165
+ };
166
+ if (originInView) place("l0", [origin[0] - 8, origin[1] + 12], "0", "end");
167
+ for (const x of xs) place(`lx${x}`, xLabelAt(x), fmtTick(x), "middle");
168
+ for (const y of ys) place(`ly${y}`, yLabelAt(y), fmtTick(y), yAnchor);
169
+ }
170
+ for (const x of xs) {
171
+ const p = c.toPx(x, axisY);
99
172
  nodes.push(/* @__PURE__ */ jsx("line", {
100
173
  x1: p[0],
101
174
  y1: p[1] - 4,
@@ -104,11 +177,10 @@ function Axes({ color = "var(--stage-axis)", ticks = true, step, stepX, stepY, l
104
177
  stroke: color,
105
178
  strokeWidth: 1.5
106
179
  }, `tx${x}`));
107
- if (labels) nodes.push(num(`lx${x}`, p[0], p[1] + 14, fmtTick(x), "middle"));
180
+ if (shown.has(`lx${x}`)) nodes.push(num(`lx${x}`, ...xLabelAt(x), fmtTick(x), "middle"));
108
181
  }
109
- for (const y of tickValues(c.view.yMin, c.view.yMax, sy)) {
110
- if (Math.abs(y) < 1e-9) continue;
111
- const p = c.toPx(0, y);
182
+ for (const y of ys) {
183
+ const p = c.toPx(axisX, y);
112
184
  nodes.push(/* @__PURE__ */ jsx("line", {
113
185
  x1: p[0] - 4,
114
186
  y1: p[1],
@@ -117,12 +189,12 @@ function Axes({ color = "var(--stage-axis)", ticks = true, step, stepX, stepY, l
117
189
  stroke: color,
118
190
  strokeWidth: 1.5
119
191
  }, `ty${y}`));
120
- if (labels) nodes.push(num(`ly${y}`, p[0] - 8, p[1], fmtTick(y), "end"));
192
+ if (shown.has(`ly${y}`)) nodes.push(num(`ly${y}`, ...yLabelAt(y), fmtTick(y), yAnchor));
121
193
  }
122
- if (labels) nodes.push(num("l0", c.toPx(0, 0)[0] - 8, c.toPx(0, 0)[1] + 12, "0", "end"));
194
+ if (shown.has("l0")) nodes.push(num("l0", origin[0] - 8, origin[1] + 12, "0", "end"));
123
195
  }
124
196
  return /* @__PURE__ */ jsx("g", { children: nodes });
125
- }
197
+ });
126
198
 
127
199
  //#endregion
128
- export { Axes, Grid, niceStep };
200
+ export { Axes, Grid, clearOfLabel, niceStep };
@@ -1,5 +1,3 @@
1
- import { ReactNode } from "react";
2
-
3
1
  //#region src/primitives/Label.d.ts
4
2
  interface LabelProps {
5
3
  x: number;
@@ -17,17 +15,6 @@ interface LabelProps {
17
15
  /** Upright pixel-space text with a background-colored outline for legibility.
18
16
  * Renders `_`/`^` as real SVG sub/superscripts via the shared `parseRichText`
19
17
  * grammar (the same one labs' HTML `<RichText>` uses, one source of truth). */
20
- declare function Label({
21
- x,
22
- y,
23
- text,
24
- color,
25
- size,
26
- dx,
27
- dy,
28
- anchor,
29
- baseline,
30
- weight
31
- }: LabelProps): ReactNode;
18
+ declare const Label: import("react").NamedExoticComponent<LabelProps>;
32
19
  //#endregion
33
20
  export { Label, LabelProps };
@@ -3,13 +3,14 @@
3
3
  import { fmt } from "../core/coords.mjs";
4
4
  import { parseRichText } from "../core/richText.mjs";
5
5
  import { useCoords } from "../core/context.mjs";
6
+ import { memo } from "react";
6
7
  import { jsx } from "react/jsx-runtime";
7
8
 
8
9
  //#region src/primitives/Label.tsx
9
10
  /** Upright pixel-space text with a background-colored outline for legibility.
10
11
  * Renders `_`/`^` as real SVG sub/superscripts via the shared `parseRichText`
11
12
  * grammar (the same one labs' HTML `<RichText>` uses, one source of truth). */
12
- function Label({ x, y, text, color = "var(--stage-fg)", size = 14, dx = 0, dy = 0, anchor = "middle", baseline = "middle", weight = 600 }) {
13
+ const Label = memo(function Label({ x, y, text, color = "var(--stage-fg)", size = 14, dx = 0, dy = 0, anchor = "middle", baseline = "middle", weight = 600 }) {
13
14
  const [px, py] = useCoords().toPx(x, y);
14
15
  const spans = parseRichText(text);
15
16
  const shift = size * .32;
@@ -39,7 +40,7 @@ function Label({ x, y, text, color = "var(--stage-fg)", size = 14, dx = 0, dy =
39
40
  }, i);
40
41
  })
41
42
  });
42
- }
43
+ });
43
44
 
44
45
  //#endregion
45
46
  export { Label };