@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.
- package/LICENSE +1 -1
- package/README.md +5 -1
- package/dist/_virtual/_rolldown/runtime.mjs +18 -0
- package/dist/assets/index.mjs +0 -1
- package/dist/atomic/aufbau.d.mts +144 -0
- package/dist/atomic/aufbau.mjs +384 -0
- package/dist/atomic/elements.d.mts +67 -0
- package/dist/atomic/elements.mjs +785 -0
- package/dist/atomic/emission.d.mts +112 -0
- package/dist/atomic/emission.mjs +179 -0
- package/dist/atomic/index.d.mts +8 -0
- package/dist/atomic/index.mjs +9 -0
- package/dist/atomic/periodic.d.mts +82 -0
- package/dist/atomic/periodic.mjs +231 -0
- package/dist/atomic/properties.d.mts +100 -0
- package/dist/atomic/properties.mjs +574 -0
- package/dist/atomic/shells.d.mts +137 -0
- package/dist/atomic/shells.mjs +170 -0
- package/dist/atomic/trends.d.mts +148 -0
- package/dist/atomic/trends.mjs +225 -0
- package/dist/builder/Palette.mjs +50 -89
- package/dist/builder/SceneBuilder.mjs +15 -73
- package/dist/chem/chromatography.d.mts +66 -0
- package/dist/chem/chromatography.mjs +63 -0
- package/dist/chem/complex.d.mts +114 -0
- package/dist/chem/complex.mjs +234 -0
- package/dist/chem/cycle.d.mts +75 -0
- package/dist/chem/cycle.mjs +145 -0
- package/dist/chem/equation.d.mts +86 -0
- package/dist/chem/equation.mjs +168 -0
- package/dist/chem/formula.d.mts +90 -0
- package/dist/chem/formula.mjs +201 -0
- package/dist/chem/functional-groups.d.mts +31 -0
- package/dist/chem/functional-groups.mjs +187 -0
- package/dist/chem/hazard.d.mts +94 -0
- package/dist/chem/hazard.mjs +329 -0
- package/dist/chem/index.d.mts +34 -33
- package/dist/chem/index.mjs +34 -28
- package/dist/chem/ionic.d.mts +98 -0
- package/dist/chem/ionic.mjs +156 -0
- package/dist/chem/layout.d.mts +46 -0
- package/dist/chem/layout.mjs +342 -0
- package/dist/chem/lewis.d.mts +79 -0
- package/dist/chem/lewis.mjs +163 -0
- package/dist/chem/molecular-orbital.d.mts +114 -0
- package/dist/chem/molecular-orbital.mjs +201 -0
- package/dist/chem/molecule.d.mts +154 -0
- package/dist/chem/molecule.mjs +282 -0
- package/dist/chem/nmr.d.mts +153 -0
- package/dist/chem/nmr.mjs +165 -0
- package/dist/chem/polymer.d.mts +65 -0
- package/dist/chem/polymer.mjs +179 -0
- package/dist/chem/redox.d.mts +138 -0
- package/dist/chem/redox.mjs +328 -0
- package/dist/chem/relations.d.mts +183 -0
- package/dist/chem/relations.mjs +299 -0
- package/dist/chem/skeleton.d.mts +51 -0
- package/dist/chem/skeleton.mjs +186 -0
- package/dist/chem/solution.d.mts +116 -0
- package/dist/chem/solution.mjs +186 -0
- package/dist/chem/species.d.mts +47 -0
- package/dist/chem/species.mjs +128 -0
- package/dist/chem/stereo.d.mts +108 -0
- package/dist/chem/stereo.mjs +286 -0
- package/dist/chem/stoichiometry.d.mts +95 -0
- package/dist/chem/stoichiometry.mjs +145 -0
- package/dist/chem/synthesis.d.mts +113 -0
- package/dist/chem/synthesis.mjs +281 -0
- package/dist/chem/vsepr.d.mts +80 -0
- package/dist/chem/vsepr.mjs +247 -0
- package/dist/circuit/ac.d.mts +163 -0
- package/dist/circuit/ac.mjs +452 -0
- package/dist/circuit/dc.d.mts +92 -0
- package/dist/circuit/dc.mjs +333 -0
- package/dist/circuit/index.d.mts +5 -92
- package/dist/circuit/index.mjs +5 -332
- package/dist/circuit/network.d.mts +99 -0
- package/dist/circuit/network.mjs +440 -0
- package/dist/circuit/topology.d.mts +102 -0
- package/dist/circuit/topology.mjs +143 -0
- package/dist/colour/absorption.d.mts +79 -0
- package/dist/colour/absorption.mjs +137 -0
- package/dist/colour/index.d.mts +6 -0
- package/dist/colour/index.mjs +7 -0
- package/dist/colour/name.d.mts +14 -0
- package/dist/colour/name.mjs +76 -0
- package/dist/colour/observer.d.mts +31 -0
- package/dist/colour/observer.mjs +46 -0
- package/dist/colour/spectrum.d.mts +61 -0
- package/dist/colour/spectrum.mjs +108 -0
- package/dist/colour/srgb.d.mts +49 -0
- package/dist/colour/srgb.mjs +108 -0
- package/dist/core/constants.d.mts +138 -0
- package/dist/core/constants.mjs +139 -0
- package/dist/core/index.d.mts +4 -1
- package/dist/core/index.mjs +4 -1
- package/dist/core/math.d.mts +26 -0
- package/dist/core/math.mjs +37 -0
- package/dist/core/vec3.d.mts +94 -0
- package/dist/core/vec3.mjs +123 -0
- package/dist/core/worked.d.mts +35 -0
- package/dist/core/worked.mjs +44 -0
- package/dist/electro/cell.d.mts +102 -0
- package/dist/electro/cell.mjs +170 -0
- package/dist/electro/conductivity.d.mts +157 -0
- package/dist/electro/conductivity.mjs +156 -0
- package/dist/electro/couples.d.mts +114 -0
- package/dist/electro/couples.mjs +334 -0
- package/dist/electro/electrolysis.d.mts +96 -0
- package/dist/electro/electrolysis.mjs +274 -0
- package/dist/electro/faraday.d.mts +150 -0
- package/dist/electro/faraday.mjs +118 -0
- package/dist/electro/index.d.mts +6 -0
- package/dist/electro/index.mjs +7 -0
- package/dist/finance/accounting/adjustments.d.mts +17 -0
- package/dist/finance/accounting/adjustments.mjs +202 -0
- package/dist/finance/accounting/analysis.d.mts +8 -0
- package/dist/finance/accounting/analysis.mjs +205 -0
- package/dist/finance/accounting/clubs.d.mts +21 -0
- package/dist/finance/accounting/clubs.mjs +217 -0
- package/dist/finance/accounting/company-statements.d.mts +7 -0
- package/dist/finance/accounting/company-statements.mjs +150 -0
- package/dist/finance/accounting/harness.d.mts +7 -0
- package/dist/finance/accounting/harness.mjs +45 -0
- package/dist/finance/accounting/index.d.mts +18 -0
- package/dist/finance/accounting/index.mjs +18 -0
- package/dist/finance/accounting/journalize.d.mts +16 -0
- package/dist/finance/accounting/journalize.mjs +128 -0
- package/dist/finance/accounting/manufacturing.d.mts +8 -0
- package/dist/finance/accounting/manufacturing.mjs +236 -0
- package/dist/finance/accounting/partnership.d.mts +7 -0
- package/dist/finance/accounting/partnership.mjs +152 -0
- package/dist/finance/accounting/period-case.d.mts +6 -0
- package/dist/finance/accounting/period-case.mjs +71 -0
- package/dist/finance/accounting/post.d.mts +9 -0
- package/dist/finance/accounting/post.mjs +84 -0
- package/dist/finance/accounting/precision.d.mts +9 -0
- package/dist/finance/accounting/precision.mjs +15 -0
- package/dist/finance/accounting/rules.d.mts +21 -0
- package/dist/finance/accounting/rules.mjs +37 -0
- package/dist/finance/accounting/solve.d.mts +28 -0
- package/dist/finance/accounting/solve.mjs +92 -0
- package/dist/finance/accounting/statements.d.mts +7 -0
- package/dist/finance/accounting/statements.mjs +142 -0
- package/dist/finance/accounting/types.d.mts +649 -0
- package/dist/finance/accounting/validate.d.mts +7 -0
- package/dist/finance/accounting/validate.mjs +67 -0
- package/dist/finance/accounting/verification.d.mts +24 -0
- package/dist/finance/accounting/verification.mjs +386 -0
- package/dist/finance/bizsim.d.mts +75 -0
- package/dist/finance/bizsim.mjs +296 -0
- package/dist/finance/index.d.mts +95 -0
- package/dist/finance/index.mjs +145 -0
- package/dist/finance/investment-math.d.mts +7 -0
- package/dist/finance/investment-math.mjs +18 -0
- package/dist/finance/management-accounting/budgeting.d.mts +7 -0
- package/dist/finance/management-accounting/budgeting.mjs +189 -0
- package/dist/finance/management-accounting/cash-budget.d.mts +7 -0
- package/dist/finance/management-accounting/cash-budget.mjs +154 -0
- package/dist/finance/management-accounting/costing.d.mts +8 -0
- package/dist/finance/management-accounting/costing.mjs +175 -0
- package/dist/finance/management-accounting/decisions.d.mts +8 -0
- package/dist/finance/management-accounting/decisions.mjs +164 -0
- package/dist/finance/management-accounting/index.d.mts +13 -0
- package/dist/finance/management-accounting/index.mjs +13 -0
- package/dist/finance/management-accounting/integrated-budget.d.mts +8 -0
- package/dist/finance/management-accounting/integrated-budget.mjs +67 -0
- package/dist/finance/management-accounting/inventory-costing.d.mts +7 -0
- package/dist/finance/management-accounting/inventory-costing.mjs +173 -0
- package/dist/finance/management-accounting/investment-appraisal.d.mts +7 -0
- package/dist/finance/management-accounting/investment-appraisal.mjs +148 -0
- package/dist/finance/management-accounting/management-case.d.mts +8 -0
- package/dist/finance/management-accounting/management-case.mjs +91 -0
- package/dist/finance/management-accounting/master-budget.d.mts +7 -0
- package/dist/finance/management-accounting/master-budget.mjs +137 -0
- package/dist/finance/management-accounting/production-costing.d.mts +8 -0
- package/dist/finance/management-accounting/production-costing.mjs +214 -0
- package/dist/finance/management-accounting/types.d.mts +644 -0
- package/dist/finance/management-accounting/variances.d.mts +7 -0
- package/dist/finance/management-accounting/variances.mjs +150 -0
- package/dist/index.d.mts +9 -8
- package/dist/index.mjs +5 -5
- package/dist/interaction/MovableDot.mjs +19 -0
- package/dist/interaction/useDraggable.mjs +24 -4
- package/dist/math/analysis.d.mts +61 -0
- package/dist/math/analysis.mjs +187 -0
- package/dist/math/answer-check.d.mts +50 -0
- package/dist/math/answer-check.mjs +106 -0
- package/dist/math/calculus.d.mts +17 -1
- package/dist/math/calculus.mjs +365 -2
- package/dist/math/complex.d.mts +93 -0
- package/dist/math/complex.mjs +239 -0
- package/dist/math/expr.d.mts +23 -0
- package/dist/math/expr.mjs +36 -0
- package/dist/math/index.d.mts +9 -23
- package/dist/math/index.mjs +9 -38
- package/dist/math/latex.mjs +10 -1
- package/dist/math/parse.mjs +1 -1
- package/dist/math/poly.d.mts +55 -0
- package/dist/math/poly.mjs +273 -0
- package/dist/math/rearrange.d.mts +14 -0
- package/dist/math/rearrange.mjs +125 -0
- package/dist/math/solve.d.mts +21 -0
- package/dist/math/solve.mjs +75 -0
- package/dist/networking/addressing.d.mts +26 -0
- package/dist/networking/addressing.mjs +33 -0
- package/dist/networking/cloud/access.d.mts +36 -0
- package/dist/networking/cloud/access.mjs +95 -0
- package/dist/networking/cloud/containers.d.mts +43 -0
- package/dist/networking/cloud/containers.mjs +54 -0
- package/dist/networking/cloud/edge.d.mts +17 -0
- package/dist/networking/cloud/edge.mjs +31 -0
- package/dist/networking/cloud/failure-domains.d.mts +19 -0
- package/dist/networking/cloud/failure-domains.mjs +22 -0
- package/dist/networking/cloud/geo-routing.d.mts +22 -0
- package/dist/networking/cloud/geo-routing.mjs +64 -0
- package/dist/networking/cloud/index.d.mts +18 -0
- package/dist/networking/cloud/index.mjs +19 -0
- package/dist/networking/cloud/inference-reliability.d.mts +30 -0
- package/dist/networking/cloud/inference-reliability.mjs +76 -0
- package/dist/networking/cloud/inference.d.mts +118 -0
- package/dist/networking/cloud/inference.mjs +179 -0
- package/dist/networking/cloud/infrastructure.d.mts +45 -0
- package/dist/networking/cloud/infrastructure.mjs +96 -0
- package/dist/networking/cloud/observability.d.mts +24 -0
- package/dist/networking/cloud/observability.mjs +34 -0
- package/dist/networking/cloud/orchestration.d.mts +32 -0
- package/dist/networking/cloud/orchestration.mjs +38 -0
- package/dist/networking/cloud/recovery.d.mts +21 -0
- package/dist/networking/cloud/recovery.mjs +27 -0
- package/dist/networking/cloud/releases.d.mts +42 -0
- package/dist/networking/cloud/releases.mjs +135 -0
- package/dist/networking/cloud/scaling.d.mts +25 -0
- package/dist/networking/cloud/scaling.mjs +65 -0
- package/dist/networking/cloud/secrets.d.mts +39 -0
- package/dist/networking/cloud/secrets.mjs +68 -0
- package/dist/networking/cloud/server-deployment.d.mts +29 -0
- package/dist/networking/cloud/server-deployment.mjs +72 -0
- package/dist/networking/cloud/storage.d.mts +16 -0
- package/dist/networking/cloud/storage.mjs +58 -0
- package/dist/networking/cloud/vpc.d.mts +18 -0
- package/dist/networking/cloud/vpc.mjs +100 -0
- package/dist/networking/delivery.d.mts +34 -0
- package/dist/networking/delivery.mjs +52 -0
- package/dist/networking/index.d.mts +47 -0
- package/dist/networking/index.mjs +47 -0
- package/dist/networking/ipv6.d.mts +28 -0
- package/dist/networking/ipv6.mjs +17 -0
- package/dist/networking/journeys.d.mts +83 -0
- package/dist/networking/journeys.mjs +161 -0
- package/dist/networking/models/airtime.d.mts +87 -0
- package/dist/networking/models/airtime.mjs +130 -0
- package/dist/networking/models/cellular.d.mts +157 -0
- package/dist/networking/models/cellular.mjs +252 -0
- package/dist/networking/models/encapsulation.d.mts +98 -0
- package/dist/networking/models/encapsulation.mjs +132 -0
- package/dist/networking/models/handover.d.mts +116 -0
- package/dist/networking/models/handover.mjs +209 -0
- package/dist/networking/models/handshake.d.mts +90 -0
- package/dist/networking/models/handshake.mjs +122 -0
- package/dist/networking/models/index.d.mts +14 -0
- package/dist/networking/models/index.mjs +15 -0
- package/dist/networking/models/media.d.mts +82 -0
- package/dist/networking/models/media.mjs +188 -0
- package/dist/networking/models/satellite.d.mts +86 -0
- package/dist/networking/models/satellite.mjs +121 -0
- package/dist/networking/models/spectrum.d.mts +83 -0
- package/dist/networking/models/spectrum.mjs +235 -0
- package/dist/networking/models/switch.d.mts +80 -0
- package/dist/networking/models/switch.mjs +155 -0
- package/dist/networking/models/vlan.d.mts +62 -0
- package/dist/networking/models/vlan.mjs +100 -0
- package/dist/networking/models/vpn.d.mts +66 -0
- package/dist/networking/models/vpn.mjs +125 -0
- package/dist/networking/models/wifi-privacy.d.mts +82 -0
- package/dist/networking/models/wifi-privacy.mjs +130 -0
- package/dist/networking/models/wifi.d.mts +240 -0
- package/dist/networking/models/wifi.mjs +562 -0
- package/dist/networking/nat-traversal.d.mts +24 -0
- package/dist/networking/nat-traversal.mjs +38 -0
- package/dist/networking/protocols.d.mts +17 -0
- package/dist/networking/protocols.mjs +57 -0
- package/dist/networking/queue.d.mts +12 -0
- package/dist/networking/queue.mjs +18 -0
- package/dist/networking/route-learning.d.mts +23 -0
- package/dist/networking/route-learning.mjs +43 -0
- package/dist/networking/routing.d.mts +11 -0
- package/dist/networking/routing.mjs +25 -0
- package/dist/networking/signals.d.mts +26 -0
- package/dist/networking/signals.mjs +35 -0
- package/dist/networking/subnet.d.mts +49 -0
- package/dist/networking/subnet.mjs +89 -0
- package/dist/networking/tcp-reliability.d.mts +24 -0
- package/dist/networking/tcp-reliability.mjs +38 -0
- package/dist/networking/topology/contract.d.mts +123 -0
- package/dist/networking/topology/index.d.mts +4 -0
- package/dist/networking/topology/index.mjs +4 -0
- package/dist/networking/topology/presets.d.mts +7 -0
- package/dist/networking/topology/presets.mjs +302 -0
- package/dist/networking/topology/simulation.d.mts +8 -0
- package/dist/networking/topology/simulation.mjs +179 -0
- package/dist/networking/wireless.d.mts +11 -0
- package/dist/networking/wireless.mjs +17 -0
- package/dist/optics/imaging.d.mts +218 -0
- package/dist/optics/imaging.mjs +276 -0
- package/dist/optics/index.d.mts +7 -0
- package/dist/optics/index.mjs +8 -0
- package/dist/optics/laws.d.mts +70 -0
- package/dist/optics/laws.mjs +195 -0
- package/dist/optics/prism.d.mts +148 -0
- package/dist/optics/prism.mjs +197 -0
- package/dist/optics/refraction.d.mts +201 -0
- package/dist/optics/refraction.mjs +270 -0
- package/dist/optics/wave-optics.d.mts +171 -0
- package/dist/optics/wave-optics.mjs +259 -0
- package/dist/physics/ac/index.d.mts +183 -0
- package/dist/physics/ac/index.mjs +203 -0
- package/dist/physics/dynamics/forces.d.mts +98 -0
- package/dist/physics/dynamics/forces.mjs +153 -0
- package/dist/physics/dynamics/friction.d.mts +76 -0
- package/dist/physics/dynamics/friction.mjs +119 -0
- package/dist/physics/dynamics/system.d.mts +68 -0
- package/dist/physics/dynamics/system.mjs +125 -0
- package/dist/physics/electrostatics/index.d.mts +169 -0
- package/dist/physics/electrostatics/index.mjs +249 -0
- package/dist/physics/energy/centre-of-mass.d.mts +115 -0
- package/dist/physics/energy/centre-of-mass.mjs +180 -0
- package/dist/physics/energy/conservation.d.mts +81 -0
- package/dist/physics/energy/conservation.mjs +129 -0
- package/dist/physics/energy/power.d.mts +90 -0
- package/dist/physics/energy/power.mjs +167 -0
- package/dist/physics/energy/spring.d.mts +73 -0
- package/dist/physics/energy/spring.mjs +129 -0
- package/dist/physics/energy/store.d.mts +63 -0
- package/dist/physics/energy/store.mjs +108 -0
- package/dist/physics/energy/theorem.d.mts +65 -0
- package/dist/physics/energy/theorem.mjs +113 -0
- package/dist/physics/energy/work.d.mts +114 -0
- package/dist/physics/energy/work.mjs +219 -0
- package/dist/physics/gravitation/body.d.mts +85 -0
- package/dist/physics/gravitation/body.mjs +168 -0
- package/dist/physics/gravitation/kepler.d.mts +127 -0
- package/dist/physics/gravitation/kepler.mjs +216 -0
- package/dist/physics/gravitation/law.d.mts +117 -0
- package/dist/physics/gravitation/law.mjs +175 -0
- package/dist/physics/gravitation/orbit.d.mts +119 -0
- package/dist/physics/gravitation/orbit.mjs +202 -0
- package/dist/physics/gravitation/potential.d.mts +114 -0
- package/dist/physics/gravitation/potential.mjs +202 -0
- package/dist/physics/gravitation/variation.d.mts +97 -0
- package/dist/physics/gravitation/variation.mjs +175 -0
- package/dist/physics/index.d.mts +35 -0
- package/dist/physics/index.mjs +36 -0
- package/dist/physics/integral-sweep.d.mts +63 -0
- package/dist/physics/integral-sweep.mjs +126 -0
- package/dist/physics/kinematics/equations.d.mts +47 -0
- package/dist/physics/kinematics/equations.mjs +126 -0
- package/dist/physics/kinematics/free-fall.d.mts +53 -0
- package/dist/physics/kinematics/free-fall.mjs +71 -0
- package/dist/physics/kinematics/given.d.mts +16 -0
- package/dist/physics/kinematics/given.mjs +55 -0
- package/dist/physics/kinematics/roots.d.mts +41 -0
- package/dist/physics/kinematics/roots.mjs +72 -0
- package/dist/physics/kinematics/solve.d.mts +52 -0
- package/dist/physics/kinematics/solve.mjs +172 -0
- package/dist/physics/magnetism/biot-savart.d.mts +161 -0
- package/dist/physics/magnetism/biot-savart.mjs +353 -0
- package/dist/physics/magnetism/flux.d.mts +94 -0
- package/dist/physics/magnetism/flux.mjs +313 -0
- package/dist/physics/magnetism/geomagnetism.d.mts +151 -0
- package/dist/physics/magnetism/geomagnetism.mjs +184 -0
- package/dist/physics/magnetism/index.d.mts +12 -0
- package/dist/physics/magnetism/index.mjs +74 -0
- package/dist/physics/magnetism/lorentz.d.mts +28 -0
- package/dist/physics/magnetism/lorentz.mjs +88 -0
- package/dist/physics/magnetism/magnetic-force.d.mts +102 -0
- package/dist/physics/magnetism/magnetic-force.mjs +169 -0
- package/dist/physics/momentum/collide.d.mts +77 -0
- package/dist/physics/momentum/collide.mjs +115 -0
- package/dist/physics/momentum/impulse.d.mts +89 -0
- package/dist/physics/momentum/impulse.mjs +159 -0
- package/dist/physics/momentum/planar.d.mts +60 -0
- package/dist/physics/momentum/planar.mjs +110 -0
- package/dist/physics/projectile.d.mts +107 -0
- package/dist/physics/projectile.mjs +185 -0
- package/dist/physics/rotation/angular.d.mts +85 -0
- package/dist/physics/rotation/angular.mjs +130 -0
- package/dist/physics/rotation/circular.d.mts +79 -0
- package/dist/physics/rotation/circular.mjs +131 -0
- package/dist/physics/rotation/inertia.d.mts +164 -0
- package/dist/physics/rotation/inertia.mjs +332 -0
- package/dist/physics/rotation/torque.d.mts +65 -0
- package/dist/physics/rotation/torque.mjs +134 -0
- package/dist/physics/vectors.d.mts +64 -0
- package/dist/physics/vectors.mjs +114 -0
- package/dist/primitives/Dot.d.mts +2 -15
- package/dist/primitives/Dot.mjs +6 -4
- package/dist/primitives/Grid.d.mts +33 -17
- package/dist/primitives/Grid.mjs +89 -17
- package/dist/primitives/Label.d.mts +1 -14
- package/dist/primitives/Label.mjs +3 -2
- package/dist/primitives/Lines.d.mts +4 -32
- package/dist/primitives/Lines.mjs +10 -8
- package/dist/primitives/Shapes.d.mts +5 -43
- package/dist/primitives/Shapes.mjs +12 -10
- package/dist/primitives/index.d.mts +2 -2
- package/dist/primitives/index.mjs +2 -2
- package/dist/primitives/props.mjs +31 -0
- package/dist/quantity/index.d.mts +119 -0
- package/dist/quantity/index.mjs +417 -0
- package/dist/relation/derive.d.mts +74 -0
- package/dist/relation/derive.mjs +73 -0
- package/dist/relation/index.d.mts +56 -0
- package/dist/relation/index.mjs +234 -0
- package/dist/scene/Scene.d.mts +6 -1
- package/dist/scene/Scene.mjs +15 -40
- package/dist/scene/evaluators.mjs +1 -1
- package/dist/sim/thermal.mjs +45 -20
- package/dist/thermo/calorimetry.d.mts +207 -0
- package/dist/thermo/calorimetry.mjs +345 -0
- package/dist/thermo/cycles.d.mts +142 -0
- package/dist/thermo/cycles.mjs +256 -0
- package/dist/thermo/entropy.d.mts +87 -0
- package/dist/thermo/entropy.mjs +140 -0
- package/dist/thermo/expansion.d.mts +130 -0
- package/dist/thermo/expansion.mjs +178 -0
- package/dist/thermo/gas.d.mts +197 -0
- package/dist/thermo/gas.mjs +279 -0
- package/dist/thermo/heat-transfer.d.mts +164 -0
- package/dist/thermo/heat-transfer.mjs +217 -0
- package/dist/thermo/index.d.mts +9 -82
- package/dist/thermo/index.mjs +9 -132
- package/dist/thermo/processes.d.mts +95 -0
- package/dist/thermo/processes.mjs +154 -0
- package/dist/thermo/temperature.d.mts +174 -0
- package/dist/thermo/temperature.mjs +236 -0
- package/dist/view/Stage.mjs +4 -11
- package/dist/waves/damping.d.mts +224 -0
- package/dist/waves/damping.mjs +317 -0
- package/dist/waves/doppler.d.mts +72 -0
- package/dist/waves/doppler.mjs +125 -0
- package/dist/waves/index.d.mts +10 -0
- package/dist/waves/index.mjs +10 -0
- package/dist/waves/pendulum.d.mts +130 -0
- package/dist/waves/pendulum.mjs +211 -0
- package/dist/waves/pitch.d.mts +125 -0
- package/dist/waves/pitch.mjs +158 -0
- package/dist/waves/shm.d.mts +170 -0
- package/dist/waves/shm.mjs +279 -0
- package/dist/waves/springs.d.mts +103 -0
- package/dist/waves/springs.mjs +176 -0
- package/dist/waves/superposition.d.mts +124 -0
- package/dist/waves/superposition.mjs +196 -0
- package/dist/waves/wave.d.mts +154 -0
- package/dist/waves/wave.mjs +178 -0
- package/package.json +103 -23
- package/styles.css +125 -8
- package/dist/assets/kit/index.mjs +0 -4
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { evaluate } from "../math/ast.mjs";
|
|
2
|
+
import { parse } from "../math/parse.mjs";
|
|
3
|
+
import { isolate, occurrences } from "../math/rearrange.mjs";
|
|
4
|
+
import { toLatex } from "../math/latex.mjs";
|
|
5
|
+
import { calc, texSigFigs } from "../core/worked.mjs";
|
|
6
|
+
import { Quantity, describeDimension, divDim, isDimensionless, mulDim, parseUnit, powDim, qty, sameDim } from "../quantity/index.mjs";
|
|
7
|
+
import { checkDerivation, deriveValue, explain } from "./derive.mjs";
|
|
8
|
+
|
|
9
|
+
//#region src/relation/index.ts
|
|
10
|
+
/**
|
|
11
|
+
* relation, a declared equation that can be solved for ANY of its variables and can show its
|
|
12
|
+
* working.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const moles = relation('n = c * V', { n: 'mol', c: 'mol/dm3', V: 'dm3' });
|
|
16
|
+
* moles.solve({ c: 0.100, V: 0.02450 }); // n = 2.45e-3 mol
|
|
17
|
+
* moles.solve({ c: 0.100, V: qty(24.50, 'cm3') }); // the same, titre straight off the burette
|
|
18
|
+
* moles.solve({ n: 2.45e-3, V: 0.02450 }); // c = 0.100 mol/dm3
|
|
19
|
+
* moles.steps({ c: 0.100, V: 0.02450 }); // the substitution, line by line
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ONE DECLARATION, THREE READERS. A lab's model recomputes from it when a slider moves, a lesson's
|
|
23
|
+
* derivation is generated from it, and a checker verifies the lesson against it. Today those are
|
|
24
|
+
* three hand-written copies of the same equation and nothing makes them agree.
|
|
25
|
+
*
|
|
26
|
+
* Two properties do the real work:
|
|
27
|
+
*
|
|
28
|
+
* **Units are checked when the relation is DECLARED, not when it is used.** The check is arithmetic
|
|
29
|
+
* on the dimension vector, never on the spelling, so `V: 'cm3'` and `V: 'dm3'` both balance against
|
|
30
|
+
* `n: 'mol'` and `c: 'mol/dm3'`: they are the same dimension and the factors sort themselves out.
|
|
31
|
+
* What does not balance is refused before a single lesson can be written against it:
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* relation('n = c + V', { n: 'mol', c: 'mol/dm3', V: 'dm3' });
|
|
35
|
+
* // Error: cannot add mol/m^3 and m^3
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* A wrong relation is worse than no relation, because lessons, labs and the checker would then all
|
|
39
|
+
* agree with each other and all be wrong.
|
|
40
|
+
*
|
|
41
|
+
* **Every number in the working came from the caller.** A derivation generated by `.steps()` cannot
|
|
42
|
+
* divide by a mass that is not in the question, because the only values it can substitute are the
|
|
43
|
+
* ones passed in. That is the class of error that shipped into a draft lesson and was caught by
|
|
44
|
+
* reading rather than by any gate.
|
|
45
|
+
*
|
|
46
|
+
* SOLVING IS ISOLATION, NOT ALGEBRA. The unknown must appear exactly once, and the solver peels
|
|
47
|
+
* operations off it one at a time, applying each inverse to the other side. That covers the whole
|
|
48
|
+
* A-level relation table. The moment a relation needs more than that, the scope is wrong and the
|
|
49
|
+
* answer is to declare a second relation. `x` appearing twice is refused with a message that says
|
|
50
|
+
* so, rather than being approximated numerically, because a silent numeric answer to a question the
|
|
51
|
+
* kernel cannot really answer is worse than a refusal.
|
|
52
|
+
*
|
|
53
|
+
* Pure: no React, no pixels.
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* The dimension of an expression, given a dimension for each variable.
|
|
57
|
+
*
|
|
58
|
+
* `log`, `log10`, `ln` are the one deliberate deviation from strict dimensional analysis. The
|
|
59
|
+
* logarithm of a dimensioned quantity is not defined, yet pH is *defined* as −log[Hâº] with [Hâº] in
|
|
60
|
+
* mol dmâ»Â³. Chemistry means "the log of the numeric value in the stated unit", so that is what this
|
|
61
|
+
* implements: the argument may carry a dimension, the result is dimensionless. Refusing it would
|
|
62
|
+
* block every acid-base relation in the syllabus; silently pretending the argument is dimensionless
|
|
63
|
+
* would lose the unit on the way in. Declaring the convention is the honest third option.
|
|
64
|
+
*/
|
|
65
|
+
function dimOf(node, dims, where) {
|
|
66
|
+
switch (node.type) {
|
|
67
|
+
case "num": return {};
|
|
68
|
+
case "var": {
|
|
69
|
+
const d = dims[node.name];
|
|
70
|
+
if (!d) throw new Error(`${where}: no unit declared for "${node.name}"`);
|
|
71
|
+
return d;
|
|
72
|
+
}
|
|
73
|
+
case "neg": return dimOf(node.arg, dims, where);
|
|
74
|
+
case "binary": {
|
|
75
|
+
const a = dimOf(node.left, dims, where);
|
|
76
|
+
const b = dimOf(node.right, dims, where);
|
|
77
|
+
switch (node.op) {
|
|
78
|
+
case "+":
|
|
79
|
+
case "-":
|
|
80
|
+
if (!sameDim(a, b)) throw new Error(`${where}: cannot ${node.op === "+" ? "add" : "subtract"} ${describeDimension(a)} and ${describeDimension(b)}`);
|
|
81
|
+
return a;
|
|
82
|
+
case "*": return mulDim(a, b);
|
|
83
|
+
case "/": return divDim(a, b);
|
|
84
|
+
case "%": return a;
|
|
85
|
+
case "^":
|
|
86
|
+
if (!isDimensionless(b)) throw new Error(`${where}: an exponent must be dimensionless, got ${describeDimension(b)}`);
|
|
87
|
+
if (node.right.type !== "num") {
|
|
88
|
+
if (isDimensionless(a)) return {};
|
|
89
|
+
throw new Error(`${where}: "${toLatex(node.left)}" is raised to a variable power, so its dimension is not fixed. Declare it dimensionless.`);
|
|
90
|
+
}
|
|
91
|
+
return powDim(a, node.right.value);
|
|
92
|
+
default: return a;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
case "call": {
|
|
96
|
+
const arg = node.args[0] ? dimOf(node.args[0], dims, where) : {};
|
|
97
|
+
if (node.fn === "log" || node.fn === "log10" || node.fn === "log2" || node.fn === "ln") return {};
|
|
98
|
+
if (node.fn === "sqrt") return powDim(arg, .5);
|
|
99
|
+
if (node.fn === "cbrt") return powDim(arg, 1 / 3);
|
|
100
|
+
if (node.fn === "abs") return arg;
|
|
101
|
+
if (!isDimensionless(arg)) throw new Error(`${where}: ${node.fn}() needs a dimensionless argument, got ${describeDimension(arg)}`);
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
default: return {};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var Relation = class {
|
|
108
|
+
equation;
|
|
109
|
+
title;
|
|
110
|
+
units;
|
|
111
|
+
variables;
|
|
112
|
+
left;
|
|
113
|
+
right;
|
|
114
|
+
dims;
|
|
115
|
+
isolated = /* @__PURE__ */ new Map();
|
|
116
|
+
constructor(spec) {
|
|
117
|
+
const halves = spec.equation.split("=");
|
|
118
|
+
if (halves.length !== 2) throw new Error(`Relation "${spec.equation}" must have exactly one "=".`);
|
|
119
|
+
this.equation = spec.equation;
|
|
120
|
+
this.title = spec.title ?? spec.equation;
|
|
121
|
+
this.units = spec.units;
|
|
122
|
+
this.left = parse(halves[0]);
|
|
123
|
+
this.right = parse(halves[1]);
|
|
124
|
+
this.dims = {};
|
|
125
|
+
for (const [name, unit] of Object.entries(spec.units)) this.dims[name] = parseUnit(unit).dim;
|
|
126
|
+
const seen = /* @__PURE__ */ new Set();
|
|
127
|
+
const walk = (node) => {
|
|
128
|
+
if (node.type === "var") seen.add(node.name);
|
|
129
|
+
else if (node.type === "neg") walk(node.arg);
|
|
130
|
+
else if (node.type === "binary") {
|
|
131
|
+
walk(node.left);
|
|
132
|
+
walk(node.right);
|
|
133
|
+
} else if (node.type === "call") node.args.forEach(walk);
|
|
134
|
+
};
|
|
135
|
+
walk(this.left);
|
|
136
|
+
walk(this.right);
|
|
137
|
+
for (const name of [
|
|
138
|
+
"e",
|
|
139
|
+
"pi",
|
|
140
|
+
"tau"
|
|
141
|
+
]) seen.delete(name);
|
|
142
|
+
this.variables = [...seen];
|
|
143
|
+
for (const name of this.variables) if (!(name in this.dims)) throw new Error(`Relation "${spec.equation}": no unit declared for "${name}".`);
|
|
144
|
+
const dl = dimOf(this.left, this.dims, `Relation "${spec.equation}"`);
|
|
145
|
+
const dr = dimOf(this.right, this.dims, `Relation "${spec.equation}"`);
|
|
146
|
+
if (!sameDim(dl, dr)) throw new Error(`Relation "${spec.equation}" does not balance: left is ${describeDimension(dl)}, right is ${describeDimension(dr)}.`);
|
|
147
|
+
}
|
|
148
|
+
/** The declared unit of a variable. */
|
|
149
|
+
unitOf(name) {
|
|
150
|
+
const unit = this.units[name];
|
|
151
|
+
if (!unit) throw new Error(`"${name}" is not a variable of ${this.equation}`);
|
|
152
|
+
return unit;
|
|
153
|
+
}
|
|
154
|
+
/** Numeric values in declared units, converting any Quantity that was passed in. */
|
|
155
|
+
normalise(given) {
|
|
156
|
+
const scope = {};
|
|
157
|
+
for (const [name, value] of Object.entries(given)) {
|
|
158
|
+
if (!this.variables.includes(name)) throw new Error(`"${name}" is not a variable of ${this.equation}`);
|
|
159
|
+
scope[name] = value instanceof Quantity ? value.in(this.unitOf(name)) : value;
|
|
160
|
+
}
|
|
161
|
+
return scope;
|
|
162
|
+
}
|
|
163
|
+
/** The one variable of the relation that `given` does not supply. */
|
|
164
|
+
unknownOf(given) {
|
|
165
|
+
const missing = this.variables.filter((v) => !(v in given));
|
|
166
|
+
if (missing.length === 0) throw new Error(`Nothing to solve for in ${this.equation}: every variable was given.`);
|
|
167
|
+
if (missing.length > 1) throw new Error(`${this.equation} has ${missing.length} unknowns (${missing.join(", ")}); exactly one must be left out.`);
|
|
168
|
+
return missing[0];
|
|
169
|
+
}
|
|
170
|
+
expressionFor(target) {
|
|
171
|
+
const cached = this.isolated.get(target);
|
|
172
|
+
if (cached) return cached;
|
|
173
|
+
const count = occurrences(this.left, target) + occurrences(this.right, target);
|
|
174
|
+
if (count === 0) throw new Error(`"${target}" does not appear in ${this.equation}`);
|
|
175
|
+
if (count > 1) throw new Error(`Cannot solve ${this.equation} for "${target}": it appears ${count} times, and this kernel isolates rather than does algebra. Declare a second relation that already has "${target}" alone.`);
|
|
176
|
+
const expr = isolate(this.left, this.right, target);
|
|
177
|
+
this.isolated.set(target, expr);
|
|
178
|
+
return expr;
|
|
179
|
+
}
|
|
180
|
+
/** Solve for whichever variable was left out of `given`. */
|
|
181
|
+
solve(given) {
|
|
182
|
+
const target = this.unknownOf(given);
|
|
183
|
+
return this.solveFor(target, given);
|
|
184
|
+
}
|
|
185
|
+
/** Solve for a named variable, ignoring any value supplied for it. */
|
|
186
|
+
solveFor(target, given) {
|
|
187
|
+
const scope = this.normalise(given);
|
|
188
|
+
delete scope[target];
|
|
189
|
+
const value = evaluate(this.expressionFor(target), scope);
|
|
190
|
+
if (!Number.isFinite(value)) throw new Error(`Solving ${this.equation} for "${target}" gave ${value}. Check that every other variable was supplied and is in range.`);
|
|
191
|
+
const unit = this.unitOf(target);
|
|
192
|
+
return {
|
|
193
|
+
name: target,
|
|
194
|
+
value,
|
|
195
|
+
unit,
|
|
196
|
+
quantity: qty(value, unit)
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The worked substitution, for a lesson's derivation.
|
|
201
|
+
*
|
|
202
|
+
* Three lines: the relation, the relation rearranged for the unknown, and the substitution with
|
|
203
|
+
* the answer. Every number shown came from `given`, which is the property that makes a generated
|
|
204
|
+
* derivation trustworthy in a way a hand-written one is not.
|
|
205
|
+
*/
|
|
206
|
+
steps(given, sf = 3) {
|
|
207
|
+
const target = this.unknownOf(given);
|
|
208
|
+
const scope = this.normalise(given);
|
|
209
|
+
const solved = this.solveFor(target, given);
|
|
210
|
+
const expr = this.expressionFor(target);
|
|
211
|
+
const w = calc().step(texOf(this.left, this.right), this.title);
|
|
212
|
+
const rearranged = `${target} = ${toLatex(expr)}`;
|
|
213
|
+
if (rearranged !== texOf(this.left, this.right)) w.step(rearranged, `rearranged for ${target}`);
|
|
214
|
+
const subs = this.variables.filter((v) => v !== target).map((v) => `${v} = ${fmt(scope[v], sf)}\\ \\mathrm{${this.unitOf(v)}}`).join(",\\quad ");
|
|
215
|
+
if (subs) w.step(subs, "the values from the question");
|
|
216
|
+
w.step(`${target} = ${fmt(solved.value, sf)}\\ \\mathrm{${solved.unit}}`, `to ${sf} significant figures`);
|
|
217
|
+
return w.done(solved.value);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const texOf = (left, right) => `${toLatex(left)} = ${toLatex(right)}`;
|
|
221
|
+
/** Significant figures, without exponent notation for ordinary magnitudes. Shared with every other
|
|
222
|
+
* kernel that shows working, so two derivations of one quantity cannot round differently. */
|
|
223
|
+
const fmt = (value, sf) => texSigFigs(value, sf);
|
|
224
|
+
const relation = (equation, units, title) => new Relation(title === void 0 ? {
|
|
225
|
+
equation,
|
|
226
|
+
units
|
|
227
|
+
} : {
|
|
228
|
+
equation,
|
|
229
|
+
units,
|
|
230
|
+
title
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
//#endregion
|
|
234
|
+
export { Relation, checkDerivation, deriveValue, explain, relation };
|
package/dist/scene/Scene.d.mts
CHANGED
|
@@ -15,6 +15,9 @@ interface SceneProps {
|
|
|
15
15
|
/** Author opt-in: start a sim running immediately (ambient motion) instead of
|
|
16
16
|
* the default "press Play". A SceneDoc with no `meta.sims` is always static. */
|
|
17
17
|
autoPlay?: boolean;
|
|
18
|
+
/** Optional controlled playback for hosts that provide their own transport. */
|
|
19
|
+
playing?: boolean;
|
|
20
|
+
onPlayingChange?: (playing: boolean) => void;
|
|
18
21
|
}
|
|
19
22
|
declare function Scene({
|
|
20
23
|
doc,
|
|
@@ -25,7 +28,9 @@ declare function Scene({
|
|
|
25
28
|
height,
|
|
26
29
|
view,
|
|
27
30
|
ariaLabel,
|
|
28
|
-
autoPlay
|
|
31
|
+
autoPlay,
|
|
32
|
+
playing: playingProp,
|
|
33
|
+
onPlayingChange
|
|
29
34
|
}: SceneProps): ReactNode;
|
|
30
35
|
//#endregion
|
|
31
36
|
export { Scene, SceneProps };
|
package/dist/scene/Scene.mjs
CHANGED
|
@@ -45,7 +45,7 @@ function autoView(resolved) {
|
|
|
45
45
|
yMax: Math.max(...ys) + pad
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
-
function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes = true, height, view, ariaLabel = "Interactive figure", autoPlay = false }) {
|
|
48
|
+
function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes = true, height, view, ariaLabel = "Interactive figure", autoPlay = false, playing: playingProp, onPlayingChange }) {
|
|
49
49
|
const [overlay, setOverlay] = useState(/* @__PURE__ */ new Map());
|
|
50
50
|
const prevRef = useRef(void 0);
|
|
51
51
|
const sims = doc.meta?.sims;
|
|
@@ -55,14 +55,22 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
|
|
|
55
55
|
const [frame, setFrame] = useState(0);
|
|
56
56
|
const { ref: rootRef, inView } = useInView();
|
|
57
57
|
const [done, setDone] = useState(false);
|
|
58
|
-
const [
|
|
58
|
+
const [internalPlaying, setInternalPlaying] = useState(autoPlay);
|
|
59
|
+
const playing = playingProp ?? internalPlaying;
|
|
60
|
+
const setPlaying = (next) => {
|
|
61
|
+
if (playingProp === void 0) setInternalPlaying(next);
|
|
62
|
+
onPlayingChange?.(next);
|
|
63
|
+
};
|
|
59
64
|
useFrameLoop((f) => {
|
|
60
65
|
if (!sims?.length || !simStates.current) return;
|
|
61
66
|
const next = stepSims(sims, simStates.current, Math.min(.05, f.dtMs / 1e3));
|
|
62
67
|
simStates.current = next.states;
|
|
63
68
|
simScalars.current = next.scalars;
|
|
64
69
|
setFrame((n) => (n + 1) % 1e6);
|
|
65
|
-
if (next.done)
|
|
70
|
+
if (next.done) {
|
|
71
|
+
setDone(true);
|
|
72
|
+
setPlaying(false);
|
|
73
|
+
}
|
|
66
74
|
}, { running: !!sims?.length && inView && playing && !done });
|
|
67
75
|
const merged = useMemo(() => {
|
|
68
76
|
const scalars = simScalars.current;
|
|
@@ -95,7 +103,7 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
|
|
|
95
103
|
const hasSims = !!sims?.length;
|
|
96
104
|
return /* @__PURE__ */ jsxs("div", {
|
|
97
105
|
ref: rootRef,
|
|
98
|
-
|
|
106
|
+
className: "stage-scene",
|
|
99
107
|
children: [
|
|
100
108
|
/* @__PURE__ */ jsxs(Stage, {
|
|
101
109
|
view: v,
|
|
@@ -114,30 +122,9 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
|
|
|
114
122
|
type: "button",
|
|
115
123
|
onClick: () => setPlaying(true),
|
|
116
124
|
"aria-label": "Play simulation",
|
|
117
|
-
|
|
118
|
-
position: "absolute",
|
|
119
|
-
inset: 0,
|
|
120
|
-
display: "flex",
|
|
121
|
-
alignItems: "center",
|
|
122
|
-
justifyContent: "center",
|
|
123
|
-
background: "color-mix(in oklab, var(--stage-bg) 38%, transparent)",
|
|
124
|
-
border: "none",
|
|
125
|
-
borderRadius: 12,
|
|
126
|
-
cursor: "pointer"
|
|
127
|
-
},
|
|
125
|
+
className: "stage-scene__play-overlay",
|
|
128
126
|
children: /* @__PURE__ */ jsx("span", {
|
|
129
|
-
|
|
130
|
-
display: "inline-flex",
|
|
131
|
-
alignItems: "center",
|
|
132
|
-
gap: 8,
|
|
133
|
-
padding: "11px 26px",
|
|
134
|
-
borderRadius: 999,
|
|
135
|
-
background: "var(--stage-accent)",
|
|
136
|
-
color: "#fff",
|
|
137
|
-
fontWeight: 800,
|
|
138
|
-
fontSize: 16,
|
|
139
|
-
boxShadow: "0 6px 18px rgba(0,0,0,.28)"
|
|
140
|
-
},
|
|
127
|
+
className: "stage-scene__play-label",
|
|
141
128
|
children: "▶ Play"
|
|
142
129
|
})
|
|
143
130
|
}),
|
|
@@ -145,19 +132,7 @@ function Scene({ doc, onChange, interactive = true, showGrid = true, showAxes =
|
|
|
145
132
|
type: "button",
|
|
146
133
|
onClick: () => setPlaying(false),
|
|
147
134
|
"aria-label": "Pause simulation",
|
|
148
|
-
|
|
149
|
-
position: "absolute",
|
|
150
|
-
top: 10,
|
|
151
|
-
right: 10,
|
|
152
|
-
width: 34,
|
|
153
|
-
height: 34,
|
|
154
|
-
borderRadius: 999,
|
|
155
|
-
border: "1px solid var(--stage-grid)",
|
|
156
|
-
background: "color-mix(in oklab, var(--stage-bg) 80%, transparent)",
|
|
157
|
-
color: "var(--stage-fg)",
|
|
158
|
-
cursor: "pointer",
|
|
159
|
-
fontSize: 13
|
|
160
|
-
},
|
|
135
|
+
className: "stage-scene__pause",
|
|
161
136
|
children: "⏸"
|
|
162
137
|
})
|
|
163
138
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { vec } from "../core/vec.mjs";
|
|
2
2
|
import { isCircleVal, isLineVal, isVec2, numOf } from "./types.mjs";
|
|
3
3
|
import { getAsset } from "./assets.mjs";
|
|
4
|
-
import { compileExpr } from "../math/
|
|
4
|
+
import { compileExpr } from "../math/expr.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/scene/evaluators.ts
|
|
7
7
|
/**
|
package/dist/sim/thermal.mjs
CHANGED
|
@@ -1,36 +1,61 @@
|
|
|
1
|
+
import { MATERIALS } from "../thermo/calorimetry.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/sim/thermal.ts
|
|
4
|
+
/**
|
|
5
|
+
* `thermal`, calorimetry: pour heat into a substance and watch its temperature
|
|
6
|
+
* and phase evolve. The one core behind the whole heat-&-temperature module.
|
|
7
|
+
*
|
|
8
|
+
* A substance has a specific heat in EACH phase (solid/liquid/gas) and a latent
|
|
9
|
+
* heat for each transition (fusion at the melting point, vaporisation at the
|
|
10
|
+
* boiling point). Pouring in power P for time dt adds q = P·dt of energy; that
|
|
11
|
+
* energy climbs an ENERGY LADDER of segments:
|
|
12
|
+
*
|
|
13
|
+
* • heating a phase → temperature RISES, q = m·c·Δθ (a sloped run, slope 1/mc)
|
|
14
|
+
* • a phase change → temperature is FLAT, q = m·L (a horizontal plateau)
|
|
15
|
+
*
|
|
16
|
+
* So plotting temperature against heat-added gives the classic heating curve:
|
|
17
|
+
* sloped rises separated by flat plateaus, and the WIDTH of each segment is the
|
|
18
|
+
* heat that part needs. The marker just walks along that fixed curve as energy
|
|
19
|
+
* accumulates. Pure + deterministic (energy is linear in time at constant power),
|
|
20
|
+
* so it replays as video and unit-tests with no DOM. `powerW` lives in state, so a
|
|
21
|
+
* slider can change the heating rate live; flip it negative to cool back down.
|
|
22
|
+
*/
|
|
2
23
|
const clamp01 = (v) => Math.max(0, Math.min(1, v));
|
|
24
|
+
/**
|
|
25
|
+
* Build a `Substance` from the SI material data in `thermo/calorimetry`.
|
|
26
|
+
*
|
|
27
|
+
* This sim works in GRAMS and J/(g·K) because its whole surface does, and
|
|
28
|
+
* changing that would break every lab driving it. The kernel works in kilograms
|
|
29
|
+
* because the rest of physics does. Rather than keep two copies of water's
|
|
30
|
+
* numbers and let them drift, the conversion happens once, here, and the
|
|
31
|
+
* factor of 1000 appears exactly this many times in the package.
|
|
32
|
+
*/
|
|
33
|
+
const fromMaterial = (material, names) => ({
|
|
34
|
+
name: material.name,
|
|
35
|
+
...names,
|
|
36
|
+
cSolid: material.cSolid / 1e3,
|
|
37
|
+
cLiquid: material.cLiquid / 1e3,
|
|
38
|
+
cGas: material.cGas / 1e3,
|
|
39
|
+
lFusion: material.latentFusion / 1e3,
|
|
40
|
+
lVapor: material.latentVaporisation / 1e3,
|
|
41
|
+
tMelt: material.meltingPointC,
|
|
42
|
+
tBoil: material.boilingPointC
|
|
43
|
+
});
|
|
3
44
|
/** Real water, the canonical example (note: latent vaporisation ≫ everything,
|
|
4
45
|
* so the boiling plateau dwarfs the rest, which is exactly the lesson). */
|
|
5
|
-
const WATER = {
|
|
6
|
-
name: "Water",
|
|
46
|
+
const WATER = fromMaterial(MATERIALS.water, {
|
|
7
47
|
solidName: "ice",
|
|
8
48
|
liquidName: "water",
|
|
9
49
|
gasName: "steam",
|
|
10
|
-
cSolid: 2.09,
|
|
11
|
-
cLiquid: 4.18,
|
|
12
|
-
cGas: 2.01,
|
|
13
|
-
lFusion: 334,
|
|
14
|
-
lVapor: 2260,
|
|
15
|
-
tMelt: 0,
|
|
16
|
-
tBoil: 100,
|
|
17
50
|
color: "var(--stage-accent, #3b82f6)"
|
|
18
|
-
};
|
|
51
|
+
});
|
|
19
52
|
/** Ethanol, lower boiling point, smaller latent heats; a contrasting curve. */
|
|
20
|
-
const ETHANOL = {
|
|
21
|
-
name: "Ethanol",
|
|
53
|
+
const ETHANOL = fromMaterial(MATERIALS.ethanol, {
|
|
22
54
|
solidName: "solid",
|
|
23
55
|
liquidName: "ethanol",
|
|
24
56
|
gasName: "vapour",
|
|
25
|
-
cSolid: 1.2,
|
|
26
|
-
cLiquid: 2.44,
|
|
27
|
-
cGas: 1.42,
|
|
28
|
-
lFusion: 108,
|
|
29
|
-
lVapor: 855,
|
|
30
|
-
tMelt: -114,
|
|
31
|
-
tBoil: 78,
|
|
32
57
|
color: "var(--stage-accent-2, #8b5cf6)"
|
|
33
|
-
};
|
|
58
|
+
});
|
|
34
59
|
/** Build the energy ladder from `tStart` up to `tMax`, including only the phases
|
|
35
60
|
* reachable heating upward (a liquid start skips the solid+melting runs). */
|
|
36
61
|
function buildSegs(sub, mass, tStart, tMax) {
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
//#region src/thermo/calorimetry.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Calorimetry: heat in, temperature and phase out.
|
|
4
|
+
*
|
|
5
|
+
* THE MODEL. Energy delivered to a body goes into exactly one of two things at
|
|
6
|
+
* any moment, and which one depends only on whether it is sitting at a
|
|
7
|
+
* transition temperature:
|
|
8
|
+
*
|
|
9
|
+
* warming a phase q = m·c·Δθ temperature rises
|
|
10
|
+
* changing phase q = m·L temperature is FLAT
|
|
11
|
+
*
|
|
12
|
+
* Everything here follows from putting those two in a row. A body's ENTHALPY
|
|
13
|
+
* LADDER is the alternating sequence of sloped runs and flat plateaus from a
|
|
14
|
+
* cold datum up to any temperature, and once you have the ladder you can ask
|
|
15
|
+
* either direction: how much heat to reach this temperature, or what
|
|
16
|
+
* temperature this much heat reaches.
|
|
17
|
+
*
|
|
18
|
+
* THE METHOD, and why it is exact. Total enthalpy H(θ) is piecewise LINEAR in
|
|
19
|
+
* temperature, with an upward jump of m·L at each transition. So the mixture
|
|
20
|
+
* problem, find the θ at which the bodies' enthalpies sum to what they started
|
|
21
|
+
* with, is the inversion of a piecewise-linear monotone function. That is
|
|
22
|
+
* solved by walking the brackets and doing one division, with no iteration and
|
|
23
|
+
* no tolerance. Bisection would also work and would be worse: it converges
|
|
24
|
+
* slowly exactly where the interesting answers are, at the plateaus, because
|
|
25
|
+
* there the function is flat in the inverse direction.
|
|
26
|
+
*
|
|
27
|
+
* This matters because it is what lets the solver answer the question a student
|
|
28
|
+
* actually cannot do by hand: mix ice below freezing with water above it, and
|
|
29
|
+
* the outcome is a case analysis (all ice, ice and water at 0 °C, all water)
|
|
30
|
+
* that the algebra hides. Here it falls out of which bracket the answer lands
|
|
31
|
+
* in.
|
|
32
|
+
*
|
|
33
|
+
* UNITS: SI. Mass in kg, specific heat capacity in J/(kg·K), latent heat in
|
|
34
|
+
* J/kg, energy in joules. Temperatures are in DEGREES CELSIUS, because every
|
|
35
|
+
* transition point a lesson names is quoted that way and the differences that
|
|
36
|
+
* appear in m·c·Δθ are identical in either scale.
|
|
37
|
+
*
|
|
38
|
+
* SCOPE. One-dimensional in temperature: no pressure dependence, so the boiling
|
|
39
|
+
* point is a constant rather than a function of the ambient pressure, and
|
|
40
|
+
* sublimation is not modelled. Specific heats are constant within a phase.
|
|
41
|
+
*/
|
|
42
|
+
type PhaseName = 'solid' | 'liquid' | 'gas';
|
|
43
|
+
/**
|
|
44
|
+
* A material, as the four numbers that fix its enthalpy ladder plus the three
|
|
45
|
+
* specific heats.
|
|
46
|
+
*
|
|
47
|
+
* `latentFusion` and `latentVaporisation` are per kilogram, so the plateau
|
|
48
|
+
* widths scale with mass exactly as the sloped runs do.
|
|
49
|
+
*/
|
|
50
|
+
interface Material {
|
|
51
|
+
name: string;
|
|
52
|
+
/** Specific heat capacity in each phase, J/(kg·K). */
|
|
53
|
+
cSolid: number;
|
|
54
|
+
cLiquid: number;
|
|
55
|
+
cGas: number;
|
|
56
|
+
/** Latent heat of fusion, J/kg. */
|
|
57
|
+
latentFusion: number;
|
|
58
|
+
/** Latent heat of vaporisation, J/kg. */
|
|
59
|
+
latentVaporisation: number;
|
|
60
|
+
/** Melting point, °C. */
|
|
61
|
+
meltingPointC: number;
|
|
62
|
+
/** Boiling point at standard pressure, °C. */
|
|
63
|
+
boilingPointC: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Materials that appear in the standard problems.
|
|
67
|
+
*
|
|
68
|
+
* Water's numbers are worth reading as a set: the latent heat of vaporisation
|
|
69
|
+
* is 2.26 MJ/kg against 4.18 kJ/(kg·K) for warming, so boiling a kilogram away
|
|
70
|
+
* costs the same as heating it through 540 °C. That ratio is the whole reason
|
|
71
|
+
* sweating works and the reason a steam burn is worse than a boiling-water
|
|
72
|
+
* burn, and it is visible in the table before any lab draws it.
|
|
73
|
+
*/
|
|
74
|
+
declare const MATERIALS: Record<string, Material>;
|
|
75
|
+
/**
|
|
76
|
+
* Heat to warm a mass through a temperature change: q = m·c·Δθ.
|
|
77
|
+
*
|
|
78
|
+
* Δθ is SIGNED, so cooling returns a negative number rather than an absolute
|
|
79
|
+
* one. That is what makes the mixture bookkeeping add up to zero instead of
|
|
80
|
+
* needing a hand-placed minus sign on whichever body happened to be hotter.
|
|
81
|
+
*/
|
|
82
|
+
declare function heatToWarmJ(massKg: number, specificHeat: number, deltaT: number): number;
|
|
83
|
+
/** Heat to change the state of a mass at its transition temperature: q = m·L. */
|
|
84
|
+
declare function heatToChangeStateJ(massKg: number, latentHeat: number): number;
|
|
85
|
+
/** The inverse of q = m·c·Δθ, solved for the temperature change. */
|
|
86
|
+
declare function temperatureRiseK(heatJ: number, massKg: number, specificHeat: number): number;
|
|
87
|
+
/** The inverse of q = m·c·Δθ, solved for the specific heat capacity. */
|
|
88
|
+
declare function specificHeatFromJ(heatJ: number, massKg: number, deltaT: number): number;
|
|
89
|
+
/**
|
|
90
|
+
* The water equivalent of a calorimeter, kg: the mass of water with the same
|
|
91
|
+
* heat capacity.
|
|
92
|
+
*
|
|
93
|
+
* Lets a calorimeter be folded into a mixture as if it were more water, which
|
|
94
|
+
* is exactly how the textbook method handles it.
|
|
95
|
+
*/
|
|
96
|
+
declare function waterEquivalentKg(massKg: number, specificHeat: number): number;
|
|
97
|
+
/**
|
|
98
|
+
* The enthalpy of a mass of material at temperature θ, J, measured from a datum
|
|
99
|
+
* of absolute zero with the material solid.
|
|
100
|
+
*
|
|
101
|
+
* The datum is arbitrary and cancels out of every difference, which is the only
|
|
102
|
+
* reason it is allowed to be as unphysical as "solid water at −273 °C". What
|
|
103
|
+
* matters is that the SAME datum is used for every body in a mixture.
|
|
104
|
+
*
|
|
105
|
+
* At a transition temperature this returns the value approached from BELOW, so
|
|
106
|
+
* `enthalpyJ(m, water, 0)` is solid ice at 0 °C with none of it melted. The
|
|
107
|
+
* jump of m·L lives between this value and `enthalpyJ` an epsilon higher, and
|
|
108
|
+
* the mixture solver resolves partial melting by landing inside that jump
|
|
109
|
+
* rather than by nudging the temperature.
|
|
110
|
+
*/
|
|
111
|
+
declare function enthalpyJ(massKg: number, material: Material, celsius: number): number;
|
|
112
|
+
/** The phase a material is in at a temperature, taking a transition point as the lower phase. */
|
|
113
|
+
declare function phaseAt(material: Material, celsius: number): PhaseName;
|
|
114
|
+
/** One body entering a mixture. */
|
|
115
|
+
interface MixtureBody {
|
|
116
|
+
label: string;
|
|
117
|
+
/** Mass, kg. */
|
|
118
|
+
massKg: number;
|
|
119
|
+
material: Material;
|
|
120
|
+
/** Starting temperature, °C. */
|
|
121
|
+
temperatureC: number;
|
|
122
|
+
}
|
|
123
|
+
interface MixtureBodyResult {
|
|
124
|
+
label: string;
|
|
125
|
+
/** Heat this body GAINED, J. Negative for a body that cooled. */
|
|
126
|
+
heatGainedJ: number;
|
|
127
|
+
startPhase: PhaseName;
|
|
128
|
+
endPhase: PhaseName;
|
|
129
|
+
/**
|
|
130
|
+
* How far through a transition the mixture ended, 0..1, or `null` when it did
|
|
131
|
+
* not settle at a transition temperature.
|
|
132
|
+
*
|
|
133
|
+
* This is the answer to "how much of the ice melted", which is the part the
|
|
134
|
+
* single final temperature cannot tell you: a mixture that settles at 0 °C
|
|
135
|
+
* with a third of the ice melted and one that settles at 0 °C with all of it
|
|
136
|
+
* melted have the same final temperature and different contents.
|
|
137
|
+
*
|
|
138
|
+
* It is a property of the POOL, not of this body, so every body of the same
|
|
139
|
+
* material sitting at that temperature reports the same number. That is not a
|
|
140
|
+
* simplification: ice at 0 °C is ice at 0 °C, and once two lots of it are in
|
|
141
|
+
* the same beaker there is no fact about which lot a given melted gram came
|
|
142
|
+
* from. `endPhase` stays at the lower phase while this is between 0 and 1, so
|
|
143
|
+
* "solid, 0.21" reads as a fifth melted.
|
|
144
|
+
*/
|
|
145
|
+
transitionFraction: number | null;
|
|
146
|
+
}
|
|
147
|
+
interface MixtureResult {
|
|
148
|
+
/** The common final temperature, °C. */
|
|
149
|
+
finalTemperatureC: number;
|
|
150
|
+
bodies: MixtureBodyResult[];
|
|
151
|
+
/**
|
|
152
|
+
* The sum of every body's heat change, J.
|
|
153
|
+
*
|
|
154
|
+
* Reported rather than asserted, following the same choice the energy ledger
|
|
155
|
+
* makes: it must be zero to within rounding, and a caller who wants to prove
|
|
156
|
+
* that gets to see the number rather than take it on faith.
|
|
157
|
+
*/
|
|
158
|
+
netHeatJ: number;
|
|
159
|
+
/** True when at least one body ended part-way through melting or boiling. */
|
|
160
|
+
partialTransition: boolean;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The equilibrium temperature of bodies placed in a perfectly insulated
|
|
164
|
+
* container, and what state each ends in.
|
|
165
|
+
*
|
|
166
|
+
* Solves ΣH_i(θ) = ΣH_i(θ_i) exactly, by walking the piecewise-linear total
|
|
167
|
+
* enthalpy curve bracket by bracket. The brackets are the distinct transition
|
|
168
|
+
* temperatures of the materials present; inside one, every body is in a fixed
|
|
169
|
+
* phase and the total is linear, so the crossing point is one division.
|
|
170
|
+
*
|
|
171
|
+
* ASSUMES the container itself has no heat capacity. Fold a real calorimeter in
|
|
172
|
+
* as an extra body, or use `waterEquivalentKg` to express it as more water.
|
|
173
|
+
*/
|
|
174
|
+
declare function mixtureEquilibrium(bodies: readonly MixtureBody[]): MixtureResult;
|
|
175
|
+
/**
|
|
176
|
+
* The temperature rise when a fraction of a moving body's kinetic energy turns
|
|
177
|
+
* into heat inside it: f·½mv² = m·c·Δθ.
|
|
178
|
+
*
|
|
179
|
+
* The mass cancels, which is the point and the thing students are surprised by:
|
|
180
|
+
* a bullet's temperature rise on impact depends on its SPEED and its material,
|
|
181
|
+
* not on how big it is. `fraction` is how much of the energy stays in the body
|
|
182
|
+
* rather than going into the target, the air, or sound.
|
|
183
|
+
*/
|
|
184
|
+
declare function temperatureRiseFromSpeedK(speedMs: number, specificHeat: number, fraction?: number): number;
|
|
185
|
+
/**
|
|
186
|
+
* The temperature rise when a fraction of a falling body's potential energy
|
|
187
|
+
* turns into heat inside it: f·mgh = m·c·Δθ.
|
|
188
|
+
*
|
|
189
|
+
* The mass cancels here too, so the answer depends only on the DROP. This is
|
|
190
|
+
* Joule's paddle-wheel experiment read backwards, and the classic worked
|
|
191
|
+
* example is the waterfall: the 4.2 kJ it takes to warm a kilogram of water by
|
|
192
|
+
* one degree is the same energy as dropping it 428 m.
|
|
193
|
+
*/
|
|
194
|
+
declare function temperatureRiseFromDropK(heightM: number, specificHeat: number, fraction?: number, gravity?: number): number;
|
|
195
|
+
/**
|
|
196
|
+
* The mass melted when a fraction of a falling body's potential energy becomes
|
|
197
|
+
* heat in it: f·mgh = m'·L.
|
|
198
|
+
*
|
|
199
|
+
* Unlike the two above, this one keeps the mass, because the answer is a mass.
|
|
200
|
+
*/
|
|
201
|
+
declare function massMeltedFromDropKg(massKg: number, heightM: number, latentHeat: number, fraction?: number, gravity?: number): number;
|
|
202
|
+
/** Calories to joules, through the thermochemical definition. */
|
|
203
|
+
declare const caloriesToJoules: (calories: number) => number;
|
|
204
|
+
/** Joules to calories, through the thermochemical definition. */
|
|
205
|
+
declare const joulesToCalories: (joules: number) => number;
|
|
206
|
+
//#endregion
|
|
207
|
+
export { MATERIALS, Material, MixtureBody, MixtureBodyResult, MixtureResult, PhaseName, caloriesToJoules, enthalpyJ, heatToChangeStateJ, heatToWarmJ, joulesToCalories, massMeltedFromDropKg, mixtureEquilibrium, phaseAt, specificHeatFromJ, temperatureRiseFromDropK, temperatureRiseFromSpeedK, temperatureRiseK, waterEquivalentKg };
|