@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,281 @@
|
|
|
1
|
+
//#region src/chem/synthesis.ts
|
|
2
|
+
/** What a learner calls the group. The name is chemistry; where it sits on a map is not. */
|
|
3
|
+
const GROUP_LABELS = {
|
|
4
|
+
alkane: "alkane",
|
|
5
|
+
alkene: "alkene",
|
|
6
|
+
halogenoalkane: "halogenoalkane",
|
|
7
|
+
alcohol: "alcohol",
|
|
8
|
+
nitrile: "nitrile",
|
|
9
|
+
aldehyde: "aldehyde",
|
|
10
|
+
ketone: "ketone",
|
|
11
|
+
acid: "carboxylic acid",
|
|
12
|
+
ester: "ester",
|
|
13
|
+
"acyl-chloride": "acyl chloride",
|
|
14
|
+
arene: "arene",
|
|
15
|
+
nitroarene: "nitroarene",
|
|
16
|
+
arylamine: "aromatic amine",
|
|
17
|
+
amine: "amine",
|
|
18
|
+
amide: "amide"
|
|
19
|
+
};
|
|
20
|
+
const ROUTE_EDGES = [
|
|
21
|
+
{
|
|
22
|
+
from: "alkane",
|
|
23
|
+
to: "halogenoalkane",
|
|
24
|
+
reagent: "Cl₂ or Br₂",
|
|
25
|
+
condition: "ultraviolet light"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
from: "alkene",
|
|
29
|
+
to: "halogenoalkane",
|
|
30
|
+
reagent: "HBr",
|
|
31
|
+
condition: "room temperature"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
from: "alkene",
|
|
35
|
+
to: "alcohol",
|
|
36
|
+
reagent: "steam",
|
|
37
|
+
condition: "300 °C, 60 atm, H₃PO₄ catalyst"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
from: "halogenoalkane",
|
|
41
|
+
to: "alcohol",
|
|
42
|
+
reagent: "NaOH",
|
|
43
|
+
condition: "aqueous, warm"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
from: "halogenoalkane",
|
|
47
|
+
to: "alkene",
|
|
48
|
+
reagent: "NaOH",
|
|
49
|
+
condition: "in ethanol, hot, under reflux"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
from: "halogenoalkane",
|
|
53
|
+
to: "nitrile",
|
|
54
|
+
reagent: "KCN",
|
|
55
|
+
condition: "in ethanol, under reflux",
|
|
56
|
+
changesChain: true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
from: "nitrile",
|
|
60
|
+
to: "acid",
|
|
61
|
+
reagent: "dilute HCl",
|
|
62
|
+
condition: "heat under reflux"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
from: "alcohol",
|
|
66
|
+
to: "alkene",
|
|
67
|
+
reagent: "conc. H₂SO₄",
|
|
68
|
+
condition: "170 °C, dehydration"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
from: "alcohol",
|
|
72
|
+
to: "halogenoalkane",
|
|
73
|
+
reagent: "HBr or PCl₅",
|
|
74
|
+
condition: "HBr made in situ from NaBr and conc. H₂SO₄, or PCl₅ at room temperature"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
from: "alcohol",
|
|
78
|
+
to: "aldehyde",
|
|
79
|
+
reagent: "K₂Cr₂O₇ / H⁺",
|
|
80
|
+
condition: "distil the product off as it forms",
|
|
81
|
+
needs: "a primary alcohol"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
from: "alcohol",
|
|
85
|
+
to: "ketone",
|
|
86
|
+
reagent: "K₂Cr₂O₇ / H⁺",
|
|
87
|
+
condition: "heat under reflux",
|
|
88
|
+
needs: "a secondary alcohol"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
from: "aldehyde",
|
|
92
|
+
to: "acid",
|
|
93
|
+
reagent: "K₂Cr₂O₇ / H⁺",
|
|
94
|
+
condition: "heat under reflux"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
from: "aldehyde",
|
|
98
|
+
to: "alcohol",
|
|
99
|
+
reagent: "NaBH₄",
|
|
100
|
+
condition: "in aqueous ethanol"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
from: "ketone",
|
|
104
|
+
to: "alcohol",
|
|
105
|
+
reagent: "NaBH₄",
|
|
106
|
+
condition: "in aqueous ethanol"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
from: "acid",
|
|
110
|
+
to: "ester",
|
|
111
|
+
reagent: "an alcohol",
|
|
112
|
+
condition: "conc. H₂SO₄ catalyst, warm"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
from: "ester",
|
|
116
|
+
to: "acid",
|
|
117
|
+
reagent: "dilute H₂SO₄",
|
|
118
|
+
condition: "heat under reflux"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
from: "acid",
|
|
122
|
+
to: "acyl-chloride",
|
|
123
|
+
reagent: "PCl₅ or SOCl₂",
|
|
124
|
+
condition: "room temperature, in dry conditions"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
from: "acyl-chloride",
|
|
128
|
+
to: "ester",
|
|
129
|
+
reagent: "an alcohol",
|
|
130
|
+
condition: "room temperature, no catalyst, and it goes to completion"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
from: "acyl-chloride",
|
|
134
|
+
to: "acid",
|
|
135
|
+
reagent: "water",
|
|
136
|
+
condition: "room temperature, vigorous, fumes of HCl"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
from: "arene",
|
|
140
|
+
to: "nitroarene",
|
|
141
|
+
reagent: "conc. HNO₃ with conc. H₂SO₄",
|
|
142
|
+
condition: "below 55 °C"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
from: "nitroarene",
|
|
146
|
+
to: "arylamine",
|
|
147
|
+
reagent: "Sn with conc. HCl, then NaOH(aq)",
|
|
148
|
+
condition: "reflux, then make alkaline to free the amine"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
from: "nitrile",
|
|
152
|
+
to: "amine",
|
|
153
|
+
reagent: "LiAlH₄, or H₂ with a nickel catalyst",
|
|
154
|
+
condition: "LiAlH₄ in dry ether, or H₂ at high temperature and pressure"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
from: "acyl-chloride",
|
|
158
|
+
to: "amide",
|
|
159
|
+
reagent: "NH₃ or a primary amine",
|
|
160
|
+
condition: "room temperature, two moles of the amine"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
from: "arylamine",
|
|
164
|
+
to: "amide",
|
|
165
|
+
reagent: "an acyl chloride",
|
|
166
|
+
condition: "room temperature"
|
|
167
|
+
}
|
|
168
|
+
];
|
|
169
|
+
/** Every group the map knows, in the order the type declares them. */
|
|
170
|
+
const GROUP_NODES = Object.keys(GROUP_LABELS);
|
|
171
|
+
/** Roads leaving a group. */
|
|
172
|
+
const edgesFrom = (id) => ROUTE_EDGES.filter((edge) => edge.from === id);
|
|
173
|
+
/** The road between two groups, if there is one. */
|
|
174
|
+
const edgeBetween = (from, to) => ROUTE_EDGES.find((edge) => edge.from === from && edge.to === to);
|
|
175
|
+
/** The roads along a path of groups, so a route can be read back as an answer. */
|
|
176
|
+
function edgesAlong(path) {
|
|
177
|
+
const out = [];
|
|
178
|
+
for (let i = 0; i < path.length - 1; i += 1) {
|
|
179
|
+
const edge = edgeBetween(path[i], path[i + 1]);
|
|
180
|
+
if (edge) out.push(edge);
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Shortest path length between two groups, by breadth-first search.
|
|
186
|
+
*
|
|
187
|
+
* `undefined` when no route exists, which is a real answer rather than a failure: a learner
|
|
188
|
+
* hunting for a reaction that does not exist is exactly what a route map is for.
|
|
189
|
+
*/
|
|
190
|
+
function shortestSteps(from, to) {
|
|
191
|
+
if (from === to) return 0;
|
|
192
|
+
const seen = new Set([from]);
|
|
193
|
+
let frontier = [from];
|
|
194
|
+
let depth = 0;
|
|
195
|
+
while (frontier.length) {
|
|
196
|
+
depth += 1;
|
|
197
|
+
const next = [];
|
|
198
|
+
for (const node of frontier) for (const edge of edgesFrom(node)) {
|
|
199
|
+
if (edge.to === to) return depth;
|
|
200
|
+
if (seen.has(edge.to)) continue;
|
|
201
|
+
seen.add(edge.to);
|
|
202
|
+
next.push(edge.to);
|
|
203
|
+
}
|
|
204
|
+
frontier = next;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const asRoute = (path) => {
|
|
208
|
+
const steps = edgesAlong(path);
|
|
209
|
+
return {
|
|
210
|
+
path,
|
|
211
|
+
steps,
|
|
212
|
+
changesChain: steps.some((s) => s.changesChain === true)
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* One shortest route, with the reagent and condition for every step.
|
|
217
|
+
*
|
|
218
|
+
* This is the difference between a distance and an answer. A lesson claiming "alkene to carboxylic
|
|
219
|
+
* acid in three steps" can now be checked against the route the map actually gives, and the steps
|
|
220
|
+
* it returns are the marks.
|
|
221
|
+
*
|
|
222
|
+
* ## Why a plain breadth-first search was not enough
|
|
223
|
+
*
|
|
224
|
+
* Alkene to carboxylic acid has TWO routes of three steps. One hydrates and oxidises twice and
|
|
225
|
+
* keeps the carbon skeleton. The other goes through the nitrile, and a nitrile road adds a carbon,
|
|
226
|
+
* so it arrives at an acid ONE CARBON LONGER than the target. A search that returned whichever it
|
|
227
|
+
* reached first would hand a learner the wrong molecule half the time, and would do it silently,
|
|
228
|
+
* because both routes are three steps and both are real chemistry.
|
|
229
|
+
*
|
|
230
|
+
* So among equally short routes the chain-preserving one wins. A question that wants the longer
|
|
231
|
+
* chain says so, and {@link allRoutes} still has both.
|
|
232
|
+
*/
|
|
233
|
+
function findRoute(from, to) {
|
|
234
|
+
if (from === to) return asRoute([from]);
|
|
235
|
+
const depth = shortestSteps(from, to);
|
|
236
|
+
if (depth === void 0) return void 0;
|
|
237
|
+
return allRoutes(from, to, depth).filter((r) => r.steps.length === depth).sort((a, b) => Number(a.changesChain) - Number(b.changesChain))[0];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Every route from one group to another within a step budget, shortest first.
|
|
241
|
+
*
|
|
242
|
+
* Exists because the shortest route is not always the best one, which is the single most arguable
|
|
243
|
+
* point in the topic. Esterifying an acid directly is one step and an equilibrium that stops well
|
|
244
|
+
* short; going via the acyl chloride is two steps and goes to completion. Both routes have to be
|
|
245
|
+
* on the table for that to be a judgement rather than a rule.
|
|
246
|
+
*
|
|
247
|
+
* `maxSteps` is capped at six, because the map's diameter is smaller than that and an uncapped
|
|
248
|
+
* enumeration on a cyclic graph does not terminate usefully.
|
|
249
|
+
*/
|
|
250
|
+
function allRoutes(from, to, maxSteps = 4) {
|
|
251
|
+
const limit = Math.min(maxSteps, 6);
|
|
252
|
+
const found = [];
|
|
253
|
+
const walk = (path, visited) => {
|
|
254
|
+
const head = path[path.length - 1];
|
|
255
|
+
if (head === to && path.length > 1) {
|
|
256
|
+
found.push(asRoute([...path]));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (path.length - 1 >= limit) return;
|
|
260
|
+
for (const edge of edgesFrom(head)) {
|
|
261
|
+
if (visited.has(edge.to)) continue;
|
|
262
|
+
visited.add(edge.to);
|
|
263
|
+
path.push(edge.to);
|
|
264
|
+
walk(path, visited);
|
|
265
|
+
path.pop();
|
|
266
|
+
visited.delete(edge.to);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
walk([from], new Set([from]));
|
|
270
|
+
return found.sort((a, b) => a.steps.length - b.steps.length);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The reactions that change the number of carbon atoms.
|
|
274
|
+
*
|
|
275
|
+
* The most useful fact in the topic and the one that is invisible in a list: to reach a target with
|
|
276
|
+
* more carbons than the starting material, a route MUST pass through one of these.
|
|
277
|
+
*/
|
|
278
|
+
const chainChangingEdges = () => ROUTE_EDGES.filter((edge) => edge.changesChain === true);
|
|
279
|
+
|
|
280
|
+
//#endregion
|
|
281
|
+
export { GROUP_LABELS, GROUP_NODES, ROUTE_EDGES, allRoutes, chainChangingEdges, edgeBetween, edgesAlong, edgesFrom, findRoute, shortestSteps };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Centre, CentreOptions } from "./species.mjs";
|
|
2
|
+
import { LewisCount } from "./lewis.mjs";
|
|
3
|
+
import { Vec3 } from "../core/vec3.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/chem/vsepr.d.ts
|
|
6
|
+
/** The arrangement of ALL regions, lone pairs included. */
|
|
7
|
+
type ElectronGeometry = 'linear' | 'trigonal planar' | 'tetrahedral' | 'trigonal bipyramidal' | 'octahedral';
|
|
8
|
+
/**
|
|
9
|
+
* The directions the regions point, for each steric number.
|
|
10
|
+
*
|
|
11
|
+
* Real coordinates, so the same numbers serve a flat diagram and a three-dimensional one. The
|
|
12
|
+
* tetrahedron is built from alternate corners of a cube, which is where its angle comes from.
|
|
13
|
+
*/
|
|
14
|
+
declare function directionsFor(stericNumber: number): Vec3[];
|
|
15
|
+
/**
|
|
16
|
+
* The angle between two directions, in DEGREES.
|
|
17
|
+
*
|
|
18
|
+
* Degrees rather than radians because every bond angle a chemist quotes is in
|
|
19
|
+
* degrees. The kernel's `angleBetween3` returns radians, as a maths kernel
|
|
20
|
+
* should; this is the one conversion, in the one place the unit is a
|
|
21
|
+
* convention of the subject rather than of the arithmetic.
|
|
22
|
+
*/
|
|
23
|
+
declare const angleBetween: (a: Vec3, b: Vec3) => number;
|
|
24
|
+
/**
|
|
25
|
+
* Choose which positions the lone pairs take, by least repulsion.
|
|
26
|
+
*
|
|
27
|
+
* Every combination is tried and ranked on the count of close contacts, worst kind first: lone pair
|
|
28
|
+
* against lone pair, then lone pair against bond pair, then bond against bond. The first arrangement
|
|
29
|
+
* that beats the rest is returned, which is how the equatorial rule and the opposite-corners rule
|
|
30
|
+
* fall out instead of being stated.
|
|
31
|
+
*/
|
|
32
|
+
declare function placeLonePairs(stericNumber: number, lonePairs: number): number[];
|
|
33
|
+
/** One direction out of the central atom: either a bond to something, or a lone pair. */
|
|
34
|
+
interface Region {
|
|
35
|
+
readonly direction: Vec3;
|
|
36
|
+
readonly kind: 'bond' | 'lone pair';
|
|
37
|
+
/** The element at the far end, for a bond. */
|
|
38
|
+
readonly element?: string;
|
|
39
|
+
}
|
|
40
|
+
interface MolecularShape {
|
|
41
|
+
readonly centre: Centre;
|
|
42
|
+
readonly count: LewisCount;
|
|
43
|
+
/** The arrangement of every region, lone pairs included. */
|
|
44
|
+
readonly electronGeometry: ElectronGeometry;
|
|
45
|
+
/** The shape, which is the arrangement with the lone pairs removed. */
|
|
46
|
+
readonly shape: string;
|
|
47
|
+
/** Hybridisation implied by the steric number. */
|
|
48
|
+
readonly hybridisation: string;
|
|
49
|
+
/**
|
|
50
|
+
* The angle the arrangement would have with no lone pairs, in degrees.
|
|
51
|
+
*
|
|
52
|
+
* Geometry, so it is exact: 109.47 for a tetrahedron, not 109.5. Two values where the arrangement
|
|
53
|
+
* has two distinct angles, as a trigonal bipyramid does.
|
|
54
|
+
*/
|
|
55
|
+
readonly idealAngle: readonly number[];
|
|
56
|
+
/** Every direction out of the central atom, bonds and lone pairs together. */
|
|
57
|
+
readonly regions: readonly Region[];
|
|
58
|
+
/** Just the bonds, which is what the shape is named after. */
|
|
59
|
+
readonly bondDirections: readonly Vec3[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Work out the shape of a species written as a formula.
|
|
63
|
+
*
|
|
64
|
+
* `shapeOf('XeF4')`, `shapeOf('NH4+')`, `shapeOf('SO4^2-')`, `shapeOf('SF4')`.
|
|
65
|
+
*/
|
|
66
|
+
declare function shapeOf(formula: string, options?: CentreOptions): MolecularShape;
|
|
67
|
+
/**
|
|
68
|
+
* Bond angles that have been measured, in degrees.
|
|
69
|
+
*
|
|
70
|
+
* Kept apart from the ideal angles because they are a different kind of thing. VSEPR predicts that
|
|
71
|
+
* a lone pair squeezes the remaining angle; it does not predict by how much, and the numbers below
|
|
72
|
+
* are experimental values from standard structural data, not outputs of the model.
|
|
73
|
+
*
|
|
74
|
+
* They are worth having because the pattern in them is the lesson: ammonia at 107.0 and phosphine
|
|
75
|
+
* at 93.5 are the same shape with the same number of lone pairs, and the angle collapses down the
|
|
76
|
+
* group, which is something the model alone would never suggest.
|
|
77
|
+
*/
|
|
78
|
+
declare const OBSERVED_ANGLES: Readonly<Record<string, number>>;
|
|
79
|
+
//#endregion
|
|
80
|
+
export { ElectronGeometry, MolecularShape, OBSERVED_ANGLES, Region, angleBetween, directionsFor, placeLonePairs, shapeOf };
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { angleBetween3, unit3, v3 } from "../core/vec3.mjs";
|
|
2
|
+
import { parseCentre } from "./species.mjs";
|
|
3
|
+
import { lewisCount } from "./lewis.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/chem/vsepr.ts
|
|
6
|
+
/**
|
|
7
|
+
* The shape of a molecule, from the electron budget.
|
|
8
|
+
*
|
|
9
|
+
* ## One idea, applied consistently
|
|
10
|
+
*
|
|
11
|
+
* Regions of electron density around an atom get as far from each other as they can. Count the
|
|
12
|
+
* regions (that is the steric number) and their arrangement follows: two go opposite, three make a
|
|
13
|
+
* triangle, four a tetrahedron, five a trigonal bipyramid, six an octahedron. The SHAPE is then
|
|
14
|
+
* what is left when the lone pairs are made invisible, because a shape is the positions of atoms
|
|
15
|
+
* and a lone pair is not an atom. That is the whole of VSEPR, and it is why ammonia and methane
|
|
16
|
+
* have the same arrangement and different shapes.
|
|
17
|
+
*
|
|
18
|
+
* ## Where the lone pairs sit is DERIVED, not looked up
|
|
19
|
+
*
|
|
20
|
+
* Textbooks state that a lone pair in a trigonal bipyramid goes equatorial, and that two lone pairs
|
|
21
|
+
* in an octahedron go opposite each other. Both are usually given as facts to remember. They are
|
|
22
|
+
* neither arbitrary nor separate: they are what you get by trying every arrangement and keeping the
|
|
23
|
+
* one with the least repulsion, ranked the way Gillespie ranks it:
|
|
24
|
+
*
|
|
25
|
+
* lone pair / lone pair is worse than
|
|
26
|
+
* lone pair / bond pair is worse than
|
|
27
|
+
* bond pair / bond pair
|
|
28
|
+
*
|
|
29
|
+
* because a lone pair is held by one nucleus instead of two and so spreads out further. Only the
|
|
30
|
+
* near-90-degree contacts matter; anything at 120 degrees or more is already far enough away to
|
|
31
|
+
* ignore, which is why the comparison is a count and not an integral.
|
|
32
|
+
*
|
|
33
|
+
* {@link placeLonePairs} does that search. So a lone pair in a trigonal bipyramid comes out
|
|
34
|
+
* equatorial here because an equatorial position has two neighbours at 90 degrees and an axial one
|
|
35
|
+
* has three, and a learner can be shown the losing arrangement beside the winning one.
|
|
36
|
+
*
|
|
37
|
+
* ## Ideal angles are geometry; real angles are measurements
|
|
38
|
+
*
|
|
39
|
+
* The tetrahedral angle is arccos(-1/3), which is exact and is computed. The 104.5 degrees in water
|
|
40
|
+
* is a measurement, and no amount of geometry will produce it, because how far a lone pair pushes is
|
|
41
|
+
* not something the model predicts. The two are kept apart: {@link Geometry.idealAngle} is derived,
|
|
42
|
+
* and {@link OBSERVED_ANGLES} is data with its provenance stated.
|
|
43
|
+
*
|
|
44
|
+
* Pure: no React, no pixels.
|
|
45
|
+
*/
|
|
46
|
+
const ELECTRON_GEOMETRY = {
|
|
47
|
+
2: "linear",
|
|
48
|
+
3: "trigonal planar",
|
|
49
|
+
4: "tetrahedral",
|
|
50
|
+
5: "trigonal bipyramidal",
|
|
51
|
+
6: "octahedral"
|
|
52
|
+
};
|
|
53
|
+
const HYBRIDISATION = {
|
|
54
|
+
2: "sp",
|
|
55
|
+
3: "sp²",
|
|
56
|
+
4: "sp³",
|
|
57
|
+
5: "sp³d",
|
|
58
|
+
6: "sp³d²"
|
|
59
|
+
};
|
|
60
|
+
/** The shape, which is the arrangement with the lone pairs taken away. */
|
|
61
|
+
const SHAPES = {
|
|
62
|
+
"2,0": "linear",
|
|
63
|
+
"3,0": "trigonal planar",
|
|
64
|
+
"3,1": "bent",
|
|
65
|
+
"4,0": "tetrahedral",
|
|
66
|
+
"4,1": "trigonal pyramidal",
|
|
67
|
+
"4,2": "bent",
|
|
68
|
+
"5,0": "trigonal bipyramidal",
|
|
69
|
+
"5,1": "seesaw",
|
|
70
|
+
"5,2": "T-shaped",
|
|
71
|
+
"5,3": "linear",
|
|
72
|
+
"6,0": "octahedral",
|
|
73
|
+
"6,1": "square pyramidal",
|
|
74
|
+
"6,2": "square planar",
|
|
75
|
+
"6,3": "T-shaped"
|
|
76
|
+
};
|
|
77
|
+
const unit = (x, y, z) => unit3(v3(x, y, z));
|
|
78
|
+
/**
|
|
79
|
+
* The directions the regions point, for each steric number.
|
|
80
|
+
*
|
|
81
|
+
* Real coordinates, so the same numbers serve a flat diagram and a three-dimensional one. The
|
|
82
|
+
* tetrahedron is built from alternate corners of a cube, which is where its angle comes from.
|
|
83
|
+
*/
|
|
84
|
+
function directionsFor(stericNumber) {
|
|
85
|
+
switch (stericNumber) {
|
|
86
|
+
case 2: return [unit(1, 0, 0), unit(-1, 0, 0)];
|
|
87
|
+
case 3: return [
|
|
88
|
+
0,
|
|
89
|
+
1,
|
|
90
|
+
2
|
|
91
|
+
].map((k) => unit(Math.cos(2 * Math.PI * k / 3), Math.sin(2 * Math.PI * k / 3), 0));
|
|
92
|
+
case 4: return [
|
|
93
|
+
unit(1, 1, 1),
|
|
94
|
+
unit(1, -1, -1),
|
|
95
|
+
unit(-1, 1, -1),
|
|
96
|
+
unit(-1, -1, 1)
|
|
97
|
+
];
|
|
98
|
+
case 5: return [
|
|
99
|
+
...[
|
|
100
|
+
0,
|
|
101
|
+
1,
|
|
102
|
+
2
|
|
103
|
+
].map((k) => unit(Math.cos(2 * Math.PI * k / 3), Math.sin(2 * Math.PI * k / 3), 0)),
|
|
104
|
+
unit(0, 0, 1),
|
|
105
|
+
unit(0, 0, -1)
|
|
106
|
+
];
|
|
107
|
+
case 6: return [
|
|
108
|
+
unit(1, 0, 0),
|
|
109
|
+
unit(-1, 0, 0),
|
|
110
|
+
unit(0, 1, 0),
|
|
111
|
+
unit(0, -1, 0),
|
|
112
|
+
unit(0, 0, 1),
|
|
113
|
+
unit(0, 0, -1)
|
|
114
|
+
];
|
|
115
|
+
default: throw new Error(`steric number ${stericNumber} is outside the range this engine describes (2 to 6)`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The angle between two directions, in DEGREES.
|
|
120
|
+
*
|
|
121
|
+
* Degrees rather than radians because every bond angle a chemist quotes is in
|
|
122
|
+
* degrees. The kernel's `angleBetween3` returns radians, as a maths kernel
|
|
123
|
+
* should; this is the one conversion, in the one place the unit is a
|
|
124
|
+
* convention of the subject rather than of the arithmetic.
|
|
125
|
+
*/
|
|
126
|
+
const angleBetween = (a, b) => angleBetween3(a, b) * 180 / Math.PI;
|
|
127
|
+
/** Contacts close enough to matter. Anything at 120 degrees or more is already far apart. */
|
|
128
|
+
const CLOSE = 100;
|
|
129
|
+
/**
|
|
130
|
+
* Choose which positions the lone pairs take, by least repulsion.
|
|
131
|
+
*
|
|
132
|
+
* Every combination is tried and ranked on the count of close contacts, worst kind first: lone pair
|
|
133
|
+
* against lone pair, then lone pair against bond pair, then bond against bond. The first arrangement
|
|
134
|
+
* that beats the rest is returned, which is how the equatorial rule and the opposite-corners rule
|
|
135
|
+
* fall out instead of being stated.
|
|
136
|
+
*/
|
|
137
|
+
function placeLonePairs(stericNumber, lonePairs) {
|
|
138
|
+
const directions = directionsFor(stericNumber);
|
|
139
|
+
if (lonePairs === 0) return [];
|
|
140
|
+
if (lonePairs > stericNumber) throw new Error(`cannot place ${lonePairs} lone pairs in ${stericNumber} positions`);
|
|
141
|
+
const score = (chosen) => {
|
|
142
|
+
const isLone = (i) => chosen.includes(i);
|
|
143
|
+
let loneLone = 0;
|
|
144
|
+
let loneBond = 0;
|
|
145
|
+
let bondBond = 0;
|
|
146
|
+
for (let i = 0; i < directions.length; i += 1) for (let j = i + 1; j < directions.length; j += 1) {
|
|
147
|
+
if (angleBetween(directions[i], directions[j]) >= CLOSE) continue;
|
|
148
|
+
const lones = Number(isLone(i)) + Number(isLone(j));
|
|
149
|
+
if (lones === 2) loneLone += 1;
|
|
150
|
+
else if (lones === 1) loneBond += 1;
|
|
151
|
+
else bondBond += 1;
|
|
152
|
+
}
|
|
153
|
+
return [
|
|
154
|
+
loneLone,
|
|
155
|
+
loneBond,
|
|
156
|
+
bondBond
|
|
157
|
+
];
|
|
158
|
+
};
|
|
159
|
+
const combinations = [];
|
|
160
|
+
const build = (start, picked) => {
|
|
161
|
+
if (picked.length === lonePairs) {
|
|
162
|
+
combinations.push([...picked]);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
for (let i = start; i < stericNumber; i += 1) build(i + 1, [...picked, i]);
|
|
166
|
+
};
|
|
167
|
+
build(0, []);
|
|
168
|
+
let best = combinations[0];
|
|
169
|
+
let bestScore = score(best);
|
|
170
|
+
for (const candidate of combinations.slice(1)) {
|
|
171
|
+
const candidateScore = score(candidate);
|
|
172
|
+
const better = candidateScore.findIndex((value, k) => value !== bestScore[k]);
|
|
173
|
+
if (better !== -1 && candidateScore[better] < bestScore[better]) {
|
|
174
|
+
best = candidate;
|
|
175
|
+
bestScore = candidateScore;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return best;
|
|
179
|
+
}
|
|
180
|
+
/** Ideal angles for an arrangement, computed from the directions themselves. */
|
|
181
|
+
function idealAnglesFor(stericNumber) {
|
|
182
|
+
const directions = directionsFor(stericNumber);
|
|
183
|
+
const angles = /* @__PURE__ */ new Set();
|
|
184
|
+
for (let i = 0; i < directions.length; i += 1) for (let j = i + 1; j < directions.length; j += 1) angles.add(Math.round(angleBetween(directions[i], directions[j]) * 100) / 100);
|
|
185
|
+
return [...angles].sort((a, b) => a - b).filter((a) => a < 180 || angles.size === 1);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Work out the shape of a species written as a formula.
|
|
189
|
+
*
|
|
190
|
+
* `shapeOf('XeF4')`, `shapeOf('NH4+')`, `shapeOf('SO4^2-')`, `shapeOf('SF4')`.
|
|
191
|
+
*/
|
|
192
|
+
function shapeOf(formula, options = {}) {
|
|
193
|
+
const centre = parseCentre(formula, options);
|
|
194
|
+
const count = lewisCount(centre);
|
|
195
|
+
if (count.impossible) throw new Error(count.impossible);
|
|
196
|
+
const { stericNumber, lonePairs } = count;
|
|
197
|
+
const electronGeometry = ELECTRON_GEOMETRY[stericNumber];
|
|
198
|
+
const shape = SHAPES[`${stericNumber},${lonePairs}`];
|
|
199
|
+
if (!electronGeometry || !shape) throw new Error(`${formula} works out to ${stericNumber} regions with ${lonePairs} lone pairs, which is outside the range this engine describes`);
|
|
200
|
+
const directions = directionsFor(stericNumber);
|
|
201
|
+
const lonePositions = new Set(placeLonePairs(stericNumber, lonePairs));
|
|
202
|
+
const bondedElements = centre.bonded.flatMap((l) => Array(l.count).fill(l.element));
|
|
203
|
+
let next = 0;
|
|
204
|
+
const regions = directions.map((direction, index) => lonePositions.has(index) ? {
|
|
205
|
+
direction,
|
|
206
|
+
kind: "lone pair"
|
|
207
|
+
} : {
|
|
208
|
+
direction,
|
|
209
|
+
kind: "bond",
|
|
210
|
+
element: bondedElements[next++] ?? centre.central
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
centre,
|
|
214
|
+
count,
|
|
215
|
+
electronGeometry,
|
|
216
|
+
shape,
|
|
217
|
+
hybridisation: HYBRIDISATION[stericNumber],
|
|
218
|
+
idealAngle: idealAnglesFor(stericNumber),
|
|
219
|
+
regions,
|
|
220
|
+
bondDirections: regions.filter((r) => r.kind === "bond").map((r) => r.direction)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Bond angles that have been measured, in degrees.
|
|
225
|
+
*
|
|
226
|
+
* Kept apart from the ideal angles because they are a different kind of thing. VSEPR predicts that
|
|
227
|
+
* a lone pair squeezes the remaining angle; it does not predict by how much, and the numbers below
|
|
228
|
+
* are experimental values from standard structural data, not outputs of the model.
|
|
229
|
+
*
|
|
230
|
+
* They are worth having because the pattern in them is the lesson: ammonia at 107.0 and phosphine
|
|
231
|
+
* at 93.5 are the same shape with the same number of lone pairs, and the angle collapses down the
|
|
232
|
+
* group, which is something the model alone would never suggest.
|
|
233
|
+
*/
|
|
234
|
+
const OBSERVED_ANGLES = {
|
|
235
|
+
CH4: 109.5,
|
|
236
|
+
NH3: 107,
|
|
237
|
+
H2O: 104.5,
|
|
238
|
+
PH3: 93.5,
|
|
239
|
+
H2S: 92.1,
|
|
240
|
+
NF3: 102.2,
|
|
241
|
+
SO2: 119,
|
|
242
|
+
BF3: 120,
|
|
243
|
+
CO2: 180
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
export { OBSERVED_ANGLES, angleBetween, directionsFor, placeLonePairs, shapeOf };
|