@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,165 @@
|
|
|
1
|
+
//#region src/chem/nmr.ts
|
|
2
|
+
/**
|
|
3
|
+
* Chemical shift ranges, in ppm, as the 9701 data booklet gives them.
|
|
4
|
+
*
|
|
5
|
+
* Ranges, never single values, because that is what the booklet prints and because a learner who
|
|
6
|
+
* memorises "2.2" for a ketone cannot place a shift of 2.4. The question is always which RANGE a
|
|
7
|
+
* peak falls in.
|
|
8
|
+
*/
|
|
9
|
+
const SHIFT_RANGES = {
|
|
10
|
+
alkane: {
|
|
11
|
+
from: .9,
|
|
12
|
+
to: 1.7,
|
|
13
|
+
environment: "R-CH3, R-CH2-R, R3CH"
|
|
14
|
+
},
|
|
15
|
+
"alkyl-next-to-carbonyl": {
|
|
16
|
+
from: 2,
|
|
17
|
+
to: 2.9,
|
|
18
|
+
environment: "CH3-CO-, -CH2-CO-"
|
|
19
|
+
},
|
|
20
|
+
"alkyl-next-to-halogen": {
|
|
21
|
+
from: 3.2,
|
|
22
|
+
to: 4,
|
|
23
|
+
environment: "R-CH2-Cl or R-CH2-Br"
|
|
24
|
+
},
|
|
25
|
+
"alkyl-next-to-oxygen": {
|
|
26
|
+
from: 3.3,
|
|
27
|
+
to: 4.3,
|
|
28
|
+
environment: "R-CH2-O-"
|
|
29
|
+
},
|
|
30
|
+
alcohol: {
|
|
31
|
+
from: .5,
|
|
32
|
+
to: 6,
|
|
33
|
+
environment: "R-O-H"
|
|
34
|
+
},
|
|
35
|
+
amine: {
|
|
36
|
+
from: 1,
|
|
37
|
+
to: 5,
|
|
38
|
+
environment: "R-N-H"
|
|
39
|
+
},
|
|
40
|
+
alkene: {
|
|
41
|
+
from: 4.5,
|
|
42
|
+
to: 6,
|
|
43
|
+
environment: "R2C=CH2"
|
|
44
|
+
},
|
|
45
|
+
arene: {
|
|
46
|
+
from: 6,
|
|
47
|
+
to: 9,
|
|
48
|
+
environment: "aromatic H"
|
|
49
|
+
},
|
|
50
|
+
aldehyde: {
|
|
51
|
+
from: 9.3,
|
|
52
|
+
to: 10.5,
|
|
53
|
+
environment: "R-CHO"
|
|
54
|
+
},
|
|
55
|
+
"carboxylic-acid": {
|
|
56
|
+
from: 9,
|
|
57
|
+
to: 13,
|
|
58
|
+
environment: "R-COOH"
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/** The name for a number of lines. Beyond five, a spectrum is reported as a multiplet. */
|
|
62
|
+
const MULTIPLICITY_NAMES = [
|
|
63
|
+
"singlet",
|
|
64
|
+
"doublet",
|
|
65
|
+
"triplet",
|
|
66
|
+
"quartet",
|
|
67
|
+
"quintet",
|
|
68
|
+
"sextet",
|
|
69
|
+
"septet"
|
|
70
|
+
];
|
|
71
|
+
/**
|
|
72
|
+
* The n + 1 rule.
|
|
73
|
+
*
|
|
74
|
+
* An exchangeable proton is always a singlet and never splits anything, because it swaps between
|
|
75
|
+
* molecules faster than the machine can see it.
|
|
76
|
+
*/
|
|
77
|
+
function multiplicityOf(neighbours, exchangeable = false) {
|
|
78
|
+
if (exchangeable) return "singlet";
|
|
79
|
+
return MULTIPLICITY_NAMES[neighbours + 1 - 1] ?? "multiplet";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Relative heights of the lines in a multiplet: a row of Pascal's triangle.
|
|
83
|
+
*
|
|
84
|
+
* A quartet is 1:3:3:1, not four equal lines. It matters because the outer lines of a large
|
|
85
|
+
* multiplet sink into the baseline, which is why a septet is so often reported as a quintet.
|
|
86
|
+
*/
|
|
87
|
+
function splittingPattern(lines) {
|
|
88
|
+
const row = [1];
|
|
89
|
+
for (let i = 1; i < lines; i += 1) row.push(row[i - 1] * (lines - i) / i);
|
|
90
|
+
return row;
|
|
91
|
+
}
|
|
92
|
+
const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
|
|
93
|
+
/**
|
|
94
|
+
* Integration as the smallest whole-number ratio.
|
|
95
|
+
*
|
|
96
|
+
* A spectrum reports 2:3, not 4:6, because the integration gives the RATIO of protons and never
|
|
97
|
+
* their number. That distinction is the one an exam question turns on: a 2:3 ratio fits both
|
|
98
|
+
* C2H5-OH and C4H10O2, and the molecular formula is what settles it.
|
|
99
|
+
*/
|
|
100
|
+
function integrationRatio(environments) {
|
|
101
|
+
const counts = environments.map((e) => e.protons);
|
|
102
|
+
if (counts.length === 0) return [];
|
|
103
|
+
const divisor = counts.reduce((a, b) => gcd(a, b));
|
|
104
|
+
return counts.map((n) => n / divisor);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Predict the spectrum.
|
|
108
|
+
*
|
|
109
|
+
* Refuses a declaration that contradicts itself rather than drawing a spectrum nobody could have
|
|
110
|
+
* measured: a peak outside the range it claims, or an exchangeable proton given neighbours to
|
|
111
|
+
* couple to. Both are ways a hand-authored figure goes quietly wrong.
|
|
112
|
+
*/
|
|
113
|
+
function predictNmr(molecule) {
|
|
114
|
+
const ratio = integrationRatio(molecule.environments);
|
|
115
|
+
return {
|
|
116
|
+
molecule,
|
|
117
|
+
peaks: [...molecule.environments.map((env, i) => {
|
|
118
|
+
const range = SHIFT_RANGES[env.shift];
|
|
119
|
+
if (!range) throw new Error(`${env.label}: "${env.shift}" is not a shift range in the booklet.`);
|
|
120
|
+
if (env.ppm < range.from || env.ppm > range.to) throw new Error(`${env.label}: ${env.ppm} ppm is outside the ${range.from} to ${range.to} range for ${env.shift}.`);
|
|
121
|
+
if (env.exchangeable && env.neighbours > 0) throw new Error(`${env.label}: an exchangeable proton cannot be coupled, so it must have 0 neighbours.`);
|
|
122
|
+
const multiplicity = multiplicityOf(env.neighbours, env.exchangeable);
|
|
123
|
+
const lines = env.exchangeable ? 1 : env.neighbours + 1;
|
|
124
|
+
return {
|
|
125
|
+
label: env.label,
|
|
126
|
+
ppm: env.ppm,
|
|
127
|
+
protons: env.protons,
|
|
128
|
+
integration: ratio[i],
|
|
129
|
+
multiplicity,
|
|
130
|
+
lines,
|
|
131
|
+
pattern: splittingPattern(lines),
|
|
132
|
+
range,
|
|
133
|
+
exchangeable: env.exchangeable === true
|
|
134
|
+
};
|
|
135
|
+
})].sort((a, b) => b.ppm - a.ppm),
|
|
136
|
+
totalProtons: molecule.environments.reduce((n, e) => n + e.protons, 0)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The spectrum after a drop of D2O, with every exchangeable peak gone.
|
|
141
|
+
*
|
|
142
|
+
* The experiment exists to answer one question: is that broad singlet an O-H? Running it is how a
|
|
143
|
+
* learner tells an alcohol from an ether without any other evidence, and doing it as a computation
|
|
144
|
+
* rather than a second hand-drawn figure means the two spectra cannot disagree.
|
|
145
|
+
*/
|
|
146
|
+
const afterD2O = (spectrum) => ({
|
|
147
|
+
...spectrum,
|
|
148
|
+
peaks: spectrum.peaks.filter((p) => !p.exchangeable),
|
|
149
|
+
totalProtons: spectrum.peaks.filter((p) => !p.exchangeable).reduce((n, p) => n + p.protons, 0)
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* The line positions of one peak, in ppm, given the coupling constant.
|
|
153
|
+
*
|
|
154
|
+
* `J` is in hertz and a spectrum is in ppm, so the conversion needs the spectrometer frequency.
|
|
155
|
+
* That is not a detail to hide: it is why the same molecule gives the same splitting PATTERN on
|
|
156
|
+
* every machine while the lines sit closer together on a stronger one.
|
|
157
|
+
*/
|
|
158
|
+
function linePositions(peak, jHz = 7, spectrometerMHz = 100) {
|
|
159
|
+
const spacing = jHz / spectrometerMHz;
|
|
160
|
+
const first = peak.ppm - spacing * (peak.lines - 1) / 2;
|
|
161
|
+
return Array.from({ length: peak.lines }, (_, i) => first + i * spacing);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//#endregion
|
|
165
|
+
export { MULTIPLICITY_NAMES, SHIFT_RANGES, afterD2O, integrationRatio, linePositions, multiplicityOf, predictNmr, splittingPattern };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Structure } from "./molecule.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/chem/polymer.d.ts
|
|
4
|
+
type PolymerKind = 'addition' | 'condensation';
|
|
5
|
+
/** What the two link points are: an ester link, an amide link, or a plain carbon-carbon bond. */
|
|
6
|
+
type LinkKind = 'carbon-carbon' | 'ester' | 'amide';
|
|
7
|
+
interface RepeatUnit {
|
|
8
|
+
readonly kind: PolymerKind;
|
|
9
|
+
readonly link: LinkKind;
|
|
10
|
+
/** The unit itself, with the two atoms that bond onto the neighbouring units. */
|
|
11
|
+
readonly structure: Structure;
|
|
12
|
+
/** Atom ids in `structure` that join to the previous and the next unit. */
|
|
13
|
+
readonly linkAtoms: readonly [number, number];
|
|
14
|
+
/** The molecular formula of one repeat unit. */
|
|
15
|
+
readonly formula: string;
|
|
16
|
+
readonly mass: number;
|
|
17
|
+
/** The small molecule lost per link, for a condensation. `undefined` for addition. */
|
|
18
|
+
readonly lost?: {
|
|
19
|
+
readonly formula: string;
|
|
20
|
+
readonly mass: number;
|
|
21
|
+
};
|
|
22
|
+
/** Why this monomer or pair polymerises the way it does, in the words a mark scheme uses. */
|
|
23
|
+
readonly reason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Open the double bond of an alkene to give the addition repeat unit.
|
|
27
|
+
*
|
|
28
|
+
* Refuses a monomer with no C=C, because "it must be addition" is a guess a learner makes and a
|
|
29
|
+
* kernel should not. The formula is unchanged by construction, and the test asserts it.
|
|
30
|
+
*/
|
|
31
|
+
declare function addition(monomer: Structure): RepeatUnit;
|
|
32
|
+
/**
|
|
33
|
+
* Can this monomer sustain a chain?
|
|
34
|
+
*
|
|
35
|
+
* Two reactive groups, or the chain stops after one join. Ethanoic acid has one and is therefore a
|
|
36
|
+
* dead end; hexanedioic acid has two and is not. This is the check most treatments leave out and
|
|
37
|
+
* the reason a learner cannot predict whether a new monomer will polymerise.
|
|
38
|
+
*/
|
|
39
|
+
declare const canSustainChain: (monomer: Structure) => boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Join two monomers and eliminate water, giving the condensation repeat unit.
|
|
42
|
+
*
|
|
43
|
+
* The pairing is decided by the groups that are actually present, not by an argument the caller
|
|
44
|
+
* passes: an acid with an alcohol gives an ester link and an acid with an amine gives an amide link,
|
|
45
|
+
* and those are the only two the syllabus needs. A pair that cannot make either is refused with the
|
|
46
|
+
* reason, because "nothing happened" is not a useful answer to a learner.
|
|
47
|
+
*/
|
|
48
|
+
declare function condensation(first: Structure, second: Structure): RepeatUnit;
|
|
49
|
+
/**
|
|
50
|
+
* The mass of a chain of `n` repeat units.
|
|
51
|
+
*
|
|
52
|
+
* For an addition polymer it is n times the monomer. For a condensation polymer the water has
|
|
53
|
+
* already gone from the repeat unit, so the same multiplication works, and the difference between
|
|
54
|
+
* the two shows up as a mass that is not a whole multiple of the monomers.
|
|
55
|
+
*/
|
|
56
|
+
declare const chainMass: (unit: RepeatUnit, n: number) => number;
|
|
57
|
+
/**
|
|
58
|
+
* How many repeat units a chain of this mass holds, to the nearest whole unit.
|
|
59
|
+
*
|
|
60
|
+
* The question an exam asks the other way round: a poly(ethene) sample with a relative molecular
|
|
61
|
+
* mass of 28 000 contains 1000 repeat units, and that number is what "long chain" means.
|
|
62
|
+
*/
|
|
63
|
+
declare const unitsInChain: (unit: RepeatUnit, chainMr: number) => number;
|
|
64
|
+
//#endregion
|
|
65
|
+
export { LinkKind, PolymerKind, RepeatUnit, addition, canSustainChain, chainMass, condensation, unitsInChain };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { ATOMIC_MASS, molecularFormula, relativeMass } from "./molecule.mjs";
|
|
2
|
+
import { functionalGroups } from "./functional-groups.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/chem/polymer.ts
|
|
5
|
+
/**
|
|
6
|
+
* polymer, the repeat unit worked out from the monomer rather than drawn beside it.
|
|
7
|
+
*
|
|
8
|
+
* WHAT THIS IS FOR. Polymerisation is taught as two pictures to copy: an alkene with its double bond
|
|
9
|
+
* opened, and two monomers with a water molecule falling out between them. Copied, they are
|
|
10
|
+
* indistinguishable, and a learner who has copied both still cannot say which one a new monomer will
|
|
11
|
+
* do. The question an exam actually asks is "draw the repeat unit", and that is a transformation of
|
|
12
|
+
* a structure, so it can be performed.
|
|
13
|
+
*
|
|
14
|
+
* ## Addition: the double bond becomes the link
|
|
15
|
+
*
|
|
16
|
+
* An alkene's C=C opens. One bond stays holding the two carbons together, and the other becomes the
|
|
17
|
+
* two bonds that join this unit to the next. So the repeat unit has the SAME molecular formula as
|
|
18
|
+
* the monomer, which is the fact that tells addition from condensation without looking at anything
|
|
19
|
+
* else.
|
|
20
|
+
*
|
|
21
|
+
* ## Condensation: two groups meet and lose a small molecule
|
|
22
|
+
*
|
|
23
|
+
* A carboxylic acid meets an alcohol and loses water, leaving an ester link. It meets an amine and
|
|
24
|
+
* loses water, leaving an amide link, which is also the peptide link in a protein. The repeat unit
|
|
25
|
+
* is therefore LIGHTER than the monomers by the mass of what left, and each monomer needs TWO
|
|
26
|
+
* reactive groups or the chain stops after one join.
|
|
27
|
+
*
|
|
28
|
+
* That last point is the one usually left out, and it is what makes the difference between ethanoic
|
|
29
|
+
* acid, which cannot polymerise, and hexanedioic acid, which can.
|
|
30
|
+
*
|
|
31
|
+
* Pure: no React, no pixels.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Open the double bond of an alkene to give the addition repeat unit.
|
|
35
|
+
*
|
|
36
|
+
* Refuses a monomer with no C=C, because "it must be addition" is a guess a learner makes and a
|
|
37
|
+
* kernel should not. The formula is unchanged by construction, and the test asserts it.
|
|
38
|
+
*/
|
|
39
|
+
function addition(monomer) {
|
|
40
|
+
const alkene = functionalGroups(monomer).find((g) => g.kind === "alkene");
|
|
41
|
+
if (!alkene) throw new Error(`${molecularFormula(monomer)} has no C=C, so it cannot polymerise by addition. Addition polymerisation needs a double bond to open.`);
|
|
42
|
+
const [a, b] = alkene.atoms;
|
|
43
|
+
const bonds = monomer.bonds.map((bond) => bond.a === a && bond.b === b || bond.a === b && bond.b === a ? {
|
|
44
|
+
...bond,
|
|
45
|
+
order: 1
|
|
46
|
+
} : bond);
|
|
47
|
+
const structure = {
|
|
48
|
+
atoms: monomer.atoms.map((atom) => atom.id === a || atom.id === b ? {
|
|
49
|
+
...atom,
|
|
50
|
+
openValence: (atom.openValence ?? 0) + 1
|
|
51
|
+
} : atom),
|
|
52
|
+
bonds
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
kind: "addition",
|
|
56
|
+
link: "carbon-carbon",
|
|
57
|
+
structure,
|
|
58
|
+
linkAtoms: [a, b],
|
|
59
|
+
formula: molecularFormula(structure),
|
|
60
|
+
mass: relativeMass(structure),
|
|
61
|
+
reason: "The C=C opens. One of its two bonds holds the carbons together and the other becomes the bonds to the units on either side, so nothing is lost and the repeat unit has the same formula as the monomer."
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Groups that can form a link, and what they form it with. */
|
|
65
|
+
const REACTIVE = [
|
|
66
|
+
"carboxylic-acid",
|
|
67
|
+
"alcohol",
|
|
68
|
+
"amine"
|
|
69
|
+
];
|
|
70
|
+
const reactiveGroups = (s) => functionalGroups(s).filter((g) => REACTIVE.includes(g.kind));
|
|
71
|
+
/**
|
|
72
|
+
* Can this monomer sustain a chain?
|
|
73
|
+
*
|
|
74
|
+
* Two reactive groups, or the chain stops after one join. Ethanoic acid has one and is therefore a
|
|
75
|
+
* dead end; hexanedioic acid has two and is not. This is the check most treatments leave out and
|
|
76
|
+
* the reason a learner cannot predict whether a new monomer will polymerise.
|
|
77
|
+
*/
|
|
78
|
+
const canSustainChain = (monomer) => reactiveGroups(monomer).length >= 2;
|
|
79
|
+
const WATER = {
|
|
80
|
+
formula: "H2O",
|
|
81
|
+
mass: ATOMIC_MASS.H * 2 + ATOMIC_MASS.O
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Join two monomers and eliminate water, giving the condensation repeat unit.
|
|
85
|
+
*
|
|
86
|
+
* The pairing is decided by the groups that are actually present, not by an argument the caller
|
|
87
|
+
* passes: an acid with an alcohol gives an ester link and an acid with an amine gives an amide link,
|
|
88
|
+
* and those are the only two the syllabus needs. A pair that cannot make either is refused with the
|
|
89
|
+
* reason, because "nothing happened" is not a useful answer to a learner.
|
|
90
|
+
*/
|
|
91
|
+
function condensation(first, second) {
|
|
92
|
+
const a = reactiveGroups(first);
|
|
93
|
+
const b = reactiveGroups(second);
|
|
94
|
+
const kindsA = new Set(a.map((g) => g.kind));
|
|
95
|
+
const kindsB = new Set(b.map((g) => g.kind));
|
|
96
|
+
const pairing = [[
|
|
97
|
+
"carboxylic-acid",
|
|
98
|
+
"alcohol",
|
|
99
|
+
"ester"
|
|
100
|
+
], [
|
|
101
|
+
"carboxylic-acid",
|
|
102
|
+
"amine",
|
|
103
|
+
"amide"
|
|
104
|
+
]].find(([x, y]) => kindsA.has(x) && kindsB.has(y) || kindsA.has(y) && kindsB.has(x));
|
|
105
|
+
if (!pairing) throw new Error(`${molecularFormula(first)} and ${molecularFormula(second)} have no pair of groups that can condense. An ester link needs an acid and an alcohol; an amide link needs an acid and an amine.`);
|
|
106
|
+
const [, , link] = pairing;
|
|
107
|
+
if (!canSustainChain(first) || !canSustainChain(second)) throw new Error(`A condensation polymer needs TWO reactive groups on each monomer. ${canSustainChain(first) ? molecularFormula(second) : molecularFormula(first)} has only one, so the chain would stop after a single join.`);
|
|
108
|
+
const acids = a.filter((g) => g.kind === "carboxylic-acid");
|
|
109
|
+
const partners = b.filter((g) => g.kind === (link === "ester" ? "alcohol" : "amine"));
|
|
110
|
+
if (acids.length < 2 || partners.length < 2) throw new Error("A repeat unit needs two acid groups and two partner groups, one pair to link and one pair to continue the chain.");
|
|
111
|
+
/** The atom that carries the new bond: the oxygen of an alcohol, or the nitrogen of an amine. */
|
|
112
|
+
const partnerAtom = (group) => group.kind === "alcohol" ? group.atoms[1] : group.atoms[0];
|
|
113
|
+
const shift = first.atoms.length;
|
|
114
|
+
const dropped = new Set([acids[0].atoms[2], acids[1].atoms[2]]);
|
|
115
|
+
const opened = new Map([[acids[1].atoms[0], 1], [partnerAtom(partners[1]) + shift, 1]]);
|
|
116
|
+
const kept = [...first.atoms.filter((atom) => !dropped.has(atom.id)), ...second.atoms.map((atom) => ({
|
|
117
|
+
...atom,
|
|
118
|
+
id: atom.id + shift
|
|
119
|
+
}))];
|
|
120
|
+
const renumber = new Map(kept.map((atom, i) => [atom.id, i]));
|
|
121
|
+
const merged = {
|
|
122
|
+
atoms: kept.map((atom, i) => ({
|
|
123
|
+
...atom,
|
|
124
|
+
id: i,
|
|
125
|
+
...opened.has(atom.id) ? { openValence: (atom.openValence ?? 0) + opened.get(atom.id) } : {}
|
|
126
|
+
})),
|
|
127
|
+
bonds: [
|
|
128
|
+
...first.bonds.filter((bond) => !dropped.has(bond.a) && !dropped.has(bond.b)),
|
|
129
|
+
...second.bonds.map((bond) => ({
|
|
130
|
+
...bond,
|
|
131
|
+
a: bond.a + shift,
|
|
132
|
+
b: bond.b + shift
|
|
133
|
+
})),
|
|
134
|
+
{
|
|
135
|
+
a: acids[0].atoms[0],
|
|
136
|
+
b: partnerAtom(partners[0]) + shift,
|
|
137
|
+
order: 1,
|
|
138
|
+
aromatic: false
|
|
139
|
+
}
|
|
140
|
+
].map((bond) => ({
|
|
141
|
+
...bond,
|
|
142
|
+
a: renumber.get(bond.a),
|
|
143
|
+
b: renumber.get(bond.b)
|
|
144
|
+
}))
|
|
145
|
+
};
|
|
146
|
+
const mass = relativeMass(merged);
|
|
147
|
+
const formula = molecularFormula(merged);
|
|
148
|
+
return {
|
|
149
|
+
kind: "condensation",
|
|
150
|
+
link,
|
|
151
|
+
structure: merged,
|
|
152
|
+
linkAtoms: [renumber.get(acids[1].atoms[0]), renumber.get(partnerAtom(partners[1]) + shift)],
|
|
153
|
+
formula,
|
|
154
|
+
mass,
|
|
155
|
+
lost: {
|
|
156
|
+
formula: WATER.formula,
|
|
157
|
+
mass: Math.round(WATER.mass * 10) / 10
|
|
158
|
+
},
|
|
159
|
+
reason: link === "ester" ? "The O-H of the acid and the O-H of the alcohol meet, water is eliminated and an ester link is left. Each monomer needs two groups, so the chain can continue at both ends." : "The O-H of the acid and an N-H of the amine meet, water is eliminated and an amide link is left. That is the same link that joins amino acids into a protein, where it is called a peptide link."
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The mass of a chain of `n` repeat units.
|
|
164
|
+
*
|
|
165
|
+
* For an addition polymer it is n times the monomer. For a condensation polymer the water has
|
|
166
|
+
* already gone from the repeat unit, so the same multiplication works, and the difference between
|
|
167
|
+
* the two shows up as a mass that is not a whole multiple of the monomers.
|
|
168
|
+
*/
|
|
169
|
+
const chainMass = (unit, n) => Math.round(unit.mass * n * 10) / 10;
|
|
170
|
+
/**
|
|
171
|
+
* How many repeat units a chain of this mass holds, to the nearest whole unit.
|
|
172
|
+
*
|
|
173
|
+
* The question an exam asks the other way round: a poly(ethene) sample with a relative molecular
|
|
174
|
+
* mass of 28 000 contains 1000 repeat units, and that number is what "long chain" means.
|
|
175
|
+
*/
|
|
176
|
+
const unitsInChain = (unit, chainMr) => Math.round(chainMr / unit.mass);
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
export { addition, canSustainChain, chainMass, condensation, unitsInChain };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
//#region src/chem/redox.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* redox: oxidation numbers, half-equations, and the ionic equation they combine into.
|
|
4
|
+
*
|
|
5
|
+
* Three things the syllabus treats as three topics and which are one procedure applied at
|
|
6
|
+
* different scales. A learner who balances a half-equation in alkali by first balancing it in acid
|
|
7
|
+
* has understood that; one who memorises two procedures has not, which is why `medium` is an
|
|
8
|
+
* ARGUMENT here rather than a second function.
|
|
9
|
+
*
|
|
10
|
+
* ## Writing a species, and why the caret is required
|
|
11
|
+
*
|
|
12
|
+
* `'MnO4-'`, `'Cr2O7^2-'`, `'Fe^3+'`, `'H2O'`.
|
|
13
|
+
*
|
|
14
|
+
* A charge of one is a bare sign. **Any larger charge needs a caret**, because plain ASCII cannot
|
|
15
|
+
* distinguish the two readings of a digit before a sign and no syntactic rule can:
|
|
16
|
+
*
|
|
17
|
+
* `MnO4-` the 4 is a subscript, and the charge is −1
|
|
18
|
+
* `Mn2+` the 2 is the charge, and there is one manganese
|
|
19
|
+
*
|
|
20
|
+
* Both end in a letter, a digit and a sign. Only chemistry tells them apart, so an implementation
|
|
21
|
+
* that guesses is wrong roughly half the time and silently. Writing `Mn^2+` removes the question,
|
|
22
|
+
* and a digit before a bare sign is refused with a message saying so.
|
|
23
|
+
*
|
|
24
|
+
* `speciesToUnicode` renders `Mn^2+` as `Mn²⁺` for display, so the caret never reaches a learner.
|
|
25
|
+
*
|
|
26
|
+
* ## Oxidation numbers
|
|
27
|
+
*
|
|
28
|
+
* The rules are applied in order of how rarely they are broken: F is always −1, group 1 always +1,
|
|
29
|
+
* group 2 always +2, H is +1 except in a metal hydride, O is −2 except in a peroxide. Everything
|
|
30
|
+
* else is then fixed by the requirement that the oxidation numbers sum to the charge.
|
|
31
|
+
*
|
|
32
|
+
* That last line is the whole method, and it is why only ONE element may be unknown. A species with
|
|
33
|
+
* two unknowns is refused rather than guessed at, because the answer would not be determined.
|
|
34
|
+
*
|
|
35
|
+
* ## Balancing
|
|
36
|
+
*
|
|
37
|
+
* The five steps, in the order a mark scheme wants them:
|
|
38
|
+
*
|
|
39
|
+
* 1. balance the element being oxidised or reduced,
|
|
40
|
+
* 2. balance oxygen with H₂O,
|
|
41
|
+
* 3. balance hydrogen with H⁺,
|
|
42
|
+
* 4. balance the charge with electrons,
|
|
43
|
+
* 5. in ALKALI only, add OH⁻ to both sides to mop up the H⁺.
|
|
44
|
+
*
|
|
45
|
+
* Step 5 is what makes alkaline balancing one procedure rather than two: balance in acid, then
|
|
46
|
+
* neutralise. Every H⁺ meets an OH⁻ and becomes water, and the excess water cancels.
|
|
47
|
+
*
|
|
48
|
+
* Pure: no React, no pixels.
|
|
49
|
+
*/
|
|
50
|
+
type Medium = 'acid' | 'alkali';
|
|
51
|
+
interface Species {
|
|
52
|
+
/** The formula as written, without its coefficient. */
|
|
53
|
+
readonly formula: string;
|
|
54
|
+
/** Element symbol to count. */
|
|
55
|
+
readonly elements: Readonly<Record<string, number>>;
|
|
56
|
+
/** Net charge. 0 for a neutral species. */
|
|
57
|
+
readonly charge: number;
|
|
58
|
+
}
|
|
59
|
+
interface Term {
|
|
60
|
+
readonly coeff: number;
|
|
61
|
+
readonly species: Species;
|
|
62
|
+
}
|
|
63
|
+
interface HalfEquation {
|
|
64
|
+
readonly left: readonly Term[];
|
|
65
|
+
readonly right: readonly Term[];
|
|
66
|
+
/** Electrons, on the side they appear. Always positive. */
|
|
67
|
+
readonly electrons: number;
|
|
68
|
+
/** 'left' for a reduction (electrons are gained), 'right' for an oxidation. */
|
|
69
|
+
readonly electronSide: 'left' | 'right';
|
|
70
|
+
readonly medium: Medium;
|
|
71
|
+
}
|
|
72
|
+
/** Parse `'Cr2O7^2-'` into its element counts and charge. See the docblock on the caret rule. */
|
|
73
|
+
declare function parseSpecies(input: string): Species;
|
|
74
|
+
/**
|
|
75
|
+
* Oxidation number of every element in a species.
|
|
76
|
+
*
|
|
77
|
+
* `peroxide` forces O to −1, for H₂O₂ and its salts. It is an argument rather than a detection
|
|
78
|
+
* rule, because H₂O₂ and H₂O differ only in a subscript and guessing from the formula would make
|
|
79
|
+
* the kernel wrong in exactly the case the syllabus asks about.
|
|
80
|
+
*/
|
|
81
|
+
declare function oxidationNumbers(input: string | Species, opts?: {
|
|
82
|
+
peroxide?: boolean;
|
|
83
|
+
hydride?: boolean;
|
|
84
|
+
}): Record<string, number>;
|
|
85
|
+
/**
|
|
86
|
+
* `'Cr2O7^2-'` → `'Cr₂O₇²⁻'`, for display.
|
|
87
|
+
*
|
|
88
|
+
* The caret is an input convention forced by ASCII ambiguity, and a learner should never see it.
|
|
89
|
+
*
|
|
90
|
+
* ## The one place the caret is not needed
|
|
91
|
+
*
|
|
92
|
+
* After a closing bracket. `[Cu(H2O)6]2+` has no ambiguity at all, because a digit cannot be the
|
|
93
|
+
* subscript of a `]`, and that is the form `complexFormula` emits. Without this case a complex ion
|
|
94
|
+
* came back as `[Cu(H₂O)₆]₂⁺`, with its charge written as a subscript: the same class of defect as a
|
|
95
|
+
* formatter producing a string its sibling parser cannot read.
|
|
96
|
+
*/
|
|
97
|
+
declare function speciesToUnicode(formula: string): string;
|
|
98
|
+
/** Positive when oxidised, negative when reduced, for `element` between two species. */
|
|
99
|
+
declare function oxidationChange(element: string, from: string, to: string): number;
|
|
100
|
+
/**
|
|
101
|
+
* Balance one half-equation, in acid or in alkali.
|
|
102
|
+
*
|
|
103
|
+
* `balanceHalf('MnO4-', 'Mn2+')` → MnO₄⁻ + 8H⁺ + 5e⁻ → Mn²⁺ + 4H₂O.
|
|
104
|
+
*
|
|
105
|
+
* Alkali is not a second procedure: the equation is balanced in acid and then neutralised, which is
|
|
106
|
+
* exactly what a mark scheme wants written.
|
|
107
|
+
*/
|
|
108
|
+
declare function balanceHalf(from: string, to: string, medium?: Medium): HalfEquation;
|
|
109
|
+
/**
|
|
110
|
+
* "MnO₄⁻ + 8H⁺ + 5e⁻ → Mn²⁺ + 4H₂O", for display.
|
|
111
|
+
*
|
|
112
|
+
* Built from the TERMS, not by running `speciesToUnicode` over the finished string. That was tried
|
|
113
|
+
* and is wrong: a coefficient is a normal-size number and a separator is a plus sign, so formatting
|
|
114
|
+
* the whole line turns `2Cr^3+ + 7H2O` into subscripted coefficients and superscripted plus signs.
|
|
115
|
+
* Chemistry typography is per-species, so the formatter has to be too.
|
|
116
|
+
*/
|
|
117
|
+
declare function halfToUnicode(half: HalfEquation): string;
|
|
118
|
+
/** "MnO₄⁻ + 8H⁺ + 5Fe²⁺ → Mn²⁺ + 4H₂O + 5Fe³⁺", for display. */
|
|
119
|
+
declare function ionicToUnicode(equation: IonicEquation): string;
|
|
120
|
+
/** "MnO4- + 8H+ + 5e- -> Mn2+ + 4H2O" */
|
|
121
|
+
declare function halfToString(half: HalfEquation): string;
|
|
122
|
+
interface IonicEquation {
|
|
123
|
+
readonly left: readonly Term[];
|
|
124
|
+
readonly right: readonly Term[];
|
|
125
|
+
/** How many electrons were transferred once the two halves were scaled to match. */
|
|
126
|
+
readonly transferred: number;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Combine a reduction and an oxidation into the full ionic equation.
|
|
130
|
+
*
|
|
131
|
+
* Each half is scaled so the electrons cancel exactly, which is the step the whole topic exists to
|
|
132
|
+
* teach: electrons are not created, so the number lost must equal the number gained.
|
|
133
|
+
*/
|
|
134
|
+
declare function combineHalves(a: HalfEquation, b: HalfEquation): IonicEquation;
|
|
135
|
+
/** "2MnO4- + 16H+ + 10Fe2+ -> 2Mn2+ + 8H2O + 10Fe3+" */
|
|
136
|
+
declare function ionicToString(equation: IonicEquation): string;
|
|
137
|
+
//#endregion
|
|
138
|
+
export { HalfEquation, IonicEquation, Medium, Species, Term, balanceHalf, combineHalves, halfToString, halfToUnicode, ionicToString, ionicToUnicode, oxidationChange, oxidationNumbers, parseSpecies, speciesToUnicode };
|