@classytic/labs 0.1.0 → 0.2.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/dist/_virtual/_rolldown/runtime.mjs +18 -0
- package/dist/biology/enzyme-rate/preset.mjs +5 -5
- package/dist/biology/genetic-cross/core.d.mts +7 -7
- package/dist/biology/genetic-cross/core.mjs +4 -4
- package/dist/biology/genetic-cross/grid.mjs +132 -123
- package/dist/biology/genetic-cross/preset.mjs +4 -4
- package/dist/biology/genetic-cross/sex-linked.mjs +7 -7
- package/dist/biology/photosynthesis-factors/preset.mjs +8 -8
- package/dist/biology/respiration/preset.mjs +4 -4
- package/dist/biology/sequence/central-dogma.mjs +5 -5
- package/dist/biology/sequence/core.d.mts +6 -6
- package/dist/biology/sequence/core.mjs +1 -1
- package/dist/biology/sequence/preset.mjs +6 -6
- package/dist/blocks/authoring.mjs +5 -5
- package/dist/blocks/biology.d.mts +2 -2
- package/dist/blocks/biology.mjs +5 -5
- package/dist/blocks/chem.d.mts +2 -2
- package/dist/blocks/chem.mjs +13 -13
- package/dist/blocks/circuits.d.mts +293 -4
- package/dist/blocks/circuits.mjs +874 -9
- package/dist/blocks/discrete.d.mts +30 -2
- package/dist/blocks/discrete.mjs +74 -4
- package/dist/blocks/economics.mjs +1 -1
- package/dist/blocks/geography.mjs +1 -1
- package/dist/blocks/geometry.mjs +3 -3
- package/dist/blocks/ict.d.mts +90 -1
- package/dist/blocks/ict.mjs +217 -7
- package/dist/blocks/index.d.mts +3064 -657
- package/dist/blocks/index.mjs +9 -9
- package/dist/blocks/lab-block.mjs +5 -5
- package/dist/blocks/lab-config.d.mts +1 -1
- package/dist/blocks/lab-config.mjs +4 -4
- package/dist/blocks/lab-gallery.d.mts +1 -1
- package/dist/blocks/lab-gallery.mjs +2 -2
- package/dist/blocks/language.d.mts +6 -6
- package/dist/blocks/language.mjs +12 -12
- package/dist/blocks/lesson.mjs +4 -4
- package/dist/blocks/math.d.mts +577 -11
- package/dist/blocks/math.mjs +1648 -14
- package/dist/blocks/ml.d.mts +45 -1
- package/dist/blocks/ml.mjs +80 -6
- package/dist/blocks/physics.d.mts +9 -4
- package/dist/blocks/physics.mjs +75 -28
- package/dist/blocks/statistics.mjs +1 -1
- package/dist/build/CircuitEditor.d.mts +14 -0
- package/dist/build/CircuitEditor.mjs +787 -0
- package/dist/build/CircuitPlayer.d.mts +19 -0
- package/dist/build/CircuitPlayer.mjs +53 -0
- package/dist/build/CircuitScene.d.mts +61 -0
- package/dist/build/CircuitScene.mjs +383 -0
- package/dist/build/contract.d.mts +116 -0
- package/dist/build/editor-ops.d.mts +65 -0
- package/dist/build/editor-ops.mjs +306 -0
- package/dist/build/editor-ui.mjs +121 -0
- package/dist/build/flow.d.mts +14 -0
- package/dist/build/flow.mjs +119 -0
- package/dist/build/index.d.mts +10 -0
- package/dist/build/index.mjs +10 -0
- package/dist/build/parts/ammeter.d.mts +1 -0
- package/dist/build/parts/ammeter.mjs +32 -0
- package/dist/build/parts/bulb.d.mts +1 -0
- package/dist/build/parts/bulb.mjs +50 -0
- package/dist/build/parts/capacitor.d.mts +1 -0
- package/dist/build/parts/capacitor.mjs +26 -0
- package/dist/build/parts/cell.d.mts +1 -0
- package/dist/build/parts/cell.mjs +40 -0
- package/dist/build/parts/diode.d.mts +1 -0
- package/dist/build/parts/diode.mjs +33 -0
- package/dist/build/parts/index.d.mts +5 -0
- package/dist/build/parts/index.mjs +43 -0
- package/dist/build/parts/mosfet.d.mts +1 -0
- package/dist/build/parts/mosfet.mjs +95 -0
- package/dist/build/parts/node.d.mts +1 -0
- package/dist/build/parts/node.mjs +32 -0
- package/dist/build/parts/resistor.d.mts +1 -0
- package/dist/build/parts/resistor.mjs +49 -0
- package/dist/build/parts/shared.mjs +42 -0
- package/dist/build/parts/switch.d.mts +1 -0
- package/dist/build/parts/switch.mjs +34 -0
- package/dist/build/parts.d.mts +1 -0
- package/dist/build/registry.d.mts +9 -0
- package/dist/build/registry.mjs +15 -0
- package/dist/build/solve.d.mts +8 -0
- package/dist/build/solve.mjs +0 -0
- package/dist/catalog/registry.d.mts +2 -2
- package/dist/catalog/templates.d.mts +1 -1
- package/dist/catalog/templates.mjs +6 -6
- package/dist/chem/battery.mjs +1 -1
- package/dist/chem/bohr-atom.mjs +4 -4
- package/dist/chem/diffusion/preset.mjs +4 -4
- package/dist/chem/electrochem/preset.mjs +5 -5
- package/dist/chem/equilibrium/preset.d.mts +1 -1
- package/dist/chem/equilibrium/preset.mjs +6 -6
- package/dist/chem/gas-box/preset.mjs +6 -6
- package/dist/chem/kinetics/preset.mjs +6 -6
- package/dist/chem/periodic-trends/preset.mjs +7 -7
- package/dist/chem/reaction-lab.mjs +3 -3
- package/dist/chem/reaction-profile.mjs +4 -4
- package/dist/chem/solution/dilution.mjs +6 -6
- package/dist/chem/solution/field.d.mts +2 -2
- package/dist/chem/solution/field.mjs +2 -2
- package/dist/chem/solution/solution-box.mjs +7 -7
- package/dist/chem/stoichiometry/preset.mjs +5 -5
- package/dist/chem/titration/preset.mjs +5 -5
- package/dist/circuits/ac-dc/asset.mjs +1 -1
- package/dist/circuits/ac-dc/preset.mjs +2 -2
- package/dist/circuits/brownout/index.d.mts +1 -0
- package/dist/circuits/brownout/preset.d.mts +22 -0
- package/dist/circuits/brownout/preset.mjs +399 -0
- package/dist/circuits/capacitor-leak/preset.mjs +13 -8
- package/dist/circuits/circuit/asset.mjs +71 -14
- package/dist/circuits/circuit/preset.mjs +23 -5
- package/dist/circuits/circuit-builder.mjs +60 -132
- package/dist/circuits/circuit-lab.mjs +60 -40
- package/dist/circuits/cmos-gate/index.d.mts +1 -0
- package/dist/circuits/cmos-gate/preset.d.mts +75 -0
- package/dist/circuits/cmos-gate/preset.mjs +1491 -0
- package/dist/circuits/diode/index.d.mts +1 -0
- package/dist/circuits/diode/preset.d.mts +25 -0
- package/dist/circuits/diode/preset.mjs +266 -0
- package/dist/circuits/index.d.mts +7 -1
- package/dist/circuits/index.mjs +7 -1
- package/dist/circuits/rc-charging/index.d.mts +1 -0
- package/dist/circuits/rc-charging/preset.d.mts +27 -0
- package/dist/circuits/rc-charging/preset.mjs +329 -0
- package/dist/circuits/semiconductor/index.d.mts +1 -0
- package/dist/circuits/semiconductor/preset.d.mts +102 -0
- package/dist/circuits/semiconductor/preset.mjs +1855 -0
- package/dist/circuits/transistor/index.d.mts +1 -0
- package/dist/circuits/transistor/preset.d.mts +27 -0
- package/dist/circuits/transistor/preset.mjs +329 -0
- package/dist/commerce/accounting/core.d.mts +1 -1
- package/dist/commerce/accounting/equation-balance.mjs +5 -5
- package/dist/commerce/accounting/journal-poster.mjs +8 -8
- package/dist/commerce/accounting/statement-sorter.mjs +4 -4
- package/dist/commerce/economics/core.d.mts +2 -2
- package/dist/commerce/economics/core.mjs +1 -1
- package/dist/commerce/economics/demand-shift-vs-move.mjs +6 -6
- package/dist/commerce/economics/elasticity-revenue.mjs +6 -6
- package/dist/commerce/economics/market-equilibrium.mjs +5 -5
- package/dist/core/easing.d.mts +3 -3
- package/dist/core/easing.mjs +2 -2
- package/dist/core/numeric.d.mts +3 -3
- package/dist/core/numeric.mjs +2 -2
- package/dist/core/reactive.d.mts +1 -1
- package/dist/core/reactive.mjs +1 -1
- package/dist/core/rng.d.mts +2 -2
- package/dist/core/rng.mjs +1 -1
- package/dist/core/tex.mjs +2 -2
- package/dist/core/timeline.d.mts +1 -1
- package/dist/core/timeline.mjs +1 -1
- package/dist/core/util.mjs +11 -1
- package/dist/discrete/arrangements/preset.mjs +6 -14
- package/dist/discrete/bayes/preset.mjs +43 -35
- package/dist/discrete/binomial/preset.mjs +2 -2
- package/dist/discrete/combination-studio/figure.d.mts +6 -0
- package/dist/discrete/combination-studio/figure.mjs +159 -0
- package/dist/discrete/combination-studio/index.d.mts +2 -0
- package/dist/discrete/combination-studio/preset.d.mts +44 -0
- package/dist/discrete/combination-studio/preset.mjs +440 -0
- package/dist/discrete/core/combinatorics.d.mts +23 -3
- package/dist/discrete/core/combinatorics.mjs +38 -3
- package/dist/discrete/core/pigeonhole.d.mts +1 -1
- package/dist/discrete/core/pigeonhole.mjs +1 -1
- package/dist/discrete/core/probability.d.mts +3 -3
- package/dist/discrete/core/probability.mjs +3 -3
- package/dist/discrete/core/sets.d.mts +1 -1
- package/dist/discrete/counting-slots/preset.mjs +8 -8
- package/dist/discrete/counting-tree/preset.mjs +65 -8
- package/dist/discrete/expected-value/preset.mjs +13 -13
- package/dist/discrete/hypergeometric/preset.mjs +48 -15
- package/dist/discrete/index.d.mts +4 -2
- package/dist/discrete/index.mjs +4 -2
- package/dist/discrete/karnaugh/preset.mjs +26 -20
- package/dist/discrete/lln/preset.mjs +44 -3
- package/dist/discrete/logic-circuit/preset.mjs +2 -2
- package/dist/discrete/monte-carlo/preset.d.mts +1 -1
- package/dist/discrete/monte-carlo/preset.mjs +14 -14
- package/dist/discrete/monty-hall/preset.mjs +6 -6
- package/dist/discrete/outcome-builder/preset.mjs +3 -3
- package/dist/discrete/pascal/preset.mjs +37 -7
- package/dist/discrete/rules.d.mts +20 -0
- package/dist/discrete/rules.mjs +264 -0
- package/dist/discrete/sample-space/preset.mjs +4 -4
- package/dist/discrete/selection/preset.mjs +7 -13
- package/dist/discrete/truth-table/preset.mjs +32 -9
- package/dist/discrete/venn/preset.mjs +71 -48
- package/dist/geography/cycle-lab/preset.mjs +17 -10
- package/dist/geography/cycles.d.mts +3 -3
- package/dist/geography/cycles.mjs +3 -3
- package/dist/geometry/board/preset.d.mts +2 -0
- package/dist/geometry/board/preset.mjs +11 -15
- package/dist/geometry/builder.mjs +3 -3
- package/dist/ict/number-systems/base-odometer.mjs +4 -4
- package/dist/ict/number-systems/bit-grouper.d.mts +1 -1
- package/dist/ict/number-systems/bit-grouper.mjs +4 -4
- package/dist/ict/number-systems/place-value-dial.d.mts +1 -1
- package/dist/ict/number-systems/place-value-dial.mjs +3 -3
- package/dist/ict/number-systems/wheel.d.mts +3 -3
- package/dist/ict/number-systems/wheel.mjs +4 -4
- package/dist/kit/annotate.mjs +3 -3
- package/dist/kit/answer-check.d.mts +2 -2
- package/dist/kit/answer-check.mjs +34 -6
- package/dist/kit/ask.d.mts +19 -0
- package/dist/kit/ask.mjs +46 -0
- package/dist/kit/bar-strip.mjs +53 -0
- package/dist/kit/calc.d.mts +33 -0
- package/dist/kit/calc.mjs +27 -0
- package/dist/kit/carrier-engine.mjs +147 -0
- package/dist/kit/clue-scene.d.mts +86 -0
- package/dist/kit/clue-scene.mjs +424 -0
- package/dist/kit/cluster.d.mts +30 -0
- package/dist/kit/cluster.mjs +81 -0
- package/dist/kit/controls.mjs +11 -5
- package/dist/kit/coords.d.mts +88 -0
- package/dist/kit/coords.mjs +192 -0
- package/dist/kit/data-scene.d.mts +27 -0
- package/dist/kit/data-scene.mjs +199 -0
- package/dist/kit/diagram.mjs +111 -50
- package/dist/kit/electronics.mjs +552 -60
- package/dist/kit/expr-analysis.d.mts +18 -6
- package/dist/kit/expr-analysis.mjs +82 -28
- package/dist/kit/frame.d.mts +3 -3
- package/dist/kit/frame.mjs +25 -6
- package/dist/kit/gameshow.mjs +2 -2
- package/dist/kit/index.d.mts +14 -2
- package/dist/kit/index.mjs +13 -1
- package/dist/kit/logic-gates.mjs +246 -47
- package/dist/kit/palette.mjs +26 -0
- package/dist/kit/pedagogy.d.mts +4 -4
- package/dist/kit/pedagogy.mjs +21 -17
- package/dist/kit/play.d.mts +1 -1
- package/dist/kit/play.mjs +3 -3
- package/dist/kit/predict.d.mts +55 -0
- package/dist/kit/predict.mjs +146 -0
- package/dist/kit/receipt.d.mts +29 -0
- package/dist/kit/receipt.mjs +94 -0
- package/dist/kit/rich.mjs +2 -2
- package/dist/kit/rule.d.mts +59 -0
- package/dist/kit/rule.mjs +215 -0
- package/dist/kit/scale.mjs +1 -1
- package/dist/kit/scene-library.d.mts +22 -0
- package/dist/kit/scene-library.mjs +311 -0
- package/dist/kit/scene-studio.d.mts +14 -0
- package/dist/kit/scene-studio.mjs +338 -0
- package/dist/kit/scenes.d.mts +31 -0
- package/dist/kit/scenes.mjs +569 -0
- package/dist/kit/slot-fill.d.mts +61 -0
- package/dist/kit/slot-fill.mjs +207 -0
- package/dist/kit/solve.d.mts +21 -0
- package/dist/kit/solve.mjs +75 -0
- package/dist/kit/space.mjs +5 -5
- package/dist/kit/steps.mjs +2 -2
- package/dist/kit/vessel.d.mts +69 -0
- package/dist/kit/vessel.mjs +191 -0
- package/dist/language/agreement/preset.mjs +2 -2
- package/dist/language/article-lens/preset.d.mts +1 -1
- package/dist/language/article-lens/preset.mjs +8 -8
- package/dist/language/deck.d.mts +3 -3
- package/dist/language/deck.mjs +1 -1
- package/dist/language/preposition-scene/preset.d.mts +2 -2
- package/dist/language/preposition-scene/preset.mjs +5 -5
- package/dist/language/sentence-builder/preset.mjs +5 -5
- package/dist/language/transform/preset.d.mts +2 -2
- package/dist/language/transform/preset.mjs +5 -5
- package/dist/language/ui.d.mts +3 -3
- package/dist/language/ui.mjs +4 -4
- package/dist/language/word-match/preset.mjs +3 -3
- package/dist/logic/LogicBuildLab.d.mts +22 -0
- package/dist/logic/LogicBuildLab.mjs +170 -0
- package/dist/logic/LogicEditScene.d.mts +31 -0
- package/dist/logic/LogicEditScene.mjs +276 -0
- package/dist/logic/LogicEditor.d.mts +14 -0
- package/dist/logic/LogicEditor.mjs +413 -0
- package/dist/logic/LogicScene.d.mts +32 -0
- package/dist/logic/LogicScene.mjs +277 -0
- package/dist/logic/contract.d.mts +72 -0
- package/dist/logic/display.d.mts +24 -0
- package/dist/logic/display.mjs +127 -0
- package/dist/logic/edit-ops.d.mts +44 -0
- package/dist/logic/edit-ops.mjs +177 -0
- package/dist/logic/evaluate.d.mts +11 -0
- package/dist/logic/evaluate.mjs +93 -0
- package/dist/logic/gates.d.mts +10 -0
- package/dist/logic/gates.mjs +73 -0
- package/dist/logic/index.d.mts +14 -0
- package/dist/logic/index.mjs +13 -0
- package/dist/logic/lab.d.mts +32 -0
- package/dist/logic/lab.mjs +160 -0
- package/dist/logic/presets.d.mts +12 -0
- package/dist/logic/presets.mjs +241 -0
- package/dist/logic/registry.d.mts +9 -0
- package/dist/logic/registry.mjs +15 -0
- package/dist/math/area-model/preset.mjs +70 -44
- package/dist/math/balance-algebra/preset.mjs +14 -5
- package/dist/math/broken-tree/index.d.mts +1 -0
- package/dist/math/broken-tree/preset.d.mts +26 -0
- package/dist/math/broken-tree/preset.mjs +435 -0
- package/dist/math/circle/index.d.mts +1 -0
- package/dist/math/circle/preset.d.mts +44 -0
- package/dist/math/circle/preset.mjs +198 -0
- package/dist/math/complex/core.d.mts +93 -0
- package/dist/math/complex/core.mjs +239 -0
- package/dist/math/complex/index.d.mts +3 -0
- package/dist/math/complex/preset.d.mts +31 -0
- package/dist/math/complex/preset.mjs +377 -0
- package/dist/math/complex/rules.d.mts +6 -0
- package/dist/math/complex/rules.mjs +121 -0
- package/dist/math/conic/index.d.mts +1 -0
- package/dist/math/conic/preset.d.mts +30 -0
- package/dist/math/conic/preset.mjs +334 -0
- package/dist/math/derivation.mjs +5 -5
- package/dist/math/derivative-explorer/preset.mjs +5 -5
- package/dist/math/domain-range/index.d.mts +1 -0
- package/dist/math/domain-range/preset.d.mts +33 -0
- package/dist/math/domain-range/preset.mjs +313 -0
- package/dist/math/fraction-bar/index.d.mts +1 -0
- package/dist/math/fraction-bar/preset.d.mts +27 -0
- package/dist/math/fraction-bar/preset.mjs +197 -0
- package/dist/math/function-machine/preset.mjs +2 -2
- package/dist/math/gradient-descent.mjs +6 -6
- package/dist/math/grapher/preset.d.mts +1 -1
- package/dist/math/grapher/preset.mjs +4 -4
- package/dist/math/harmonic-form/preset.mjs +6 -6
- package/dist/math/index.d.mts +27 -2
- package/dist/math/index.mjs +27 -2
- package/dist/math/integral-explorer/preset.mjs +2 -2
- package/dist/math/interactive/preset.mjs +3 -3
- package/dist/math/limit-explorer/preset.mjs +4 -4
- package/dist/math/linear-model/index.d.mts +1 -0
- package/dist/math/linear-model/preset.d.mts +53 -0
- package/dist/math/linear-model/preset.mjs +164 -0
- package/dist/math/linear-system/preset.mjs +4 -4
- package/dist/math/mystery-bucket/preset.mjs +6 -6
- package/dist/math/number-line/preset.mjs +3 -2
- package/dist/math/parabola/preset.mjs +4 -3
- package/dist/math/pattern/preset.mjs +7 -13
- package/dist/math/percent-bar/index.d.mts +1 -0
- package/dist/math/percent-bar/preset.d.mts +37 -0
- package/dist/math/percent-bar/preset.mjs +275 -0
- package/dist/math/poly/core.d.mts +50 -0
- package/dist/math/poly/core.mjs +268 -0
- package/dist/math/poly/index.d.mts +3 -0
- package/dist/math/poly/preset.d.mts +23 -0
- package/dist/math/poly/preset.mjs +141 -0
- package/dist/math/poly/steps.d.mts +10 -0
- package/dist/math/poly/steps.mjs +99 -0
- package/dist/math/rate-machine/index.d.mts +1 -0
- package/dist/math/rate-machine/preset.d.mts +35 -0
- package/dist/math/rate-machine/preset.mjs +281 -0
- package/dist/math/ratio-share/index.d.mts +1 -0
- package/dist/math/ratio-share/preset.d.mts +24 -0
- package/dist/math/ratio-share/preset.mjs +204 -0
- package/dist/math/receipt/index.d.mts +1 -0
- package/dist/math/receipt/preset.d.mts +22 -0
- package/dist/math/receipt/preset.mjs +137 -0
- package/dist/math/sequence-predict/index.d.mts +1 -0
- package/dist/math/sequence-predict/preset.d.mts +34 -0
- package/dist/math/sequence-predict/preset.mjs +130 -0
- package/dist/math/straight-line/index.d.mts +1 -0
- package/dist/math/straight-line/preset.d.mts +37 -0
- package/dist/math/straight-line/preset.mjs +374 -0
- package/dist/math/system-solve/index.d.mts +1 -0
- package/dist/math/system-solve/preset.d.mts +21 -0
- package/dist/math/system-solve/preset.mjs +227 -0
- package/dist/math/transform/index.d.mts +1 -0
- package/dist/math/transform/preset.d.mts +41 -0
- package/dist/math/transform/preset.mjs +343 -0
- package/dist/math/triangle-trig/preset.mjs +12 -9
- package/dist/math/trig/core.d.mts +37 -0
- package/dist/math/trig/core.mjs +150 -0
- package/dist/math/trig/diagrams.d.mts +1 -0
- package/dist/math/trig/diagrams.mjs +321 -0
- package/dist/math/trig/index.d.mts +3 -0
- package/dist/math/trig/preset.d.mts +25 -0
- package/dist/math/trig/preset.mjs +260 -0
- package/dist/math/trig/rules.d.mts +6 -0
- package/dist/math/trig/rules.mjs +159 -0
- package/dist/math/trig-explorer.mjs +4 -4
- package/dist/ml/boundary/preset.mjs +8 -3
- package/dist/ml/classifier/preset.mjs +31 -6
- package/dist/ml/kmeans/preset.mjs +4 -4
- package/dist/ml/knn/preset.mjs +47 -12
- package/dist/ml/regression/preset.mjs +36 -7
- package/dist/physics/atwood/preset.mjs +4 -4
- package/dist/physics/bullet-walls/preset.d.mts +1 -1
- package/dist/physics/bullet-walls/preset.mjs +7 -7
- package/dist/physics/carnot/preset.mjs +6 -6
- package/dist/physics/circular-motion/preset.mjs +36 -5
- package/dist/physics/collision-track/preset.mjs +35 -5
- package/dist/physics/efficiency/preset.d.mts +2 -2
- package/dist/physics/efficiency/preset.mjs +5 -5
- package/dist/physics/electric-field/preset.mjs +32 -23
- package/dist/physics/electric-flux/index.d.mts +1 -0
- package/dist/physics/electric-flux/preset.d.mts +29 -0
- package/dist/physics/electric-flux/preset.mjs +264 -0
- package/dist/physics/energy-skate/preset.mjs +5 -5
- package/dist/physics/entropy/preset.mjs +7 -7
- package/dist/physics/expansion/preset.mjs +13 -13
- package/dist/physics/exponential/asset.mjs +1 -1
- package/dist/physics/exponential/preset.mjs +4 -4
- package/dist/physics/gas-process/preset.mjs +3 -3
- package/dist/physics/gauss-law/index.d.mts +1 -0
- package/dist/physics/gauss-law/preset.d.mts +20 -0
- package/dist/physics/gauss-law/preset.mjs +286 -0
- package/dist/physics/gravitation/preset.mjs +7 -7
- package/dist/physics/gravity-drop.mjs +4 -4
- package/dist/physics/heat-transfer/preset.d.mts +1 -1
- package/dist/physics/heat-transfer/preset.mjs +10 -10
- package/dist/physics/impulse/preset.mjs +5 -5
- package/dist/physics/index.d.mts +4 -1
- package/dist/physics/index.mjs +4 -1
- package/dist/physics/kepler/preset.mjs +4 -4
- package/dist/physics/lever/preset.mjs +3 -3
- package/dist/physics/lorentz/preset.mjs +11 -11
- package/dist/physics/magnetism/preset.mjs +3 -3
- package/dist/physics/optics/preset.mjs +2 -2
- package/dist/physics/orbit-lab.mjs +5 -5
- package/dist/physics/projectile-lab.mjs +4 -4
- package/dist/physics/rain-relative/preset.mjs +5 -5
- package/dist/physics/ramp-forces/preset.mjs +5 -5
- package/dist/physics/river-boat.mjs +6 -6
- package/dist/physics/shm/preset.mjs +9 -9
- package/dist/physics/stopping-distance/preset.mjs +3 -3
- package/dist/physics/temperature-scales/preset.mjs +105 -116
- package/dist/physics/terminal-velocity/preset.mjs +5 -5
- package/dist/physics/thermal/preset.mjs +5 -5
- package/dist/physics/vector-board/preset.mjs +7 -6
- package/dist/physics/vector-scene.mjs +2 -2
- package/dist/physics/vector-types/preset.mjs +37 -3
- package/dist/physics/water-density/preset.mjs +12 -12
- package/dist/physics/waves/doppler.mjs +2 -2
- package/dist/physics/waves/preset.mjs +4 -4
- package/dist/physics/waves/ripple.mjs +6 -6
- package/dist/physics/waves/string.mjs +5 -5
- package/dist/physics/work-energy/preset.mjs +4 -4
- package/dist/physics/work-potential/index.d.mts +1 -0
- package/dist/physics/work-potential/preset.d.mts +20 -0
- package/dist/physics/work-potential/preset.mjs +276 -0
- package/dist/schemas/index.d.mts +6 -6
- package/dist/schemas/index.mjs +3 -3
- package/dist/statistics/center-spread/preset.mjs +3 -3
- package/dist/statistics/core/descriptive.d.mts +2 -2
- package/dist/statistics/core/descriptive.mjs +2 -2
- package/dist/statistics/core/normal.d.mts +3 -3
- package/dist/statistics/core/normal.mjs +3 -3
- package/dist/statistics/core/sequences.d.mts +1 -1
- package/dist/statistics/galton/preset.mjs +2 -2
- package/dist/statistics/histogram/preset.mjs +6 -6
- package/dist/statistics/normal/preset.mjs +2 -2
- package/dist/statistics/sampling/preset.mjs +4 -4
- package/dist/statistics/sequence/preset.mjs +58 -15
- package/dist/statistics/z-table/preset.mjs +17 -4
- package/package.json +11 -3
- package/styles.css +4 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { toRad } from "../../core/util.mjs";
|
|
2
|
+
import { calc } from "../../kit/calc.mjs";
|
|
3
|
+
import { castLetter, castPositive, evalTrig, exactTex, isSpecial, normDeg, quadrant, radTex, referenceAngleDeg, sign } from "./core.mjs";
|
|
4
|
+
import { SpecialTriangles, UnitCircleMini } from "./diagrams.mjs";
|
|
5
|
+
import { Fragment, createElement } from "react";
|
|
6
|
+
|
|
7
|
+
//#region src/math/trig/rules.ts
|
|
8
|
+
/**
|
|
9
|
+
* The trig RULEBOOK: signs (CAST), exact values, the Pythagorean identity, and
|
|
10
|
+
* degree↔radian — as RuleDef data for the RuleCard concept engine (formula +
|
|
11
|
+
* analogy + live worked calculator + derivation + tricks). Calculators run on the
|
|
12
|
+
* trig teaching kernel.
|
|
13
|
+
*/
|
|
14
|
+
const fmt = (n) => Number.isNaN(n) ? "\\text{undef}" : (Math.round(n * 1e3) / 1e3).toString();
|
|
15
|
+
/** Which functions are positive in the quadrant of `deg`, with the signs. */
|
|
16
|
+
function explainCast(deg) {
|
|
17
|
+
const q = quadrant(deg);
|
|
18
|
+
const pos = castPositive(deg);
|
|
19
|
+
const sg = (fn) => `\\${fn}: ${sign(fn, deg) > 0 ? "+" : sign(fn, deg) < 0 ? "-" : "0"}`;
|
|
20
|
+
return calc().step(`\\theta = ${normDeg(deg)}^\\circ \\Rightarrow \\text{Quadrant } ${q || "\\text{axis}"}`, "where the terminal ray lands").step(`${sg("sin")}, \\quad ${sg("cos")}, \\quad ${sg("tan")}`, q ? `Q${q}: only ${pos.join("/")} positive (${castLetter(deg)})` : "on an axis").done(q);
|
|
21
|
+
}
|
|
22
|
+
/** Exact sin/cos/tan at a special angle, via reference angle + sign. */
|
|
23
|
+
function explainExact(deg) {
|
|
24
|
+
const ref = referenceAngleDeg(deg);
|
|
25
|
+
const special = isSpecial(deg);
|
|
26
|
+
const c = calc().step(`\\theta = ${normDeg(deg)}^\\circ, \\quad \\text{reference } = ${ref}^\\circ`, "reduce to the acute angle");
|
|
27
|
+
if (special) c.step(`\\sin = ${exactTex("sin", deg)}, \\;\\; \\cos = ${exactTex("cos", deg)}, \\;\\; \\tan = ${exactTex("tan", deg)}`, "reference value × the quadrant sign");
|
|
28
|
+
else c.step(`\\sin \\approx ${fmt(evalTrig("sin", deg))}, \\;\\; \\cos \\approx ${fmt(evalTrig("cos", deg))}`, "not a standard angle, decimal");
|
|
29
|
+
return c.done(ref);
|
|
30
|
+
}
|
|
31
|
+
/** Degree → exact radian. */
|
|
32
|
+
function explainDegRad(deg) {
|
|
33
|
+
return calc().step(`${normDeg(deg)}^\\circ \\times \\dfrac{\\pi}{180^\\circ}`, "because 180° = π radians").step(`= ${radTex(deg)}`).done(toRad(deg));
|
|
34
|
+
}
|
|
35
|
+
const CAST_RULE = {
|
|
36
|
+
id: "cast",
|
|
37
|
+
name: "CAST: signs by quadrant",
|
|
38
|
+
formula: "\\text{Q1: All} \\;|\\; \\text{Q2: Sin} \\;|\\; \\text{Q3: Tan} \\;|\\; \\text{Q4: Cos}",
|
|
39
|
+
analogy: "\"All Students Take Calculus.\" Going anticlockwise from Q1: All positive, then only Sin, then only Tan, then only Cos.",
|
|
40
|
+
figure: (v) => createElement(UnitCircleMini, {
|
|
41
|
+
deg: v.deg ?? 0,
|
|
42
|
+
showLegs: true,
|
|
43
|
+
showCast: true
|
|
44
|
+
}),
|
|
45
|
+
inputs: [{
|
|
46
|
+
key: "deg",
|
|
47
|
+
label: "θ°",
|
|
48
|
+
default: 210,
|
|
49
|
+
min: 0,
|
|
50
|
+
max: 345,
|
|
51
|
+
step: 15
|
|
52
|
+
}],
|
|
53
|
+
compute: (v) => explainCast(v.deg ?? 0),
|
|
54
|
+
derivation: [
|
|
55
|
+
{
|
|
56
|
+
tex: "\\cos\\theta = x, \\quad \\sin\\theta = y",
|
|
57
|
+
note: "the point on the unit circle"
|
|
58
|
+
},
|
|
59
|
+
{ tex: "\\tan\\theta = \\tfrac{\\sin\\theta}{\\cos\\theta} = \\tfrac{y}{x}" },
|
|
60
|
+
{
|
|
61
|
+
tex: "\\text{so the signs are just the signs of } x, y",
|
|
62
|
+
note: "Q2: x<0,y>0 ⇒ only sin +"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
tricks: [
|
|
66
|
+
"It is literally the sign of x (cos) and y (sin) in each quadrant.",
|
|
67
|
+
"tan is positive where sin and cos AGREE in sign (Q1, Q3).",
|
|
68
|
+
"Reference angle gives the size; CAST gives the sign."
|
|
69
|
+
]
|
|
70
|
+
};
|
|
71
|
+
const EXACT_RULE = {
|
|
72
|
+
id: "exact-values",
|
|
73
|
+
name: "Exact special-angle values",
|
|
74
|
+
formula: "\\sin 30^\\circ=\\tfrac12,\\; \\cos 30^\\circ=\\tfrac{\\sqrt3}{2},\\; \\sin 45^\\circ=\\tfrac{\\sqrt2}{2}",
|
|
75
|
+
analogy: "They all come from two triangles: the 45-45-90 (sides 1,1,√2) and the 30-60-90 (sides 1,√3,2).",
|
|
76
|
+
figure: (v) => createElement(Fragment, null, createElement("div", { style: {
|
|
77
|
+
display: "grid",
|
|
78
|
+
gap: 6,
|
|
79
|
+
justifyItems: "center"
|
|
80
|
+
} }, createElement(UnitCircleMini, {
|
|
81
|
+
deg: v.deg ?? 0,
|
|
82
|
+
showLegs: true,
|
|
83
|
+
showValue: true
|
|
84
|
+
}), createElement(SpecialTriangles, {}))),
|
|
85
|
+
inputs: [{
|
|
86
|
+
key: "deg",
|
|
87
|
+
label: "θ°",
|
|
88
|
+
default: 150,
|
|
89
|
+
min: 0,
|
|
90
|
+
max: 330,
|
|
91
|
+
step: 30
|
|
92
|
+
}],
|
|
93
|
+
compute: (v) => explainExact(v.deg ?? 0),
|
|
94
|
+
tricks: [
|
|
95
|
+
"Only the multiples of 30° and 45° are \"exact\".",
|
|
96
|
+
"Find the reference angle, look up its value, then apply the CAST sign.",
|
|
97
|
+
"sin and cos swap between 30° and 60° (½ ↔ √3⁄2)."
|
|
98
|
+
]
|
|
99
|
+
};
|
|
100
|
+
const PYTHAG_RULE = {
|
|
101
|
+
id: "pythagorean",
|
|
102
|
+
name: "The Pythagorean identity",
|
|
103
|
+
formula: "\\sin^2\\theta + \\cos^2\\theta = 1",
|
|
104
|
+
analogy: "It IS Pythagoras on the unit circle: the point (cosθ, sinθ) sits at distance 1 from the centre, so x² + y² = 1.",
|
|
105
|
+
figure: createElement(UnitCircleMini, {
|
|
106
|
+
deg: 52,
|
|
107
|
+
showLegs: true,
|
|
108
|
+
showHyp: true
|
|
109
|
+
}),
|
|
110
|
+
derivation: [
|
|
111
|
+
{
|
|
112
|
+
tex: "(\\cos\\theta,\\ \\sin\\theta) \\text{ is on the unit circle}",
|
|
113
|
+
note: "radius 1"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
tex: "x^2 + y^2 = 1^2",
|
|
117
|
+
note: "distance from the origin"
|
|
118
|
+
},
|
|
119
|
+
{ tex: "\\Rightarrow \\cos^2\\theta + \\sin^2\\theta = 1" }
|
|
120
|
+
],
|
|
121
|
+
tricks: [
|
|
122
|
+
"Divide by cos²θ → 1 + tan²θ = sec²θ.",
|
|
123
|
+
"Divide by sin²θ → 1 + cot²θ = csc²θ.",
|
|
124
|
+
"Lets you get cos from sin (up to sign): cosθ = ±√(1 − sin²θ)."
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
const DEG_RAD_RULE = {
|
|
128
|
+
id: "deg-rad",
|
|
129
|
+
name: "Degrees ↔ radians",
|
|
130
|
+
formula: "180^\\circ = \\pi \\text{ rad}, \\qquad 1^\\circ = \\tfrac{\\pi}{180}",
|
|
131
|
+
analogy: "A radian is the angle that wraps one radius-length of arc around the circle; a full turn (360°) is 2π of them.",
|
|
132
|
+
figure: (v) => createElement(UnitCircleMini, {
|
|
133
|
+
deg: v.deg ?? 0,
|
|
134
|
+
showLegs: false
|
|
135
|
+
}),
|
|
136
|
+
inputs: [{
|
|
137
|
+
key: "deg",
|
|
138
|
+
label: "θ°",
|
|
139
|
+
default: 30,
|
|
140
|
+
min: 0,
|
|
141
|
+
max: 360,
|
|
142
|
+
step: 15
|
|
143
|
+
}],
|
|
144
|
+
compute: (v) => explainDegRad(v.deg ?? 0),
|
|
145
|
+
tricks: [
|
|
146
|
+
"× π/180 to go deg → rad; × 180/π to go back.",
|
|
147
|
+
"Memorise the anchors: 180°=π, 90°=π/2, 60°=π/3, 45°=π/4, 30°=π/6.",
|
|
148
|
+
"Calculus needs radians (so d/dx sin x = cos x works)."
|
|
149
|
+
]
|
|
150
|
+
};
|
|
151
|
+
const TRIG_RULES = [
|
|
152
|
+
CAST_RULE,
|
|
153
|
+
EXACT_RULE,
|
|
154
|
+
PYTHAG_RULE,
|
|
155
|
+
DEG_RAD_RULE
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
//#endregion
|
|
159
|
+
export { TRIG_RULES };
|
|
@@ -10,14 +10,14 @@ import { Axes, Circle, Dot, Grid, MovableDot, Polyline, Segment, Stage, Vector,
|
|
|
10
10
|
|
|
11
11
|
//#region src/math/trig-explorer.tsx
|
|
12
12
|
/**
|
|
13
|
-
* TrigExplorer
|
|
13
|
+
* TrigExplorer, the unit circle ↔ sine wave connection (3Blue1Brown style),
|
|
14
14
|
* but interactive: drag the angle (or press play) and watch sin/cos trace out.
|
|
15
15
|
*
|
|
16
|
-
* Now on the @classytic/stage engine (SVG, accessible, themed)
|
|
16
|
+
* Now on the @classytic/stage engine (SVG, accessible, themed), the unit circle,
|
|
17
17
|
* radius, projections, and unrolled wave are real primitives, the angle handle is
|
|
18
18
|
* a `MovableDot` (keyboard + aria), and animation runs on the engine clock.
|
|
19
19
|
*
|
|
20
|
-
* Deliberately FOCUSED on one idea
|
|
20
|
+
* Deliberately FOCUSED on one idea, "sin and cos are the shadows of a point
|
|
21
21
|
* going around a circle." Plotting arbitrary trig (tan, y=a·sin(b·x), …) is the
|
|
22
22
|
* job of the general `Grapher`; this widget stays clean. Configurable only in
|
|
23
23
|
* which of sin/cos to show.
|
|
@@ -173,7 +173,7 @@ function TrigExplorer({ functions, startDeg = 30 } = {}) {
|
|
|
173
173
|
});
|
|
174
174
|
return /* @__PURE__ */ jsx(LabFrame, {
|
|
175
175
|
title: "Trig Explorer",
|
|
176
|
-
prompt: "Drag the angle on the circle (or press play)
|
|
176
|
+
prompt: "Drag the angle on the circle (or press play), sin and cos are just its shadows.",
|
|
177
177
|
controls: /* @__PURE__ */ jsxs(ControlBar, { children: [
|
|
178
178
|
/* @__PURE__ */ jsx(CheckButton, {
|
|
179
179
|
onClick: () => setPlaying((p) => !p),
|
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
import { gaussian, mulberry32 } from "../../core/rng.mjs";
|
|
4
4
|
import { Chip } from "../../kit/controls.mjs";
|
|
5
5
|
import { Callout, ControlBar, LabFrame } from "../../kit/frame.mjs";
|
|
6
|
-
import { HintLadder, useHints } from "../../kit/pedagogy.mjs";
|
|
6
|
+
import { HintLadder, useCheckpoint, useHints } from "../../kit/pedagogy.mjs";
|
|
7
7
|
import { useCallback, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { Axes, Circle, Dot, Grid, MovableDot, Polygon, Segment, Stage, useControlSurface, useFrameLoop, useInView } from "@classytic/stage";
|
|
10
10
|
|
|
11
11
|
//#region src/ml/boundary/preset.tsx
|
|
12
12
|
/**
|
|
13
|
-
* DecisionBoundaryLab
|
|
13
|
+
* DecisionBoundaryLab, a linear classifier you can SEE think. Two classes of
|
|
14
14
|
* points sit in the plane; a straight boundary splits it into two predicted
|
|
15
15
|
* regions. Drag the boundary's two handles to separate the classes by hand
|
|
16
16
|
* (misclassified points get a red ring, accuracy updates live), then hit "train"
|
|
17
17
|
* and watch a PERCEPTRON nudge the same line into place on its own. The honest
|
|
18
|
-
* twist: the XOR dataset can't be split by ANY straight line
|
|
18
|
+
* twist: the XOR dataset can't be split by ANY straight line, accuracy stalls
|
|
19
19
|
* below 100%, the door to "why we need more than a line."
|
|
20
20
|
*
|
|
21
21
|
* Pure stage primitives (Stage + Polygon shading + MovableDot handles + frame-loop
|
|
@@ -96,6 +96,11 @@ function DecisionBoundaryLab({ dataset = "separable", seed = 11, title = "Draw t
|
|
|
96
96
|
wrong: wr
|
|
97
97
|
};
|
|
98
98
|
}, [pts, weights]);
|
|
99
|
+
useCheckpoint({
|
|
100
|
+
solved: acc === 1,
|
|
101
|
+
activity: `boundary:${title}`,
|
|
102
|
+
hintsUsed: hints.count
|
|
103
|
+
});
|
|
99
104
|
const regions = useMemo(() => {
|
|
100
105
|
const corners = [
|
|
101
106
|
{
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
import { clamp } from "../../core/util.mjs";
|
|
4
4
|
import { StatusPill } from "../../kit/controls.mjs";
|
|
5
5
|
import { LabFrame, LiveRegion } from "../../kit/frame.mjs";
|
|
6
|
-
import { useCheckpoint } from "../../kit/pedagogy.mjs";
|
|
6
|
+
import { ChallengeCard, useChallenge, useCheckpoint } from "../../kit/pedagogy.mjs";
|
|
7
7
|
import { useState } from "react";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { Circle, Dot, Label, MovableDot, Polygon, Segment, Stage } from "@classytic/stage";
|
|
10
10
|
|
|
11
11
|
//#region src/ml/classifier/preset.tsx
|
|
12
12
|
/**
|
|
13
|
-
* ClassifierThresholdLab
|
|
13
|
+
* ClassifierThresholdLab, the precision/recall trade-off you can drag. Positive
|
|
14
14
|
* and negative examples sit along a score axis (they OVERLAP, like any real
|
|
15
15
|
* classifier). Drag the threshold: everything to its right is predicted positive.
|
|
16
|
-
* The 2×2 confusion matrix and precision / recall / accuracy / F1 update live
|
|
16
|
+
* The 2×2 confusion matrix and precision / recall / accuracy / F1 update live ,
|
|
17
17
|
* slide right and precision climbs while recall falls; there's no setting that
|
|
18
18
|
* maxes both. Misclassified points get a red ring.
|
|
19
19
|
*
|
|
@@ -46,7 +46,27 @@ const DEFAULT_NEG = [
|
|
|
46
46
|
5,
|
|
47
47
|
6
|
|
48
48
|
];
|
|
49
|
-
|
|
49
|
+
const PREDICT_Q = [{
|
|
50
|
+
id: "recall-vs-threshold",
|
|
51
|
+
prompt: "As you RAISE the threshold (label fewer things positive), what happens to RECALL — the share of true positives you catch?",
|
|
52
|
+
choices: [
|
|
53
|
+
{
|
|
54
|
+
value: "down",
|
|
55
|
+
label: "recall falls"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
value: "up",
|
|
59
|
+
label: "recall rises"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
value: "same",
|
|
63
|
+
label: "unchanged"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
answer: "down",
|
|
67
|
+
explain: "Raising the threshold means fewer points clear the bar, so you catch fewer of the real positives (more false negatives): recall falls. Precision usually rises in return, since the ones you do call positive are more likely correct. That is the trade-off, no single threshold maxes both."
|
|
68
|
+
}];
|
|
69
|
+
function ClassifierThresholdLab({ positives = DEFAULT_POS, negatives = DEFAULT_NEG, threshold = 5, span = 10, title = "The precision–recall trade-off", prompt = "Drag the threshold: everything to its right is called positive. Watch the confusion matrix, pushing precision up costs you recall.", objectives, height = 300 }) {
|
|
50
70
|
const view = {
|
|
51
71
|
xMin: 0,
|
|
52
72
|
xMax: span,
|
|
@@ -55,8 +75,9 @@ function ClassifierThresholdLab({ positives = DEFAULT_POS, negatives = DEFAULT_N
|
|
|
55
75
|
};
|
|
56
76
|
const [t, setT] = useState(threshold);
|
|
57
77
|
const [moved, setMoved] = useState(false);
|
|
78
|
+
const ch = useChallenge(PREDICT_Q);
|
|
58
79
|
useCheckpoint({
|
|
59
|
-
solved: moved,
|
|
80
|
+
solved: moved && ch.allCorrect,
|
|
60
81
|
activity: "classifier-threshold"
|
|
61
82
|
});
|
|
62
83
|
const TP = positives.filter((s) => s >= t).length;
|
|
@@ -306,7 +327,11 @@ function ClassifierThresholdLab({ positives = DEFAULT_POS, negatives = DEFAULT_N
|
|
|
306
327
|
})
|
|
307
328
|
]
|
|
308
329
|
})] }),
|
|
309
|
-
footer: /* @__PURE__ */
|
|
330
|
+
footer: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ChallengeCard, {
|
|
331
|
+
questions: PREDICT_Q,
|
|
332
|
+
state: ch,
|
|
333
|
+
title: "Predict first"
|
|
334
|
+
}), /* @__PURE__ */ jsx(LiveRegion, { children: `Threshold ${t.toFixed(1)}. Precision ${(precision * 100).toFixed(0)} percent, recall ${(recall * 100).toFixed(0)} percent.` })] }),
|
|
310
335
|
children: figure
|
|
311
336
|
});
|
|
312
337
|
}
|
|
@@ -9,12 +9,12 @@ import { Circle, Dot, Grid, MovableDot, Segment, Stage, useFrameLoop, useInView
|
|
|
9
9
|
|
|
10
10
|
//#region src/ml/kmeans/preset.tsx
|
|
11
11
|
/**
|
|
12
|
-
* KMeansLab
|
|
12
|
+
* KMeansLab, unsupervised clustering you can watch converge. Points sit in a few
|
|
13
13
|
* blobs; you drag the k centroids to seed them, then Step (Lloyd's algorithm):
|
|
14
14
|
* every point recolours to its nearest centroid, and each centroid jumps to the
|
|
15
|
-
* mean of its cluster. Run animates it
|
|
15
|
+
* mean of its cluster. Run animates it, centroids migrate into the blobs, the
|
|
16
16
|
* within-cluster error (inertia) drops, and it stops when nothing moves. Seed the
|
|
17
|
-
* centroids badly on purpose and you land in a worse local minimum
|
|
17
|
+
* centroids badly on purpose and you land in a worse local minimum, the key
|
|
18
18
|
* k-means intuition that initialisation matters.
|
|
19
19
|
*
|
|
20
20
|
* Pure stage primitives: Dot (coloured by assignment) + MovableDot (centroids) +
|
|
@@ -59,7 +59,7 @@ const DEFAULT_SEEDS = [
|
|
|
59
59
|
}
|
|
60
60
|
];
|
|
61
61
|
const d2 = (a, b) => (a.x - b.x) ** 2 + (a.y - b.y) ** 2;
|
|
62
|
-
function KMeansLab({ points = DEFAULT_POINTS, k = 3, seeds, span = 10, showLines = true, title = "k-means
|
|
62
|
+
function KMeansLab({ points = DEFAULT_POINTS, k = 3, seeds, span = 10, showLines = true, title = "k-means: watch the clusters form", prompt = "Drag the centroids to seed them, then Step: points recolour to the nearest centroid, centroids jump to their cluster mean. Seed them badly and you get stuck in a worse answer.", objectives, height = 380 }) {
|
|
63
63
|
const view = {
|
|
64
64
|
xMin: 0,
|
|
65
65
|
xMax: span,
|
package/dist/ml/knn/preset.mjs
CHANGED
|
@@ -10,15 +10,15 @@ import { CanvasLayer, useControlSurface } from "@classytic/stage";
|
|
|
10
10
|
|
|
11
11
|
//#region src/ml/knn/preset.tsx
|
|
12
12
|
/**
|
|
13
|
-
* KNNBoundaryLab
|
|
13
|
+
* KNNBoundaryLab, the answer to "a line can't split XOR." k-nearest-neighbours
|
|
14
14
|
* paints the whole plane by asking, at every point, "what are my k closest
|
|
15
15
|
* labelled neighbours, and which class wins the vote?" The boundary it carves is
|
|
16
|
-
* CURVY
|
|
16
|
+
* CURVY, it shrugs off XOR and even concentric rings that no straight line could
|
|
17
17
|
* touch. Drag the ✦ test point to watch its k neighbours light up and cast their
|
|
18
18
|
* votes; slide k to feel the trade-off: k=1 memorises every island (jagged,
|
|
19
19
|
* overfit), big k smooths everything (and starts ignoring real structure).
|
|
20
20
|
*
|
|
21
|
-
* The decision regions are a CanvasLayer heatmap (one vote per cell
|
|
21
|
+
* The decision regions are a CanvasLayer heatmap (one vote per cell, exactly the
|
|
22
22
|
* high-element-count job canvas is for); points + neighbour spokes draw on top.
|
|
23
23
|
*/
|
|
24
24
|
const R = 5, A0 = "#1c7ed6", A1 = "#e8590c";
|
|
@@ -182,15 +182,50 @@ function KNNBoundaryLab({ dataset = "circles", k = 5, seed = 7, title = "k-neare
|
|
|
182
182
|
set: setKk
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
185
|
+
const vw = useMemo(() => view, []);
|
|
186
|
+
const clampX = (x) => Math.max(view.xMin, Math.min(view.xMax, x));
|
|
187
|
+
const clampY = (y) => Math.max(view.yMin, Math.min(view.yMax, y));
|
|
188
|
+
const KEY_STEP = .25;
|
|
189
|
+
const onKeyDown = useCallback((e) => {
|
|
190
|
+
let dx = 0, dy = 0;
|
|
191
|
+
if (e.key === "ArrowLeft") dx = -.25;
|
|
192
|
+
else if (e.key === "ArrowRight") dx = KEY_STEP;
|
|
193
|
+
else if (e.key === "ArrowUp") dy = KEY_STEP;
|
|
194
|
+
else if (e.key === "ArrowDown") dy = -.25;
|
|
195
|
+
else return;
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
setQuery((q) => ({
|
|
198
|
+
x: clampX(q.x + dx),
|
|
199
|
+
y: clampY(q.y + dy)
|
|
200
|
+
}));
|
|
201
|
+
}, []);
|
|
202
|
+
const figure = /* @__PURE__ */ jsx("div", {
|
|
203
|
+
tabIndex: 0,
|
|
204
|
+
role: "application",
|
|
205
|
+
"aria-label": `k-NN decision boundary, k ${kk}. Test point predicts class ${qCls ? "B" : "A"}. Accuracy ${(acc * 100).toFixed(0)} percent. Use arrow keys to move the test point.`,
|
|
206
|
+
onKeyDown,
|
|
207
|
+
style: {
|
|
208
|
+
outline: "none",
|
|
209
|
+
borderRadius: 6,
|
|
210
|
+
position: "relative"
|
|
211
|
+
},
|
|
212
|
+
onFocus: (e) => {
|
|
213
|
+
e.currentTarget.style.outline = "2px solid var(--stage-accent, #1c7ed6)";
|
|
214
|
+
e.currentTarget.style.outlineOffset = "2px";
|
|
215
|
+
},
|
|
216
|
+
onBlur: (e) => {
|
|
217
|
+
e.currentTarget.style.outline = "none";
|
|
218
|
+
},
|
|
219
|
+
children: /* @__PURE__ */ jsx(CanvasLayer, {
|
|
220
|
+
view: vw,
|
|
221
|
+
height,
|
|
222
|
+
draw,
|
|
223
|
+
onPointerMath: (m) => setQuery({
|
|
224
|
+
x: clampX(m[0]),
|
|
225
|
+
y: clampY(m[1])
|
|
226
|
+
}),
|
|
227
|
+
ariaLabel: `k-NN boundary, k ${kk}, accuracy ${(acc * 100).toFixed(0)}%`
|
|
228
|
+
})
|
|
194
229
|
});
|
|
195
230
|
return /* @__PURE__ */ jsx(LabFrame, {
|
|
196
231
|
title,
|
|
@@ -3,26 +3,26 @@
|
|
|
3
3
|
import { clamp } from "../../core/util.mjs";
|
|
4
4
|
import { CheckButton, Slider, StatusPill } from "../../kit/controls.mjs";
|
|
5
5
|
import { Callout, ControlBar, Field, LabFrame, LiveRegion } from "../../kit/frame.mjs";
|
|
6
|
-
import { useCheckpoint } from "../../kit/pedagogy.mjs";
|
|
6
|
+
import { ChallengeCard, useChallenge, useCheckpoint } from "../../kit/pedagogy.mjs";
|
|
7
7
|
import { useMemo, useRef, useState } from "react";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { Axes, Dot, Grid, Label, MovableDot, Polygon, Segment, Stage, useFrameLoop, useInView } from "@classytic/stage";
|
|
10
10
|
|
|
11
11
|
//#region src/ml/regression/preset.tsx
|
|
12
12
|
/**
|
|
13
|
-
* RegressionLab
|
|
13
|
+
* RegressionLab, least squares you can FEEL, and gradient descent you can WATCH.
|
|
14
14
|
*
|
|
15
15
|
* Drag the line's two handles: every data point grows a SQUARE whose area is its
|
|
16
|
-
* squared error, and the loss (mean squared error) updates live
|
|
16
|
+
* squared error, and the loss (mean squared error) updates live, "least squares"
|
|
17
17
|
* is literally "make the total square area smallest". Then press Descend and watch
|
|
18
18
|
* the line crawl downhill on its own, squares shrinking, loss tumbling. The
|
|
19
|
-
* learning-rate slider lets you make it overshoot and DIVERGE
|
|
19
|
+
* learning-rate slider lets you make it overshoot and DIVERGE, the #1 gradient-
|
|
20
20
|
* descent intuition. Reveal snaps to the closed-form optimum to check yourself.
|
|
21
21
|
*
|
|
22
22
|
* The litmus test for ML/data labs on the stage engine: scatter + draggable fit +
|
|
23
23
|
* frame-loop optimisation + live loss, all from the shared primitives. Isometric
|
|
24
24
|
* view (preserveAspect + equal spans) so a data-unit square reads as a real
|
|
25
|
-
* on-screen square
|
|
25
|
+
* on-screen square, and stays SSR-deterministic.
|
|
26
26
|
*/
|
|
27
27
|
const DEFAULT_DATA = [
|
|
28
28
|
{
|
|
@@ -62,7 +62,16 @@ const DEFAULT_DATA = [
|
|
|
62
62
|
y: 8.3
|
|
63
63
|
}
|
|
64
64
|
];
|
|
65
|
-
|
|
65
|
+
const slopeSign = (data) => {
|
|
66
|
+
const n = data.length;
|
|
67
|
+
if (n === 0) return "up";
|
|
68
|
+
const xb = data.reduce((s, p) => s + p.x, 0) / n;
|
|
69
|
+
const yb = data.reduce((s, p) => s + p.y, 0) / n;
|
|
70
|
+
let num = 0;
|
|
71
|
+
for (const p of data) num += (p.x - xb) * (p.y - yb);
|
|
72
|
+
return num >= 0 ? "up" : "down";
|
|
73
|
+
};
|
|
74
|
+
function RegressionLab({ data = DEFAULT_DATA, showSquares = true, learnRate = .006, m0 = .3, b0 = 3.2, span = 10, title = "Least squares: drag the line, watch the error", prompt = "Each point grows a square of its squared error. Make the total area smallest, then press Descend and watch gradient descent do it for you.", objectives, height = 380 }) {
|
|
66
75
|
const view = {
|
|
67
76
|
xMin: 0,
|
|
68
77
|
xMax: span,
|
|
@@ -118,6 +127,22 @@ function RegressionLab({ data = DEFAULT_DATA, showSquares = true, learnRate = .0
|
|
|
118
127
|
solved: closeEnough,
|
|
119
128
|
activity: "regression"
|
|
120
129
|
});
|
|
130
|
+
const predictQ = useMemo(() => {
|
|
131
|
+
return [{
|
|
132
|
+
id: "slope-sign",
|
|
133
|
+
prompt: "Before you fit the line — looking at the cloud of points, will the best-fit line slope UP or DOWN?",
|
|
134
|
+
choices: [{
|
|
135
|
+
value: "up",
|
|
136
|
+
label: "slope up (positive)"
|
|
137
|
+
}, {
|
|
138
|
+
value: "down",
|
|
139
|
+
label: "slope down (negative)"
|
|
140
|
+
}],
|
|
141
|
+
answer: slopeSign(data),
|
|
142
|
+
explain: "Least squares follows the overall trend of the cloud: as x increases the points drift the same way, so the best-fit slope picks up that direction — no dragging needed to call its sign."
|
|
143
|
+
}];
|
|
144
|
+
}, [data]);
|
|
145
|
+
const ch = useChallenge(predictQ);
|
|
121
146
|
const lineRef = useRef(line);
|
|
122
147
|
lineRef.current = line;
|
|
123
148
|
const iterRef = useRef(0);
|
|
@@ -385,7 +410,11 @@ function RegressionLab({ data = DEFAULT_DATA, showSquares = true, learnRate = .0
|
|
|
385
410
|
children: ["step ", iter]
|
|
386
411
|
})
|
|
387
412
|
] }),
|
|
388
|
-
footer: /* @__PURE__ */
|
|
413
|
+
footer: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ChallengeCard, {
|
|
414
|
+
questions: predictQ,
|
|
415
|
+
state: ch,
|
|
416
|
+
title: "Predict first"
|
|
417
|
+
}), /* @__PURE__ */ jsx(LiveRegion, { children: `Line y = ${line.m.toFixed(2)}x + ${line.b.toFixed(2)}. Mean squared error ${loss.toFixed(2)}. ${closeEnough ? "This is the best fit." : ""}` })] }),
|
|
389
418
|
children: figure
|
|
390
419
|
});
|
|
391
420
|
}
|
|
@@ -11,7 +11,7 @@ import { Circle, Label, Polygon, Segment, Stage, useInView } from "@classytic/st
|
|
|
11
11
|
|
|
12
12
|
//#region src/physics/atwood/preset.tsx
|
|
13
13
|
/**
|
|
14
|
-
* AtwoodLab
|
|
14
|
+
* AtwoodLab, "Which way, and how fast?", two masses over a pulley.
|
|
15
15
|
*
|
|
16
16
|
* The classic Atwood machine: link two masses over a frictionless pulley and the
|
|
17
17
|
* WHOLE system shares one acceleration. Gravity pulls each side (m·g), but only
|
|
@@ -27,7 +27,7 @@ import { Circle, Label, Polygon, Segment, Stage, useInView } from "@classytic/st
|
|
|
27
27
|
const G = 9.8;
|
|
28
28
|
const Y0 = .6;
|
|
29
29
|
const TRAVEL = 2.6;
|
|
30
|
-
function AtwoodLab({ m1 = 3, m2 = 2, title = "Atwood machine
|
|
30
|
+
function AtwoodLab({ m1 = 3, m2 = 2, title = "Atwood machine: which way, and how fast?", prompt = "Two masses share one rope over a pulley. Only the difference in weight drives them, while the total mass resists: a = (m₁ − m₂)g / (m₁ + m₂). Predict which side falls, then release.", objectives, controlConfig }) {
|
|
31
31
|
const [ma, setMa] = useState(m1);
|
|
32
32
|
const [mb, setMb] = useState(m2);
|
|
33
33
|
const [running, setRunning] = useState(false);
|
|
@@ -244,7 +244,7 @@ function AtwoodLab({ m1 = 3, m2 = 2, title = "Atwood machine — which way, and
|
|
|
244
244
|
}),
|
|
245
245
|
/* @__PURE__ */ jsx(StatusPill, {
|
|
246
246
|
ok: !balanced,
|
|
247
|
-
children: balanced ? "balanced
|
|
247
|
+
children: balanced ? "balanced, no motion" : `${ma > mb ? "left" : "right"} side falls`
|
|
248
248
|
}),
|
|
249
249
|
/* @__PURE__ */ jsxs("p", {
|
|
250
250
|
style: {
|
|
@@ -257,7 +257,7 @@ function AtwoodLab({ m1 = 3, m2 = 2, title = "Atwood machine — which way, and
|
|
|
257
257
|
/* @__PURE__ */ jsx("strong", { children: "difference" }),
|
|
258
258
|
" (m₁−m₂)g drives it; the ",
|
|
259
259
|
/* @__PURE__ */ jsx("strong", { children: "total" }),
|
|
260
|
-
" (m₁+m₂) resists. Big equal masses with a tiny difference give a slow, measurable a
|
|
260
|
+
" (m₁+m₂) resists. Big equal masses with a tiny difference give a slow, measurable a, how Atwood weighed gravity."
|
|
261
261
|
]
|
|
262
262
|
}),
|
|
263
263
|
/* @__PURE__ */ jsx(LiveRegion, { children: balanced ? "Balanced, no motion." : `${ma > mb ? "Left" : "Right"} side falls at ${Math.abs(a).toFixed(2)} metres per second squared.` })
|
|
@@ -11,7 +11,7 @@ interface BulletWallsProps {
|
|
|
11
11
|
title?: string;
|
|
12
12
|
prompt?: string;
|
|
13
13
|
objectives?: string[];
|
|
14
|
-
/** Lock/hide any knob
|
|
14
|
+
/** Lock/hide any knob, e.g. `{ lock: ['plank toughness'] }` to fix the toughness. */
|
|
15
15
|
controlConfig?: ControlConfig;
|
|
16
16
|
}
|
|
17
17
|
declare function BulletWallsLab({
|
|
@@ -10,18 +10,18 @@ import { Label, Polygon, Segment, Stage, useInView, useLearner } from "@classyti
|
|
|
10
10
|
|
|
11
11
|
//#region src/physics/bullet-walls/preset.tsx
|
|
12
12
|
/**
|
|
13
|
-
* BulletWallsLab
|
|
13
|
+
* BulletWallsLab, "How many planks?", the classic penetration problem made
|
|
14
14
|
* watchable and PREDICT-FIRST.
|
|
15
15
|
*
|
|
16
16
|
* A bullet fires into a stack of identical planks. Each plank pushes back with a
|
|
17
17
|
* constant retarding force over its thickness, so it drains a FIXED chunk of
|
|
18
|
-
* kinetic energy
|
|
18
|
+
* kinetic energy, equivalently a fixed Δ(v²) per plank. The bullet free-flies
|
|
19
19
|
* between planks (constant v) and decelerates linearly in v² while inside one
|
|
20
20
|
* (v²(x) = v²ₑ − (cost/width)·depth), the textbook v² = u² − 2as. It stops the
|
|
21
|
-
* instant its kinetic energy runs out
|
|
21
|
+
* instant its kinetic energy runs out, embedding partway through a plank.
|
|
22
22
|
*
|
|
23
23
|
* The learner GUESSES how many planks it punches through, then fires and watches
|
|
24
|
-
* it slow plank-by-plank while the KE bar drains
|
|
24
|
+
* it slow plank-by-plank while the KE bar drains, turning "N = u²/(2as)" from a
|
|
25
25
|
* formula into a bet you can win.
|
|
26
26
|
*
|
|
27
27
|
* Tokenized SVG; time-dependent integrator here; honours reduced-motion.
|
|
@@ -29,7 +29,7 @@ import { Label, Polygon, Segment, Stage, useInView, useLearner } from "@classyti
|
|
|
29
29
|
const PLANK_W = .7;
|
|
30
30
|
const START_X = -4;
|
|
31
31
|
const STACK_X0 = 0;
|
|
32
|
-
function BulletWallsLab({ speed = 30, toughness = 160, planks = 6, mass = .02, title = "How many planks
|
|
32
|
+
function BulletWallsLab({ speed = 30, toughness = 160, planks = 6, mass = .02, title = "How many planks?: bet, then fire", prompt = "Each plank steals the same chunk of energy. Guess how many the bullet smashes through, then fire and watch the speed (and the energy bar) drain plank by plank.", objectives, controlConfig }) {
|
|
33
33
|
const [v0, setV0] = useState(speed);
|
|
34
34
|
const [cost, setCost] = useState(toughness);
|
|
35
35
|
const [n, setN] = useState(planks);
|
|
@@ -253,7 +253,7 @@ function BulletWallsLab({ speed = 30, toughness = 160, planks = 6, mass = .02, t
|
|
|
253
253
|
]
|
|
254
254
|
})
|
|
255
255
|
});
|
|
256
|
-
const verdict = settled ? guess === fullPenetrated ? `🎯 Spot on
|
|
256
|
+
const verdict = settled ? guess === fullPenetrated ? `🎯 Spot on, ${fullPenetrated} planks` : `It broke ${fullPenetrated}, you guessed ${guess}` : null;
|
|
257
257
|
return /* @__PURE__ */ jsx(LabFrame, {
|
|
258
258
|
title,
|
|
259
259
|
prompt,
|
|
@@ -303,7 +303,7 @@ function BulletWallsLab({ speed = 30, toughness = 160, planks = 6, mass = .02, t
|
|
|
303
303
|
children: [
|
|
304
304
|
"Predict with ",
|
|
305
305
|
/* @__PURE__ */ jsx("strong", { children: "N = u² / (Δv² per plank)" }),
|
|
306
|
-
"
|
|
306
|
+
", then watch v² fall by the same step each plank (v² = u² − 2as). It lodges when the energy hits zero."
|
|
307
307
|
]
|
|
308
308
|
}),
|
|
309
309
|
/* @__PURE__ */ jsx(LiveRegion, { children: settled ? `The bullet broke ${fullPenetrated} of ${n} planks. You guessed ${guess}.` : "" })
|
|
@@ -11,11 +11,11 @@ import { carnotCycle, gammaDiatomic, gammaMonatomic } from "@classytic/stage/the
|
|
|
11
11
|
|
|
12
12
|
//#region src/physics/carnot/preset.tsx
|
|
13
13
|
/**
|
|
14
|
-
* CarnotCycleLab
|
|
14
|
+
* CarnotCycleLab, the most efficient possible heat engine, shown two ways at once:
|
|
15
15
|
* • P–V diagram: the four-leg loop (hot isothermal expansion → adiabatic expansion
|
|
16
16
|
* → cold isothermal compression → adiabatic compression). The ENCLOSED area is
|
|
17
17
|
* the net work the engine delivers per cycle.
|
|
18
|
-
* • T–S diagram: the very same cycle is a RECTANGLE
|
|
18
|
+
* • T–S diagram: the very same cycle is a RECTANGLE, heat in along the top (Th),
|
|
19
19
|
* heat out along the bottom (Tc), entropy unchanged on the adiabatic sides. Its
|
|
20
20
|
* area (ΔS·(Th−Tc)) equals the net work too, and it makes the entropy bookkeeping
|
|
21
21
|
* obvious: ΔS = Qh/Th = Qc/Tc, so the gas returns to the same entropy (net ΔS = 0).
|
|
@@ -27,10 +27,10 @@ import { carnotCycle, gammaDiatomic, gammaMonatomic } from "@classytic/stage/the
|
|
|
27
27
|
const W = 720, H = 380;
|
|
28
28
|
const V1 = .02;
|
|
29
29
|
const J = (x) => Math.round(x).toString();
|
|
30
|
-
function CarnotCycleLab({ hotK = 500, coldK = 300, gas = "monatomic", expansionRatio = 2, title = "Carnot cycle
|
|
30
|
+
function CarnotCycleLab({ hotK = 500, coldK = 300, gas = "monatomic", expansionRatio = 2, title = "Carnot cycle: the best a heat engine can do", prompt = "A gas absorbs heat at a hot temperature, does work, and dumps the rest at a cold one. Watch the loop on the P–V diagram and the same cycle as a rectangle on the T–S diagram.", objectives = [
|
|
31
31
|
"See the Carnot cycle as a P–V loop whose area is the net work",
|
|
32
32
|
"Read the same cycle as a T–S rectangle (heat in at Th, out at Tc)",
|
|
33
|
-
"Derive the efficiency η = 1 − Tc/Th
|
|
33
|
+
"Derive the efficiency η = 1 − Tc/Th, the ceiling no engine can beat"
|
|
34
34
|
] } = {}) {
|
|
35
35
|
const [Th, setTh] = useState(hotK);
|
|
36
36
|
const [Tc, setTc] = useState(coldK);
|
|
@@ -345,7 +345,7 @@ function CarnotCycleLab({ hotK = 500, coldK = 300, gas = "monatomic", expansionR
|
|
|
345
345
|
" K"
|
|
346
346
|
]
|
|
347
347
|
}),
|
|
348
|
-
". The wider the temperature gap, the more efficient
|
|
348
|
+
". The wider the temperature gap, the more efficient, but you can never reach 100% without Tc = 0."
|
|
349
349
|
]
|
|
350
350
|
}),
|
|
351
351
|
/* @__PURE__ */ jsxs("span", {
|
|
@@ -355,7 +355,7 @@ function CarnotCycleLab({ hotK = 500, coldK = 300, gas = "monatomic", expansionR
|
|
|
355
355
|
/* @__PURE__ */ jsx(Tex, { tex: "\\Delta S = Q_h/T_h = Q_c/T_c" }),
|
|
356
356
|
" = ",
|
|
357
357
|
dS.toFixed(2),
|
|
358
|
-
" J/K, so the gas comes back to the same entropy
|
|
358
|
+
" J/K, so the gas comes back to the same entropy, ",
|
|
359
359
|
/* @__PURE__ */ jsx("strong", {
|
|
360
360
|
style: { color: "var(--stage-fg)" },
|
|
361
361
|
children: "net ΔS = 0"
|