@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,19 @@
|
|
|
1
|
+
import { CircuitDoc } from "./contract.mjs";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/build/CircuitPlayer.d.ts
|
|
5
|
+
interface CircuitPlayerProps {
|
|
6
|
+
doc: CircuitDoc;
|
|
7
|
+
flow?: boolean;
|
|
8
|
+
ariaLabel?: string;
|
|
9
|
+
/** notified after every tap with the new doc (e.g. to check a goal). */
|
|
10
|
+
onChange?: (doc: CircuitDoc) => void;
|
|
11
|
+
}
|
|
12
|
+
declare function CircuitPlayer({
|
|
13
|
+
doc: authored,
|
|
14
|
+
flow,
|
|
15
|
+
ariaLabel,
|
|
16
|
+
onChange
|
|
17
|
+
}: CircuitPlayerProps): ReactNode;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { CircuitPlayer, CircuitPlayerProps };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { getPart } from "./registry.mjs";
|
|
4
|
+
import { CircuitScene } from "./CircuitScene.mjs";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/build/CircuitPlayer.tsx
|
|
9
|
+
/**
|
|
10
|
+
* CircuitPlayer — a CircuitScene you can operate. The authored `doc` is the source of truth (so a
|
|
11
|
+
* parent can drive it, e.g. a slider that changes a resistor's ohms), and the learner's taps
|
|
12
|
+
* (close a switch, flip a component) are kept as a small per-part OVERLAY on top. That way an
|
|
13
|
+
* external doc change is adopted live AND the user's taps survive it. The engine re-solves on
|
|
14
|
+
* every change; CircuitScene stays a pure render.
|
|
15
|
+
*/
|
|
16
|
+
const applyOverlay = (doc, overlay) => ({
|
|
17
|
+
...doc,
|
|
18
|
+
parts: doc.parts.map((p) => overlay[p.id] ? {
|
|
19
|
+
...p,
|
|
20
|
+
props: {
|
|
21
|
+
...p.props,
|
|
22
|
+
...overlay[p.id]
|
|
23
|
+
}
|
|
24
|
+
} : p)
|
|
25
|
+
});
|
|
26
|
+
function CircuitPlayer({ doc: authored, flow = true, ariaLabel, onChange }) {
|
|
27
|
+
const [overlay, setOverlay] = useState({});
|
|
28
|
+
const doc = useMemo(() => applyOverlay(authored, overlay), [authored, overlay]);
|
|
29
|
+
const tap = (partId) => {
|
|
30
|
+
const part = doc.parts.find((p) => p.id === partId);
|
|
31
|
+
if (!part) return;
|
|
32
|
+
const patch = getPart(part.kind)?.tap?.(part);
|
|
33
|
+
if (!patch) return;
|
|
34
|
+
const next = {
|
|
35
|
+
...overlay,
|
|
36
|
+
[partId]: {
|
|
37
|
+
...overlay[partId] ?? {},
|
|
38
|
+
...patch
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
setOverlay(next);
|
|
42
|
+
onChange?.(applyOverlay(authored, next));
|
|
43
|
+
};
|
|
44
|
+
return /* @__PURE__ */ jsx(CircuitScene, {
|
|
45
|
+
doc,
|
|
46
|
+
flow,
|
|
47
|
+
ariaLabel,
|
|
48
|
+
onPartTap: tap
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
export { CircuitPlayer };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { CircuitDoc } from "./contract.mjs";
|
|
2
|
+
import { PointerEvent, ReactNode } from "react";
|
|
3
|
+
import { Vec2 } from "@classytic/stage";
|
|
4
|
+
|
|
5
|
+
//#region src/build/CircuitScene.d.ts
|
|
6
|
+
interface CircuitEditorBag {
|
|
7
|
+
/** draw a small handle at every pin terminal (for wiring). */
|
|
8
|
+
showPins?: boolean;
|
|
9
|
+
/** a part body was clicked (select it). */
|
|
10
|
+
onSelect?: (partId: string) => void;
|
|
11
|
+
/** the pin a pending wire starts from (highlighted). */
|
|
12
|
+
wireStart?: {
|
|
13
|
+
partId: string;
|
|
14
|
+
pin: string;
|
|
15
|
+
};
|
|
16
|
+
/** pointer went down on a part body (begin a drag). */
|
|
17
|
+
onPartPointerDown?: (partId: string, e: PointerEvent) => void;
|
|
18
|
+
/** pointer went down on a pin: a click wires it, a drag moves the part (click-vs-drag). */
|
|
19
|
+
onPinPointerDown?: (partId: string, pin: string, e: PointerEvent) => void;
|
|
20
|
+
/** a wire edge was clicked (select / branch / delete — the editor decides). */
|
|
21
|
+
onWireClick?: (wireId: string) => void;
|
|
22
|
+
/** highlight one wire as selected. */
|
|
23
|
+
selectedWireId?: string;
|
|
24
|
+
/** pointer went down on the BODY of the selected wire (grab to bend / reroute it). */
|
|
25
|
+
onWireBodyDown?: (wireId: string, e: PointerEvent) => void;
|
|
26
|
+
/** pointer went down on an existing bend handle (drag to move that bend). */
|
|
27
|
+
onWireWaypointDown?: (wireId: string, index: number, e: PointerEvent) => void;
|
|
28
|
+
/** pointer went down on a wire's endpoint ring (drag to detach / re-target that end). */
|
|
29
|
+
onWireEndDown?: (wireId: string, end: 'a' | 'b', e: PointerEvent) => void;
|
|
30
|
+
/** the empty canvas was pressed (clear selection, or drop a routing bend while drawing). */
|
|
31
|
+
onBackground?: (e: PointerEvent) => void;
|
|
32
|
+
/** live rubber-band while drawing a wire: origin terminal → any dropped bends → the cursor. */
|
|
33
|
+
wirePreview?: {
|
|
34
|
+
from: Vec2;
|
|
35
|
+
mids?: Vec2[];
|
|
36
|
+
to: Vec2;
|
|
37
|
+
valid?: boolean;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
interface CircuitSceneProps {
|
|
41
|
+
doc: CircuitDoc;
|
|
42
|
+
/** show moving charge dots on live wires (default true). */
|
|
43
|
+
flow?: boolean;
|
|
44
|
+
ariaLabel?: string;
|
|
45
|
+
/** when set, tappable parts (e.g. switches) are clickable and call this with the part id. */
|
|
46
|
+
onPartTap?: (partId: string) => void;
|
|
47
|
+
/** highlight one part as selected (authoring). */
|
|
48
|
+
selectedId?: string;
|
|
49
|
+
/** authoring affordances; omit for the read-only / learner view. */
|
|
50
|
+
editor?: CircuitEditorBag;
|
|
51
|
+
}
|
|
52
|
+
declare function CircuitScene({
|
|
53
|
+
doc,
|
|
54
|
+
flow,
|
|
55
|
+
ariaLabel,
|
|
56
|
+
onPartTap,
|
|
57
|
+
selectedId,
|
|
58
|
+
editor
|
|
59
|
+
}: CircuitSceneProps): ReactNode;
|
|
60
|
+
//#endregion
|
|
61
|
+
export { CircuitEditorBag, CircuitScene, CircuitSceneProps };
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { FlowDots, JunctionDot, Wire } from "../kit/electronics.mjs";
|
|
4
|
+
import { useReducedMotion } from "../kit/anim.mjs";
|
|
5
|
+
import { getPart } from "./registry.mjs";
|
|
6
|
+
import { registerBuiltinParts } from "./parts/index.mjs";
|
|
7
|
+
import { partState, solveCircuit } from "./solve.mjs";
|
|
8
|
+
import { FLOW_EPS, nodeKey, pinKey, wireCurrents } from "./flow.mjs";
|
|
9
|
+
import { wirePolyline } from "./editor-ops.mjs";
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { useFrameLoop } from "@classytic/stage";
|
|
13
|
+
|
|
14
|
+
//#region src/build/CircuitScene.tsx
|
|
15
|
+
/**
|
|
16
|
+
* CircuitScene — pure render of a CircuitDoc. It solves the doc, draws a wire from
|
|
17
|
+
* every part pin to the node it names, places a junction dot where 3+ pins meet, and
|
|
18
|
+
* lets each PartDef draw its own glyph with the solved state. Live branches carry
|
|
19
|
+
* moving charge dots. No drawing logic lives here: parts own their look.
|
|
20
|
+
*
|
|
21
|
+
* Read-only by default. Pass `onPartTap` for click-to-operate (learner), or an
|
|
22
|
+
* `editor` bag for authoring (selection + pin handles for wiring). A node with no
|
|
23
|
+
* explicit `at` is drawn at the centroid of the terminals wired to it, so the
|
|
24
|
+
* author never has to place junctions by hand.
|
|
25
|
+
*/
|
|
26
|
+
registerBuiltinParts();
|
|
27
|
+
/** Manhattan route terminal → node: straight when aligned, else a single elbow (vertical-first). */
|
|
28
|
+
function orthRoute(a, b) {
|
|
29
|
+
if (a.x === b.x || a.y === b.y) return [[a.x, a.y], [b.x, b.y]];
|
|
30
|
+
return [
|
|
31
|
+
[a.x, a.y],
|
|
32
|
+
[a.x, b.y],
|
|
33
|
+
[b.x, b.y]
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
function CircuitScene({ doc, flow = true, ariaLabel = "circuit diagram", onPartTap, selectedId, editor }) {
|
|
37
|
+
const [phase, setPhase] = useState(0);
|
|
38
|
+
const reduce = useReducedMotion();
|
|
39
|
+
const W = doc.size?.w ?? 520;
|
|
40
|
+
const H = doc.size?.h ?? 220;
|
|
41
|
+
const sol = solveCircuit(doc);
|
|
42
|
+
const states = new Map(doc.parts.map((p) => [p.id, partState(p, sol)]));
|
|
43
|
+
const anyLive = [...states.values()].some((s) => s.live);
|
|
44
|
+
const wf = wireCurrents(doc, sol);
|
|
45
|
+
const termOf = (partId, pin) => {
|
|
46
|
+
const p = doc.parts.find((x) => x.id === partId);
|
|
47
|
+
const def = p && getPart(p.kind);
|
|
48
|
+
return def ? def.terminalAt(p, pin) : void 0;
|
|
49
|
+
};
|
|
50
|
+
const rev = (s) => [...s].reverse();
|
|
51
|
+
const termsByNode = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const p of doc.parts) {
|
|
53
|
+
const def = getPart(p.kind);
|
|
54
|
+
if (!def) continue;
|
|
55
|
+
for (const pin of def.pins) {
|
|
56
|
+
const nid = p.pins[pin];
|
|
57
|
+
if (!nid) continue;
|
|
58
|
+
(termsByNode.get(nid) ?? termsByNode.set(nid, []).get(nid)).push(def.terminalAt(p, pin));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const explicit = new Map(doc.nodes.map((n) => [n.id, n.at]));
|
|
62
|
+
const nodePos = (nid) => {
|
|
63
|
+
const fixed = explicit.get(nid);
|
|
64
|
+
if (fixed) return fixed;
|
|
65
|
+
const ts = termsByNode.get(nid) ?? [];
|
|
66
|
+
return ts.length ? {
|
|
67
|
+
x: ts.reduce((s, t) => s + t.x, 0) / ts.length,
|
|
68
|
+
y: ts.reduce((s, t) => s + t.y, 0) / ts.length
|
|
69
|
+
} : {
|
|
70
|
+
x: W / 2,
|
|
71
|
+
y: H / 2
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const drawnNode = (nid) => explicit.has(nid) || (termsByNode.get(nid)?.length ?? 0) >= 2;
|
|
75
|
+
useFrameLoop((f) => setPhase((p) => (p + f.dtMs / 1400) % 1), { running: flow && anyLive && !reduce });
|
|
76
|
+
const wires = [];
|
|
77
|
+
const live = [];
|
|
78
|
+
const pinHandles = [];
|
|
79
|
+
const wireEndpoints = /* @__PURE__ */ new Map();
|
|
80
|
+
const bump = (k) => {
|
|
81
|
+
wireEndpoints.set(k, (wireEndpoints.get(k) ?? 0) + 1);
|
|
82
|
+
};
|
|
83
|
+
for (const p of doc.parts) {
|
|
84
|
+
const def = getPart(p.kind);
|
|
85
|
+
if (!def) continue;
|
|
86
|
+
for (const pin of def.pins) {
|
|
87
|
+
const nid = p.pins[pin];
|
|
88
|
+
if (!nid || !drawnNode(nid)) continue;
|
|
89
|
+
const seg = orthRoute(def.terminalAt(p, pin), nodePos(nid));
|
|
90
|
+
const I = wf.current(pinKey(p.id, pin), nodeKey(nid));
|
|
91
|
+
const isLive = Math.abs(I) > FLOW_EPS;
|
|
92
|
+
wires.push(/* @__PURE__ */ jsx(Wire, {
|
|
93
|
+
points: seg,
|
|
94
|
+
live: isLive
|
|
95
|
+
}, `stub-${p.id}-${pin}`));
|
|
96
|
+
if (isLive) live.push(I > 0 ? rev(seg) : seg);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const w of doc.wires ?? []) {
|
|
100
|
+
const ta = termOf(w.a.partId, w.a.pin);
|
|
101
|
+
const tb = termOf(w.b.partId, w.b.pin);
|
|
102
|
+
if (!ta || !tb) continue;
|
|
103
|
+
const seg = wirePolyline(ta, w.mid ?? [], tb).map((p) => [p.x, p.y]);
|
|
104
|
+
const ptStr = seg.map((q) => `${q[0]},${q[1]}`).join(" ");
|
|
105
|
+
const isSel = editor?.selectedWireId === w.id;
|
|
106
|
+
const I = wf.current(pinKey(w.a.partId, w.a.pin), pinKey(w.b.partId, w.b.pin));
|
|
107
|
+
const isLive = Math.abs(I) > FLOW_EPS;
|
|
108
|
+
wires.push(/* @__PURE__ */ jsxs("g", {
|
|
109
|
+
style: { cursor: editor?.onWireClick ? "pointer" : "default" },
|
|
110
|
+
onClick: editor?.onWireClick ? (e) => {
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
editor.onWireClick(w.id);
|
|
113
|
+
} : void 0,
|
|
114
|
+
children: [
|
|
115
|
+
isSel && /* @__PURE__ */ jsx("polyline", {
|
|
116
|
+
points: ptStr,
|
|
117
|
+
fill: "none",
|
|
118
|
+
stroke: "var(--stage-accent)",
|
|
119
|
+
strokeWidth: 6,
|
|
120
|
+
strokeLinecap: "round",
|
|
121
|
+
strokeLinejoin: "round",
|
|
122
|
+
opacity: .4
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ jsx(Wire, {
|
|
125
|
+
points: seg,
|
|
126
|
+
live: isLive
|
|
127
|
+
}),
|
|
128
|
+
editor?.onWireClick && /* @__PURE__ */ jsx("polyline", {
|
|
129
|
+
points: ptStr,
|
|
130
|
+
fill: "none",
|
|
131
|
+
stroke: "transparent",
|
|
132
|
+
strokeWidth: isSel ? 20 : 14,
|
|
133
|
+
strokeLinecap: "round",
|
|
134
|
+
strokeLinejoin: "round",
|
|
135
|
+
style: {
|
|
136
|
+
cursor: isSel ? "move" : "pointer",
|
|
137
|
+
pointerEvents: "stroke"
|
|
138
|
+
},
|
|
139
|
+
onPointerDown: isSel && editor.onWireBodyDown ? (e) => {
|
|
140
|
+
e.stopPropagation();
|
|
141
|
+
editor.onWireBodyDown(w.id, e);
|
|
142
|
+
} : void 0
|
|
143
|
+
}),
|
|
144
|
+
isSel && (w.mid ?? []).map((m, i) => /* @__PURE__ */ jsx("rect", {
|
|
145
|
+
x: m.x - 5,
|
|
146
|
+
y: m.y - 5,
|
|
147
|
+
width: 10,
|
|
148
|
+
height: 10,
|
|
149
|
+
rx: 2,
|
|
150
|
+
fill: "var(--stage-bg)",
|
|
151
|
+
stroke: "var(--stage-accent)",
|
|
152
|
+
strokeWidth: 2,
|
|
153
|
+
style: { cursor: "move" },
|
|
154
|
+
onPointerDown: editor.onWireWaypointDown ? (e) => {
|
|
155
|
+
e.stopPropagation();
|
|
156
|
+
editor.onWireWaypointDown(w.id, i, e);
|
|
157
|
+
} : void 0
|
|
158
|
+
}, `wp-${i}`))
|
|
159
|
+
]
|
|
160
|
+
}, `wire-${w.id}`));
|
|
161
|
+
if (isLive) live.push(I > 0 ? rev(seg) : seg);
|
|
162
|
+
bump(`${w.a.partId} ${w.a.pin}`);
|
|
163
|
+
bump(`${w.b.partId} ${w.b.pin}`);
|
|
164
|
+
}
|
|
165
|
+
if (editor?.showPins) for (const p of doc.parts) {
|
|
166
|
+
const def = getPart(p.kind);
|
|
167
|
+
if (!def) continue;
|
|
168
|
+
for (const pin of def.pins) {
|
|
169
|
+
const t = def.terminalAt(p, pin);
|
|
170
|
+
const armed = editor.wireStart?.partId === p.id && editor.wireStart?.pin === pin;
|
|
171
|
+
pinHandles.push(/* @__PURE__ */ jsx("circle", {
|
|
172
|
+
cx: t.x,
|
|
173
|
+
cy: t.y,
|
|
174
|
+
r: armed ? 7 : 5.5,
|
|
175
|
+
fill: armed ? "var(--stage-accent)" : "var(--stage-bg)",
|
|
176
|
+
stroke: "var(--stage-accent)",
|
|
177
|
+
strokeWidth: 2,
|
|
178
|
+
style: { cursor: "crosshair" },
|
|
179
|
+
role: "button",
|
|
180
|
+
"aria-label": `${pin} of ${def.label}`,
|
|
181
|
+
onPointerDown: (e) => {
|
|
182
|
+
e.stopPropagation();
|
|
183
|
+
editor.onPinPointerDown?.(p.id, pin, e);
|
|
184
|
+
}
|
|
185
|
+
}, `pin-${p.id}-${pin}`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const dots = [];
|
|
189
|
+
for (const [nid, ts] of termsByNode) if (drawnNode(nid) && ts.length > 2) {
|
|
190
|
+
const np = nodePos(nid);
|
|
191
|
+
dots.push(/* @__PURE__ */ jsx(JunctionDot, {
|
|
192
|
+
x: np.x,
|
|
193
|
+
y: np.y,
|
|
194
|
+
live: false
|
|
195
|
+
}, `j-${nid}`));
|
|
196
|
+
}
|
|
197
|
+
for (const [key, n] of wireEndpoints) if (n >= 2) {
|
|
198
|
+
const [partId, pin] = key.split(" ");
|
|
199
|
+
const t = partId && pin ? termOf(partId, pin) : void 0;
|
|
200
|
+
if (t) dots.push(/* @__PURE__ */ jsx(JunctionDot, {
|
|
201
|
+
x: t.x,
|
|
202
|
+
y: t.y,
|
|
203
|
+
live: false
|
|
204
|
+
}, `jw-${key}`));
|
|
205
|
+
}
|
|
206
|
+
return /* @__PURE__ */ jsx("div", {
|
|
207
|
+
style: {
|
|
208
|
+
borderRadius: 14,
|
|
209
|
+
overflow: "hidden",
|
|
210
|
+
background: "var(--stage-bg)",
|
|
211
|
+
border: "1px solid var(--stage-grid)"
|
|
212
|
+
},
|
|
213
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
214
|
+
viewBox: `0 0 ${W} ${H}`,
|
|
215
|
+
width: "100%",
|
|
216
|
+
role: "img",
|
|
217
|
+
"aria-label": ariaLabel,
|
|
218
|
+
children: [
|
|
219
|
+
editor?.onBackground && /* @__PURE__ */ jsx("rect", {
|
|
220
|
+
x: 0,
|
|
221
|
+
y: 0,
|
|
222
|
+
width: W,
|
|
223
|
+
height: H,
|
|
224
|
+
fill: "transparent",
|
|
225
|
+
onPointerDown: (e) => editor.onBackground(e)
|
|
226
|
+
}),
|
|
227
|
+
wires,
|
|
228
|
+
flow && !reduce && live.map((seg, k) => /* @__PURE__ */ jsx(FlowDots, {
|
|
229
|
+
points: seg,
|
|
230
|
+
phase,
|
|
231
|
+
spacing: 54,
|
|
232
|
+
r: 2.3
|
|
233
|
+
}, `f${k}`)),
|
|
234
|
+
dots,
|
|
235
|
+
editor?.wirePreview && (() => {
|
|
236
|
+
const { from, mids = [], to, valid } = editor.wirePreview;
|
|
237
|
+
const pts = [
|
|
238
|
+
from,
|
|
239
|
+
...mids,
|
|
240
|
+
to
|
|
241
|
+
];
|
|
242
|
+
const col = "var(--stage-accent)";
|
|
243
|
+
return /* @__PURE__ */ jsxs("g", {
|
|
244
|
+
style: { pointerEvents: "none" },
|
|
245
|
+
children: [
|
|
246
|
+
/* @__PURE__ */ jsx("polyline", {
|
|
247
|
+
points: pts.map((q) => `${q.x},${q.y}`).join(" "),
|
|
248
|
+
fill: "none",
|
|
249
|
+
stroke: col,
|
|
250
|
+
strokeWidth: 2.5,
|
|
251
|
+
strokeDasharray: "5 4",
|
|
252
|
+
strokeLinecap: "round",
|
|
253
|
+
strokeLinejoin: "round",
|
|
254
|
+
opacity: .9
|
|
255
|
+
}),
|
|
256
|
+
mids.map((m, i) => /* @__PURE__ */ jsx("circle", {
|
|
257
|
+
cx: m.x,
|
|
258
|
+
cy: m.y,
|
|
259
|
+
r: 3.5,
|
|
260
|
+
fill: col
|
|
261
|
+
}, i)),
|
|
262
|
+
/* @__PURE__ */ jsx("circle", {
|
|
263
|
+
cx: to.x,
|
|
264
|
+
cy: to.y,
|
|
265
|
+
r: valid ? 6 : 5,
|
|
266
|
+
fill: valid ? col : "var(--stage-bg)",
|
|
267
|
+
stroke: col,
|
|
268
|
+
strokeWidth: 2
|
|
269
|
+
})
|
|
270
|
+
]
|
|
271
|
+
});
|
|
272
|
+
})(),
|
|
273
|
+
doc.parts.map((p) => {
|
|
274
|
+
const def = getPart(p.kind);
|
|
275
|
+
if (!def) return null;
|
|
276
|
+
const glyph = def.render(p, states.get(p.id));
|
|
277
|
+
const tappable = !!onPartTap && (def.tap?.(p) ?? null) !== null;
|
|
278
|
+
const selectable = !!editor?.onSelect;
|
|
279
|
+
const selected = selectedId === p.id;
|
|
280
|
+
if (!tappable && !selectable && !selected) return glyph;
|
|
281
|
+
const onActivate = selectable ? () => editor.onSelect(p.id) : tappable ? () => onPartTap(p.id) : void 0;
|
|
282
|
+
const label = !selectable && tappable ? `toggle ${def.label.toLowerCase()}` : `select ${def.label.toLowerCase()}`;
|
|
283
|
+
return /* @__PURE__ */ jsxs("g", {
|
|
284
|
+
role: onActivate ? "button" : void 0,
|
|
285
|
+
tabIndex: onActivate ? 0 : void 0,
|
|
286
|
+
"aria-label": onActivate ? label : void 0,
|
|
287
|
+
style: { cursor: editor?.onPartPointerDown ? "move" : onActivate ? "pointer" : "default" },
|
|
288
|
+
onPointerDown: editor?.onPartPointerDown ? (e) => editor.onPartPointerDown(p.id, e) : void 0,
|
|
289
|
+
onClick: onActivate ? (e) => {
|
|
290
|
+
e.stopPropagation();
|
|
291
|
+
onActivate();
|
|
292
|
+
} : void 0,
|
|
293
|
+
onKeyDown: onActivate ? (e) => {
|
|
294
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
295
|
+
e.preventDefault();
|
|
296
|
+
onActivate();
|
|
297
|
+
}
|
|
298
|
+
} : void 0,
|
|
299
|
+
children: [
|
|
300
|
+
selected && /* @__PURE__ */ jsx("rect", {
|
|
301
|
+
x: p.at.x - 40,
|
|
302
|
+
y: p.at.y - 30,
|
|
303
|
+
width: 80,
|
|
304
|
+
height: 60,
|
|
305
|
+
rx: 8,
|
|
306
|
+
fill: "none",
|
|
307
|
+
stroke: "var(--stage-accent)",
|
|
308
|
+
strokeWidth: 1.5,
|
|
309
|
+
strokeDasharray: "4 3"
|
|
310
|
+
}),
|
|
311
|
+
(tappable || selectable) && /* @__PURE__ */ jsx("rect", {
|
|
312
|
+
x: p.at.x - 38,
|
|
313
|
+
y: p.at.y - 28,
|
|
314
|
+
width: 76,
|
|
315
|
+
height: 56,
|
|
316
|
+
rx: 8,
|
|
317
|
+
fill: "transparent"
|
|
318
|
+
}),
|
|
319
|
+
glyph
|
|
320
|
+
]
|
|
321
|
+
}, `hit-${p.id}`);
|
|
322
|
+
}),
|
|
323
|
+
doc.parts.map((p) => {
|
|
324
|
+
const dmg = states.get(p.id)?.damage;
|
|
325
|
+
if (!dmg) return null;
|
|
326
|
+
const c = "var(--stage-danger, #e03131)";
|
|
327
|
+
return /* @__PURE__ */ jsxs("g", {
|
|
328
|
+
style: { pointerEvents: "none" },
|
|
329
|
+
children: [/* @__PURE__ */ jsx("rect", {
|
|
330
|
+
x: p.at.x - 34,
|
|
331
|
+
y: p.at.y - 24,
|
|
332
|
+
width: 68,
|
|
333
|
+
height: 48,
|
|
334
|
+
rx: 8,
|
|
335
|
+
fill: "color-mix(in oklab, var(--stage-danger, #e03131) 14%, transparent)",
|
|
336
|
+
stroke: c,
|
|
337
|
+
strokeWidth: 2,
|
|
338
|
+
strokeDasharray: "3 3"
|
|
339
|
+
}), /* @__PURE__ */ jsx("text", {
|
|
340
|
+
x: p.at.x,
|
|
341
|
+
y: p.at.y - 30,
|
|
342
|
+
fill: c,
|
|
343
|
+
fontSize: 14,
|
|
344
|
+
fontWeight: 800,
|
|
345
|
+
textAnchor: "middle",
|
|
346
|
+
dominantBaseline: "auto",
|
|
347
|
+
children: dmg === "overpower" ? "🔥" : "⚡"
|
|
348
|
+
})]
|
|
349
|
+
}, `dmg-${p.id}`);
|
|
350
|
+
}),
|
|
351
|
+
pinHandles,
|
|
352
|
+
editor?.selectedWireId && editor.onWireEndDown && (() => {
|
|
353
|
+
const w = (doc.wires ?? []).find((x) => x.id === editor.selectedWireId);
|
|
354
|
+
if (!w) return null;
|
|
355
|
+
return [{
|
|
356
|
+
end: "a",
|
|
357
|
+
t: termOf(w.a.partId, w.a.pin)
|
|
358
|
+
}, {
|
|
359
|
+
end: "b",
|
|
360
|
+
t: termOf(w.b.partId, w.b.pin)
|
|
361
|
+
}].map(({ end, t }) => t && /* @__PURE__ */ jsx("circle", {
|
|
362
|
+
cx: t.x,
|
|
363
|
+
cy: t.y,
|
|
364
|
+
r: 7.5,
|
|
365
|
+
fill: "var(--stage-bg)",
|
|
366
|
+
stroke: "var(--stage-accent)",
|
|
367
|
+
strokeWidth: 2.5,
|
|
368
|
+
style: { cursor: "grab" },
|
|
369
|
+
role: "button",
|
|
370
|
+
"aria-label": `wire end ${end}`,
|
|
371
|
+
onPointerDown: (e) => {
|
|
372
|
+
e.stopPropagation();
|
|
373
|
+
editor.onWireEndDown(w.id, end, e);
|
|
374
|
+
}
|
|
375
|
+
}, `we-${end}`));
|
|
376
|
+
})()
|
|
377
|
+
]
|
|
378
|
+
})
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
//#endregion
|
|
383
|
+
export { CircuitScene };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { Vec2 } from "@classytic/stage";
|
|
3
|
+
import { Elem } from "@classytic/stage/circuit";
|
|
4
|
+
|
|
5
|
+
//#region src/build/contract.d.ts
|
|
6
|
+
type PartKind = string;
|
|
7
|
+
/** A placed component instance. */
|
|
8
|
+
interface PartInstance {
|
|
9
|
+
id: string;
|
|
10
|
+
kind: PartKind;
|
|
11
|
+
/** centre on the canvas, pixels. */
|
|
12
|
+
at: Vec2;
|
|
13
|
+
/** 'h' = terminals left/right, 'v' = top/bottom (2-terminal parts). */
|
|
14
|
+
orient?: 'h' | 'v';
|
|
15
|
+
/** value / vth / closed / label … (the part's tunables). */
|
|
16
|
+
props?: Record<string, number | string | boolean>;
|
|
17
|
+
/** pin id → node id it connects to. */
|
|
18
|
+
pins: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/** An electrical node (junction) with a canvas position. `id === 'gnd'` is the reference. */
|
|
21
|
+
interface CNode {
|
|
22
|
+
id: string;
|
|
23
|
+
at: Vec2;
|
|
24
|
+
}
|
|
25
|
+
/** One end of a wire: a specific pin of a specific part. */
|
|
26
|
+
interface PinRef {
|
|
27
|
+
partId: string;
|
|
28
|
+
pin: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A wire is its OWN edge between two pin terminals. Parts keep their positions; the
|
|
32
|
+
* wire routes between them. Electrical nets are derived by union-find over wires (plus
|
|
33
|
+
* any pins that share a node id), so connecting two pins never drags parts together.
|
|
34
|
+
*/
|
|
35
|
+
interface Wire {
|
|
36
|
+
id: string;
|
|
37
|
+
a: PinRef;
|
|
38
|
+
b: PinRef;
|
|
39
|
+
/** optional bend points the wire routes through (drag to position the wire's path). */
|
|
40
|
+
mid?: Vec2[];
|
|
41
|
+
}
|
|
42
|
+
/** A circuit, the serializable source of truth. */
|
|
43
|
+
interface CircuitDoc {
|
|
44
|
+
parts: PartInstance[];
|
|
45
|
+
nodes: CNode[];
|
|
46
|
+
/** explicit wire edges (the editor's connection model). */
|
|
47
|
+
wires?: Wire[];
|
|
48
|
+
size?: {
|
|
49
|
+
w: number;
|
|
50
|
+
h: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
interface CircuitSolution {
|
|
54
|
+
/** node voltage by node id (each pin's node id resolves to its net voltage). */
|
|
55
|
+
nodeV: Record<string, number>;
|
|
56
|
+
/** branch current by part id (sources / diodes / mosfets / closed switches). */
|
|
57
|
+
current: Record<string, number>;
|
|
58
|
+
ok: boolean;
|
|
59
|
+
/** part ids whose two terminals collapsed onto the same net (a dead short across a source). */
|
|
60
|
+
shorted: string[];
|
|
61
|
+
/** voltage at a specific pin (net-accurate). */
|
|
62
|
+
pinV: (partId: string, pin: string) => number;
|
|
63
|
+
/** net number a pin belongs to (0 = ground). */
|
|
64
|
+
net: (partId: string, pin: string) => number;
|
|
65
|
+
}
|
|
66
|
+
/** What a part's glyph needs from the solve to draw itself. */
|
|
67
|
+
interface PartState {
|
|
68
|
+
/** current is flowing through this part. */
|
|
69
|
+
live: boolean;
|
|
70
|
+
/** representative current through the part, amps (signed). */
|
|
71
|
+
i: number;
|
|
72
|
+
/** representative voltage across the part, volts. */
|
|
73
|
+
v: number;
|
|
74
|
+
/** power the part dissipates, watts (|v·i|). */
|
|
75
|
+
power: number;
|
|
76
|
+
/** the part is being destroyed: it exceeds a rated limit. `overpower` = dissipating more
|
|
77
|
+
* than its power rating (it overheats / burns out); `overvoltage` = the voltage across it
|
|
78
|
+
* exceeds its breakdown rating. undefined = within ratings. Drives the "you fried it" signal. */
|
|
79
|
+
damage?: 'overpower' | 'overvoltage';
|
|
80
|
+
/** voltage at one of this part's pins. */
|
|
81
|
+
pinV: (pin: string) => number;
|
|
82
|
+
}
|
|
83
|
+
/** A tunable a part exposes to the inspector as a friendly labelled field (vs a raw prop key). */
|
|
84
|
+
interface PartControl {
|
|
85
|
+
/** the prop key it edits. */
|
|
86
|
+
key: string;
|
|
87
|
+
label: string;
|
|
88
|
+
/** unit shown after the value (e.g. 'V', 'Ω'). */
|
|
89
|
+
unit?: string;
|
|
90
|
+
min?: number;
|
|
91
|
+
max?: number;
|
|
92
|
+
step?: number;
|
|
93
|
+
}
|
|
94
|
+
/** The one extension point: register a part once to make it placeable + solvable + drawable. */
|
|
95
|
+
interface PartDef {
|
|
96
|
+
kind: PartKind;
|
|
97
|
+
label: string;
|
|
98
|
+
/** pin ids (e.g. ['a','b'] or ['d','s','g']). */
|
|
99
|
+
pins: string[];
|
|
100
|
+
defaultProps?: Record<string, number | string | boolean>;
|
|
101
|
+
/** the part's tunables, rendered as labelled inspector fields (falls back to defaultProps). */
|
|
102
|
+
controls?: PartControl[];
|
|
103
|
+
/** world position of a pin's terminal, given the instance (handles orientation). */
|
|
104
|
+
terminalAt(inst: PartInstance, pin: string): Vec2;
|
|
105
|
+
/** map this instance to engine elements; nodeNum(pin) resolves a pin to an engine node number. */
|
|
106
|
+
toElems(inst: PartInstance, nodeNum: (pin: string) => number): Elem[];
|
|
107
|
+
/** draw the glyph at the instance centre with its solved state. */
|
|
108
|
+
render(inst: PartInstance, state: PartState): ReactNode;
|
|
109
|
+
/**
|
|
110
|
+
* If the part can be operated by tapping it (e.g. a switch), return the props patch
|
|
111
|
+
* a tap applies; return null for inert parts. Drives click-on-the-circuit interaction.
|
|
112
|
+
*/
|
|
113
|
+
tap?(inst: PartInstance): Record<string, number | string | boolean> | null;
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
export { CNode, CircuitDoc, CircuitSolution, PartDef, PartInstance, PartKind, PartState, PinRef, Wire };
|