├── photons ├── metal.jpg ├── _linear_polarization_free.html ├── _arrow_multiplication.html ├── _hong_ou_mandel_effect.html ├── _linear_polarization_exp1.html ├── _linear_polarization_exp2.html ├── _linear_polarization_exp3.html ├── _linear_polarization_exp4.html ├── _polarizing_beam_splitter.html ├── _seeing_in_the_dark_part1.html ├── _seeing_in_the_dark_part2.html ├── _mach_zehnder_exp1.html ├── _mach_zehnder_exp2.html ├── _mach_zehnder_exp3.html ├── lib │ ├── jquery.hammer.js │ ├── raphael-group.js │ └── mirror.js ├── readme.md └── linear-polarization-exp1.js ├── qed ├── images │ ├── bt_go.png │ ├── grid.png │ ├── knob.png │ ├── bt_fast.png │ ├── bt_help.png │ ├── bt_kill.png │ ├── bt_next.png │ ├── bt_undo.png │ ├── loader.gif │ ├── bt_fast_on.png │ ├── bt_graph.png │ ├── button_go.png │ ├── button_up.png │ ├── detector.png │ ├── fast_mode.png │ ├── source_red.png │ ├── sqrt_0.96.png │ ├── bt_fast_off.png │ ├── bt_slit_wide.png │ ├── button_down.png │ ├── button_graph.png │ ├── button_help.png │ ├── button_next.png │ ├── clocks_label.png │ ├── source_blue.png │ ├── source_green.png │ ├── bt_slit_narrow.png │ ├── probability_box.png │ ├── detector_nolabel.png │ ├── label_photonpaths.png │ ├── source_blue_nolabel.png │ ├── source_green_nolabel.png │ ├── source_red_nolabel.png │ └── probability_box - Copy.png ├── readme.md ├── _Exp1.html ├── _FreeMode.html ├── _FreeMode2.html ├── 20_DoubleSlit.html ├── 01_DetectorInGlass.html ├── _DetectorInGlass.html ├── 10_SingleSlitIntro.html ├── 11_SingleSlitColor.html ├── 12_SingleSlitWidth.html ├── 20_DoubleSlitScreen.html ├── 21_DoubleSlitScreen.html ├── 22_DoubleSlitDetector.html ├── 02_DetectorOutsideGlass.html ├── 22_DoubleSlitDetectors.html ├── 30_NormalizationLens.html ├── _DetectorOutsideGlass.html ├── _DetectorsInAndOutGlass.html ├── _InteractionFreeDoubleSlit.html ├── 00__DetectorsInAndOutGlass.html ├── lib │ ├── jquery.hammer.js │ └── raphael-group.js ├── qed_styles.css ├── free.js ├── free2.js ├── exp_30_normalization_lens.js ├── exp_11_single_slit_color.js ├── exp_12_single_slit_width.js └── exp_00_detectors_in_and_outside_glass.js ├── sterngerlach ├── img │ ├── gate.png │ ├── pass.png │ ├── sky.png │ ├── ignore.png │ ├── source.png │ ├── symbols.png │ ├── button_next.png │ ├── no_texture.png │ ├── sprite_plus.png │ ├── status_icon.png │ ├── symbols_big.png │ ├── sprite_minus.png │ ├── bell_light_minus.png │ ├── bell_light_plus.png │ ├── sprite_plus_red.png │ ├── sprite_minus_blue.png │ └── analyzer_interferometer.png ├── readme.md ├── sge_exp_1-11_watching.html ├── sge_exp_2-01_epr.html ├── sge_exp_1-06_three_analyzers.html ├── sge_exp_1-12_delayed_choice.html ├── sge_exp_1-02_sg_atoms.html ├── sge_exp_1-03_analyzer_intro.html ├── sge_exp_1-08_al_intro.html ├── sge_exp_1-07_arbitrary_angles.html ├── sge_exp_2-02_bell_intro.html ├── sge_exp_1-05_perpendicular_analyzers.html ├── sge_exp_2-03_bell_quantum.html ├── sge_exp_2-05_bell_test.html ├── sge_exp_1-09_al_known_states.html ├── sge_exp_2-04_bell_determinism.html ├── sge_exp_1-10_al_classical_vs_quantum.html ├── sge_exp_1-04_repeated_measurements.html ├── sge_exp_1-01_sg_current_loops.html └── sge_styles.css ├── probability ├── coin_atlas.png ├── readme.md ├── 03_birthday.html ├── 01_coins.html ├── 02_penney.html ├── 01_coins.css ├── 03_birthday.css ├── 02_penney.css ├── 03_birthday.js └── 01_coins.js ├── readme.md └── electrons.html /photons/metal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/photons/metal.jpg -------------------------------------------------------------------------------- /qed/images/bt_go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_go.png -------------------------------------------------------------------------------- /qed/images/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/grid.png -------------------------------------------------------------------------------- /qed/images/knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/knob.png -------------------------------------------------------------------------------- /qed/images/bt_fast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_fast.png -------------------------------------------------------------------------------- /qed/images/bt_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_help.png -------------------------------------------------------------------------------- /qed/images/bt_kill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_kill.png -------------------------------------------------------------------------------- /qed/images/bt_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_next.png -------------------------------------------------------------------------------- /qed/images/bt_undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_undo.png -------------------------------------------------------------------------------- /qed/images/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/loader.gif -------------------------------------------------------------------------------- /qed/images/bt_fast_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_fast_on.png -------------------------------------------------------------------------------- /qed/images/bt_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_graph.png -------------------------------------------------------------------------------- /qed/images/button_go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/button_go.png -------------------------------------------------------------------------------- /qed/images/button_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/button_up.png -------------------------------------------------------------------------------- /qed/images/detector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/detector.png -------------------------------------------------------------------------------- /qed/images/fast_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/fast_mode.png -------------------------------------------------------------------------------- /qed/images/source_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/source_red.png -------------------------------------------------------------------------------- /qed/images/sqrt_0.96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/sqrt_0.96.png -------------------------------------------------------------------------------- /sterngerlach/img/gate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/gate.png -------------------------------------------------------------------------------- /sterngerlach/img/pass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/pass.png -------------------------------------------------------------------------------- /sterngerlach/img/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/sky.png -------------------------------------------------------------------------------- /probability/coin_atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/probability/coin_atlas.png -------------------------------------------------------------------------------- /qed/images/bt_fast_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_fast_off.png -------------------------------------------------------------------------------- /qed/images/bt_slit_wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_slit_wide.png -------------------------------------------------------------------------------- /qed/images/button_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/button_down.png -------------------------------------------------------------------------------- /qed/images/button_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/button_graph.png -------------------------------------------------------------------------------- /qed/images/button_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/button_help.png -------------------------------------------------------------------------------- /qed/images/button_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/button_next.png -------------------------------------------------------------------------------- /qed/images/clocks_label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/clocks_label.png -------------------------------------------------------------------------------- /qed/images/source_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/source_blue.png -------------------------------------------------------------------------------- /qed/images/source_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/source_green.png -------------------------------------------------------------------------------- /sterngerlach/img/ignore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/ignore.png -------------------------------------------------------------------------------- /sterngerlach/img/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/source.png -------------------------------------------------------------------------------- /qed/images/bt_slit_narrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/bt_slit_narrow.png -------------------------------------------------------------------------------- /qed/images/probability_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/probability_box.png -------------------------------------------------------------------------------- /sterngerlach/img/symbols.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/symbols.png -------------------------------------------------------------------------------- /qed/images/detector_nolabel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/detector_nolabel.png -------------------------------------------------------------------------------- /qed/images/label_photonpaths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/label_photonpaths.png -------------------------------------------------------------------------------- /sterngerlach/img/button_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/button_next.png -------------------------------------------------------------------------------- /sterngerlach/img/no_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/no_texture.png -------------------------------------------------------------------------------- /sterngerlach/img/sprite_plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/sprite_plus.png -------------------------------------------------------------------------------- /sterngerlach/img/status_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/status_icon.png -------------------------------------------------------------------------------- /sterngerlach/img/symbols_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/symbols_big.png -------------------------------------------------------------------------------- /qed/images/source_blue_nolabel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/source_blue_nolabel.png -------------------------------------------------------------------------------- /qed/images/source_green_nolabel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/source_green_nolabel.png -------------------------------------------------------------------------------- /qed/images/source_red_nolabel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/source_red_nolabel.png -------------------------------------------------------------------------------- /sterngerlach/img/sprite_minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/sprite_minus.png -------------------------------------------------------------------------------- /qed/images/probability_box - Copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/qed/images/probability_box - Copy.png -------------------------------------------------------------------------------- /sterngerlach/img/bell_light_minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/bell_light_minus.png -------------------------------------------------------------------------------- /sterngerlach/img/bell_light_plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/bell_light_plus.png -------------------------------------------------------------------------------- /sterngerlach/img/sprite_plus_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/sprite_plus_red.png -------------------------------------------------------------------------------- /sterngerlach/img/sprite_minus_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/sprite_minus_blue.png -------------------------------------------------------------------------------- /probability/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains basic simulations on classical probability, made for the "Quantum Mechanics for 2 | Everyone" course by Georgetown University. -------------------------------------------------------------------------------- /sterngerlach/img/analyzer_interferometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantum-mechanics-for-everyone/simulations/HEAD/sterngerlach/img/analyzer_interferometer.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | These are the simulations for the tutorials made for the "Quantum Mechanics for Everyone" course by 2 | Georgetown University. 3 | 4 | The course is available on EdX at: 5 | https://www.edx.org/course/quantum-mechanics-everyone-georgetownx-phyx-008-01x -------------------------------------------------------------------------------- /sterngerlach/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains the Stern-Gerlach Engine and simulations for the "Quantum Mechanics for 2 | Everyone" course. 3 | 4 | These are based on Daniel F. Styer's "The Strange World of Quantum Mechanics". 5 | 6 | The bulk of the code is on the sge_engine.js file, a 3D engine which simulates many possible 7 | setups for chained Stern-Gerlach experiments. 8 | 9 | The "lib" folder contains third-party libraries, which should be distributed with their respective 10 | licenses. -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-11_watching.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.11: Watching atoms 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_2-01_epr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 2.01: The Einstein-Podolsky-Rosen Paradox 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-06_three_analyzers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.06: Three analyzers 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-12_delayed_choice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.12: Delayed choice 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-02_sg_atoms.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.02: The Stern-Gerlach apparatus with atoms 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-03_analyzer_intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.03: The Stern-Gerlach Analyzer 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-08_al_intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.08: Analyzer Loops (Part 1: Introduction) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-07_arbitrary_angles.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.07: Analyzers at arbitrary angles 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_2-02_bell_intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 2.02: The Bell Experiment (Part 1: Introduction) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-05_perpendicular_analyzers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.05: Perpendicular analyzers 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_2-03_bell_quantum.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 2.03: The Bell Experiment (Part 2: Quantum Mechanics) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_2-05_bell_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 2.05: The Bell Experiment (Part 4: Testing the predictions) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-09_al_known_states.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.09: Analyzer Loops (Part 2: Atoms with a known state) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_2-04_bell_determinism.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 2.04: The Bell Experiment (Part 3: Local Determinism) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-10_al_classical_vs_quantum.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.10: Analyzer Loops (Part 3: Classical vs Quantum) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-04_repeated_measurements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.04: Repeated measurements with the Stern-Gerlach Analyzer 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sterngerlach/sge_exp_1-01_sg_current_loops.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - 1.01: Introduction to the the Stern-Gerlach apparatus with current loops 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /qed/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains quantum mechanics simulations made for the "Quantum Mechanics for Everyone" 2 | course by Georgetown University. 3 | 4 | This work was inspired and based on Richard P. Feynman's book "QED: The Strange Theory of Light and 5 | Matter". 6 | 7 | These simulations involve variations on the double slit experiment and examples of interference, 8 | which are visually explained via Feynman's path integral formulation of quantum mechanics. 9 | 10 | The bulk of the software resides in the qed_engine.js file, which contains the QED Engine code that 11 | is used by the simulations to assemble a "scene", and run simulations on the objects being used. 12 | 13 | The "lib" folder contains third-party libraries, which should be distributed with their respective 14 | licenses. -------------------------------------------------------------------------------- /photons/_linear_polarization_free.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Polarization Free Mode 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_arrow_multiplication.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Arrow Multplication Exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_hong_ou_mandel_effect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hong-Ou-Mandel Effect Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_linear_polarization_exp1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | First Polarization Exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_linear_polarization_exp2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Second Polarization Exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_linear_polarization_exp3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Third Polarization Exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_linear_polarization_exp4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Fourth Polarization Exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_polarizing_beam_splitter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Polarizating Beam Splitter Exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_seeing_in_the_dark_part1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PHOTON Seeing In The Dark: Part 1 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_seeing_in_the_dark_part2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PHOTON Seeing In The Dark: Part 2 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /photons/_mach_zehnder_exp1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Mach Zehnder Interferometer 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /photons/_mach_zehnder_exp2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Mach Zehnder Interferometer 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /photons/_mach_zehnder_exp3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Mach Zehnder Interferometer 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/_Exp1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qed/_FreeMode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qed/_FreeMode2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/20_DoubleSlit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/01_DetectorInGlass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qed/_DetectorInGlass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/10_SingleSlitIntro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/11_SingleSlitColor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/12_SingleSlitWidth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/20_DoubleSlitScreen.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qed/21_DoubleSlitScreen.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/22_DoubleSlitDetector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qed/02_DetectorOutsideGlass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qed/22_DoubleSlitDetectors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/30_NormalizationLens.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/_DetectorOutsideGlass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/_DetectorsInAndOutGlass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/_InteractionFreeDoubleSlit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Double Slit: Interaction Free Measurement 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /qed/00__DetectorsInAndOutGlass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics Engine 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qed/lib/jquery.hammer.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['jquery', 'hammerjs'], factory); 4 | } else if (typeof exports === 'object') { 5 | factory(require('jquery'), require('hammerjs')); 6 | } else { 7 | factory(jQuery, Hammer); 8 | } 9 | }(function($, Hammer) { 10 | function hammerify(el, options) { 11 | var $el = $(el); 12 | if(!$el.data("hammer")) { 13 | $el.data("hammer", new Hammer($el[0], options)); 14 | } 15 | } 16 | 17 | $.fn.hammer = function(options) { 18 | return this.each(function() { 19 | hammerify(this, options); 20 | }); 21 | }; 22 | 23 | // extend the emit method to also trigger jQuery events 24 | Hammer.Manager.prototype.emit = (function(originalEmit) { 25 | return function(type, data) { 26 | originalEmit.call(this, type, data); 27 | $(this.element).trigger({ 28 | type: type, 29 | gesture: data 30 | }); 31 | }; 32 | })(Hammer.Manager.prototype.emit); 33 | })); 34 | -------------------------------------------------------------------------------- /photons/lib/jquery.hammer.js: -------------------------------------------------------------------------------- 1 | (function(factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['jquery', 'hammerjs'], factory); 4 | } else if (typeof exports === 'object') { 5 | factory(require('jquery'), require('hammerjs')); 6 | } else { 7 | factory(jQuery, Hammer); 8 | } 9 | }(function($, Hammer) { 10 | function hammerify(el, options) { 11 | var $el = $(el); 12 | if(!$el.data("hammer")) { 13 | $el.data("hammer", new Hammer($el[0], options)); 14 | } 15 | } 16 | 17 | $.fn.hammer = function(options) { 18 | return this.each(function() { 19 | hammerify(this, options); 20 | }); 21 | }; 22 | 23 | // extend the emit method to also trigger jQuery events 24 | Hammer.Manager.prototype.emit = (function(originalEmit) { 25 | return function(type, data) { 26 | originalEmit.call(this, type, data); 27 | $(this.element).trigger({ 28 | type: type, 29 | gesture: data 30 | }); 31 | }; 32 | })(Hammer.Manager.prototype.emit); 33 | })); 34 | -------------------------------------------------------------------------------- /probability/03_birthday.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - Birthday Problem 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Birthday Problem

12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /photons/readme.md: -------------------------------------------------------------------------------- 1 | This directory contains simulations that use a code engine, the PHOTON 2 | engine (PHOTON-engine.js). This engine is capable 3 | of rendering 3D graphics using Three.js and WebGL, and SVG graphics 4 | using RaphaelJS. It uses 2D and 3D animations to teach students 5 | about the nature of photons. 6 | 7 | The engine supports exercises on: 8 | - [The polarization of light](https://en.wikipedia.org/wiki/Linear_polarization) 9 | - [Interaction-free measurement](https://en.wikipedia.org/wiki/Interaction-free_measurement) 10 | - [Polarizing beam splitter](https://en.wikipedia.org/wiki/Polarizer#Beam-splitting_polarizers) 11 | - [Mach-Zehnder interferometers](https://en.wikipedia.org/wiki/Mach%E2%80%93Zehnder_interferometer) 12 | - Determining probability amplitudes for complex events using a visual 13 | representation of complex number multiplication 14 | - [Hong-Ou-Mandel effect](https://en.wikipedia.org/wiki/Hong%E2%80%93Ou%E2%80%93Mandel_effect) 15 | 16 | Live demos: 17 | - [Arrow Multiplication](http://dylancutler.us/PHOTON_sims/_arrow_multiplication.html) 18 | - [PHoton Polarization](http://dylancutler.us/PHOTON_sims/_linear_polarization_exp1.html) 19 | - [Photon Polarization cont.](http://dylancutler.us/PHOTON_sims/_linear_polarization_free.html) 20 | - [Polarizing Beam Splitter](http://dylancutler.us/PHOTON_sims/_polarizing_beam_splitter.html) 21 | -------------------------------------------------------------------------------- /sterngerlach/sge_styles.css: -------------------------------------------------------------------------------- 1 | /* Open Sans */ 2 | @import url(https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,600,600italic); 3 | 4 | * { 5 | /* Prevent text highlight */ 6 | -webkit-tap-highlight-color: rgba(0,0,0,0); 7 | -webkit-touch-callout: none; 8 | -webkit-user-select: none; 9 | -khtml-user-select: none; 10 | -moz-user-select: none; 11 | -ms-user-select: none; 12 | user-select: none; 13 | font-family: 'Open Sans', sans-serif; 14 | } 15 | 16 | a:link, a:visited { color: #00C; text-decoration: none; } 17 | a:hover, a:active { color: #48F; text-decoration: underline; } 18 | 19 | canvas { 20 | -webkit-tap-highlight-color: rgba(0,0,0,0); 21 | -webkit-touch-callout: none; 22 | -webkit-user-select: none; 23 | -khtml-user-select: none; 24 | -moz-user-select: none; 25 | -ms-user-select: none; 26 | user-select: none; 27 | } 28 | 29 | html { width: 100%; } 30 | body { 31 | margin: 0; 32 | padding: 0; 33 | width: 100%; 34 | font-size: 16px; 35 | } 36 | 37 | html, body { height: 100%; overflow-x: hidden; } 38 | 39 | .SGE_Application { 40 | background-color: #CCC; 41 | } 42 | 43 | .SGE_Component { 44 | outline: 1px solid #888; 45 | } 46 | 47 | .SGE_Viewport3D { 48 | } 49 | 50 | .SGE_MessageBox { 51 | background-color: #FFF; 52 | font-size: 120%; 53 | } 54 | 55 | .SGE_MessageBox .contents { 56 | padding: 5px 10px 30px 10px; /* extra padding on the bottom looks better */ 57 | } 58 | .SGE_MessageBox .contents em { 59 | padding: 0 0.125em; 60 | } 61 | 62 | .SGE_MessageBox p { 63 | margin: 0 0 1.25em 0; 64 | } 65 | 66 | .SGE_MessageBox img { 67 | display: inline-block; 68 | vertical-align: middle; 69 | } 70 | 71 | .SGE_Button button { 72 | padding: 0; 73 | margin: 0; 74 | } 75 | 76 | .SGE_Canvas2D { 77 | } -------------------------------------------------------------------------------- /probability/01_coins.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - Coin Probabilities 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Coin flipper

12 | 13 |
14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 | 24 |
25 | 30 |
31 |
32 | Heads:0 (0%) 33 | Tails:0 (0%) 34 | Total:0 35 |
36 | 37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /probability/02_penney.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quantum Mechanics - Penney's Game 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Penney's Game

12 | 13 |
14 |
15 | YOU 16 | 17 | 18 | 19 |
00
20 |
21 | 22 | vs 23 | 24 |
25 | COMPUTER 26 | 27 | 28 | 29 |
00
30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 |

Matches: 0 / 100

38 |

You won 0%

39 |

Computer won 0%

40 |
41 |
42 | 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /qed/lib/raphael-group.js: -------------------------------------------------------------------------------- 1 | // https://github.com/rhyolight/Raphael-Plugins/blob/master/raphael.group.js 2 | 3 | Raphael.fn.group = function() { 4 | 5 | var r = this, 6 | cfg = (arguments[0] instanceof Array) ? {} : arguments[0], 7 | items = (arguments[0] instanceof Array) ? arguments[0] : arguments[1]; 8 | 9 | function Group(cfg, items) { 10 | var inst, 11 | set = r.set(items), 12 | group = r.raphael.vml ? 13 | document.createElement("group") : 14 | document.createElementNS("http://www.w3.org/2000/svg", "g"); 15 | 16 | r.canvas.appendChild(group); 17 | 18 | function updateScale(transform, scaleX, scaleY) { 19 | var scaleString = 'scale(' + scaleX + ' ' + scaleY + ')'; 20 | if (!transform) { 21 | return scaleString; 22 | } 23 | if (transform.indexOf('scale(') < 0) { 24 | return transform + ' ' + scaleString; 25 | } 26 | return transform.replace(/scale\(-?[0-9]*?\.?[0-9]*?\ -?[0-9]*?\.?[0-9]*?\)/, scaleString); 27 | } 28 | 29 | function updateRotation(transform, rotation) { 30 | var rotateString = 'rotate(' + rotation + ')'; 31 | if (!transform) { 32 | return rotateString; 33 | } 34 | if (transform.indexOf('rotate(') < 0) { 35 | return transform + ' ' + rotateString; 36 | } 37 | return transform.replace(/rotate\(-?[0-9]+(\.[0-9][0-9]*)?\)/, rotateString); 38 | } 39 | 40 | function updateTranslation(transform, x, y) { 41 | var translateString = 'translate(' + x + ' ' + y + ')'; 42 | if (!transform) { 43 | return translateString; 44 | } 45 | return transform.replace(/translate\(-?[0-9]*?\.?[0-9]*?\ -?[0-9]*?\.?[0-9]*?\)/, translateString); 46 | } 47 | 48 | inst = { 49 | scale: function (newScaleX, newScaleY) { 50 | var transform = group.getAttribute('transform'); 51 | group.setAttribute('transform', updateScale(transform, newScaleX, newScaleY)); 52 | return this; 53 | }, 54 | rotate: function(deg) { 55 | var transform = group.getAttribute('transform'); 56 | group.setAttribute('transform', updateRotation(transform, deg)); 57 | }, 58 | push: function(item) { 59 | function pushOneRaphaelVector(it){ 60 | var i; 61 | if (it.type === 'set') { 62 | for (i=0; i< it.length; i++) { 63 | pushOneRaphaelVector(it[i]); 64 | } 65 | } else { 66 | group.appendChild(it.node); 67 | set.push(it); 68 | } 69 | } 70 | pushOneRaphaelVector(item) 71 | return this; 72 | }, 73 | translate: function(newTranslateX, newTranslateY) { 74 | var transform = group.getAttribute('transform'); 75 | group.setAttribute('transform', updateTranslation(transform, newTranslateX, newTranslateY)); 76 | return this; 77 | }, 78 | getBBox: function() { 79 | return set.getBBox(); 80 | }, 81 | type: 'group', 82 | node: group 83 | }; 84 | 85 | return inst; 86 | } 87 | 88 | return Group(cfg, items); 89 | 90 | }; -------------------------------------------------------------------------------- /photons/lib/raphael-group.js: -------------------------------------------------------------------------------- 1 | // https://github.com/rhyolight/Raphael-Plugins/blob/master/raphael.group.js 2 | 3 | Raphael.fn.group = function() { 4 | 5 | var r = this, 6 | cfg = (arguments[0] instanceof Array) ? {} : arguments[0], 7 | items = (arguments[0] instanceof Array) ? arguments[0] : arguments[1]; 8 | 9 | function Group(cfg, items) { 10 | var inst, 11 | set = r.set(items), 12 | group = r.raphael.vml ? 13 | document.createElement("group") : 14 | document.createElementNS("http://www.w3.org/2000/svg", "g"); 15 | 16 | r.canvas.appendChild(group); 17 | 18 | function updateScale(transform, scaleX, scaleY) { 19 | var scaleString = 'scale(' + scaleX + ' ' + scaleY + ')'; 20 | if (!transform) { 21 | return scaleString; 22 | } 23 | if (transform.indexOf('scale(') < 0) { 24 | return transform + ' ' + scaleString; 25 | } 26 | return transform.replace(/scale\(-?[0-9]*?\.?[0-9]*?\ -?[0-9]*?\.?[0-9]*?\)/, scaleString); 27 | } 28 | 29 | function updateRotation(transform, rotation) { 30 | var rotateString = 'rotate(' + rotation + ')'; 31 | if (!transform) { 32 | return rotateString; 33 | } 34 | if (transform.indexOf('rotate(') < 0) { 35 | return transform + ' ' + rotateString; 36 | } 37 | return transform.replace(/rotate\(-?[0-9]+(\.[0-9][0-9]*)?\)/, rotateString); 38 | } 39 | 40 | function updateTranslation(transform, x, y) { 41 | var translateString = 'translate(' + x + ' ' + y + ')'; 42 | if (!transform) { 43 | return translateString; 44 | } 45 | return transform.replace(/translate\(-?[0-9]*?\.?[0-9]*?\ -?[0-9]*?\.?[0-9]*?\)/, translateString); 46 | } 47 | 48 | inst = { 49 | scale: function (newScaleX, newScaleY) { 50 | var transform = group.getAttribute('transform'); 51 | group.setAttribute('transform', updateScale(transform, newScaleX, newScaleY)); 52 | return this; 53 | }, 54 | rotate: function(deg) { 55 | var transform = group.getAttribute('transform'); 56 | group.setAttribute('transform', updateRotation(transform, deg)); 57 | }, 58 | push: function(item) { 59 | function pushOneRaphaelVector(it){ 60 | var i; 61 | if (it.type === 'set') { 62 | for (i=0; i< it.length; i++) { 63 | pushOneRaphaelVector(it[i]); 64 | } 65 | } else { 66 | group.appendChild(it.node); 67 | set.push(it); 68 | } 69 | } 70 | pushOneRaphaelVector(item) 71 | return this; 72 | }, 73 | translate: function(newTranslateX, newTranslateY) { 74 | var transform = group.getAttribute('transform'); 75 | group.setAttribute('transform', updateTranslation(transform, newTranslateX, newTranslateY)); 76 | return this; 77 | }, 78 | getBBox: function() { 79 | return set.getBBox(); 80 | }, 81 | type: 'group', 82 | node: group 83 | }; 84 | 85 | return inst; 86 | } 87 | 88 | return Group(cfg, items); 89 | 90 | }; -------------------------------------------------------------------------------- /qed/qed_styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-tap-highlight-color: rgba(0,0,0,0); 3 | -webkit-touch-callout: none; 4 | -webkit-user-select: none; 5 | -khtml-user-select: none; 6 | -moz-user-select: none; 7 | -ms-user-select: none; 8 | user-select: none; 9 | } 10 | 11 | html { width: 100%; } 12 | body { 13 | margin: 0; 14 | padding: 0; 15 | width: 100%; 16 | /* Uncomment for letterbox HD mode */ 17 | /* 18 | background-color: #000000; 19 | padding: 15px 49px; 20 | */ 21 | } 22 | 23 | html, body { height: 100%; overflow: hidden; } 24 | 25 | #experiment { 26 | border: 1px solid #555; 27 | } 28 | 29 | .messagebox { 30 | cursor: default; 31 | position: absolute; 32 | abackground-color: #FFFFFF; 33 | font-family: 'Calibri', 'Helvetica', sans-serif; 34 | padding: 0 5px; 35 | font-size: 15pt; 36 | overflow-x: hidden; 37 | overflow-y: auto; 38 | -webkit-overflow-scrolling: touch; 39 | } 40 | 41 | .messagebox img { 42 | vertical-align: middle; 43 | } 44 | 45 | .arrowmanager { } 46 | 47 | .helparrow { 48 | position: absolute; 49 | overflow: hidden; 50 | /* outline: 1px solid red; */ 51 | } 52 | 53 | .labelmanager { 54 | position: absolute; 55 | left: 0; top: 0; 56 | } 57 | 58 | .labelmanager .background { 59 | position: absolute; 60 | left: 0; top: 0; 61 | background-color: #000000; 62 | opacity: 0.65; 63 | } 64 | 65 | .labelmanager .container { 66 | position: absolute; 67 | left: 0; top: 0; 68 | } 69 | 70 | .labelmanager .container .label { 71 | -webkit-touch-callout: none; 72 | -webkit-user-select: none; 73 | -khtml-user-select: none; 74 | -moz-user-select: none; 75 | -ms-user-select: none; 76 | user-select: none; 77 | cursor: default; 78 | afont-variant: small-caps; 79 | text-transform: uppercase; 80 | position: absolute; 81 | display: inline-block; 82 | padding: 5px 10px; 83 | color: #FFF; 84 | font-family: 'Calibri', 'Helvetica', sans-serif; 85 | border-radius: 10px; 86 | width: auto; 87 | text-align: center; 88 | background-color: rgba(0,0,0,0.8); 89 | box-shadow: #000000 0px 0px 15px; 90 | } 91 | 92 | .labelmanager .container .label p { margin: 0; padding: 0; } 93 | 94 | .labelmanager .container .label img { 95 | vertical-align: middle; 96 | } 97 | 98 | .labelmanager .container .imagelabel { 99 | -webkit-touch-callout: none; 100 | -webkit-user-select: none; 101 | -khtml-user-select: none; 102 | -moz-user-select: none; 103 | -ms-user-select: none; 104 | user-select: none; 105 | cursor: default; 106 | position: absolute; 107 | display: inline-block; 108 | } 109 | 110 | #loaderlayer { 111 | position: absolute; 112 | top: 0; 113 | left: 0; 114 | background-color: rgba(255,255,255,0.5); 115 | } 116 | #loader { 117 | position: absolute; 118 | width: 300px; 119 | height: 100px; 120 | border-radius: 15px; 121 | background-color: rgba(255,255,255,0.8); 122 | box-shadow: #000000 0px 0px 20px; 123 | text-align: center; 124 | } 125 | #loader p { 126 | font-size: 15pt; 127 | font-weight: bold; 128 | font-family: sans-serif; 129 | color: #444; 130 | } 131 | #loader p img { 132 | margin-top: 15px; 133 | border: 1px solid #E0E0E0; 134 | } -------------------------------------------------------------------------------- /electrons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | photons 17 | | 18 | 24 |
25 |
26 |
27 | 28 |
29 | 30 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /probability/01_coins.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | font-size: 16px; 6 | } 7 | html, body { width: 820px; height: 100%; overflow-x: hidden; } 8 | 9 | #app { 10 | width: 780px; 11 | height: 400px; 12 | background-color: #F0F0F0; 13 | font-size: 20px; 14 | padding: 20px; 15 | } 16 | h1 { 17 | font-family: sans-serif; 18 | font-weight: bold; 19 | margin: 0; 20 | padding: 0; 21 | font-size: 1.5em; 22 | text-align: center; 23 | } 24 | input, button { 25 | vertical-align: middle; 26 | } 27 | input[type="range"] { 28 | color: #000000; 29 | width: 300px; 30 | height: 50px; 31 | } 32 | input[type="number"] { 33 | color: #000000; 34 | width: 75px; 35 | height: 20px; 36 | padding: 5px; 37 | text-align: left; 38 | font-size: 1em; 39 | } 40 | button { 41 | } 42 | #bt_fair { 43 | font-size: 1.25em; 44 | height: 35px; 45 | width: 150px; 46 | } 47 | label { 48 | font-size: 1.1em; 49 | display: inline-block; 50 | margin-right: 10px; 51 | width: 200px; 52 | } 53 | .lowerpanel { 54 | margin-top: 10px; 55 | width: 780px; 56 | position: relative; 57 | } 58 | .sidebar { 59 | position: relative; 60 | } 61 | 62 | #coin_image { 63 | border: 1px solid #CCC; 64 | position: absolute; 65 | left: 0; 66 | top: 0; 67 | width: 100px; 68 | height: 100px; 69 | } 70 | 71 | #coin_result { 72 | border: 1px solid #CCC; 73 | background-color: #FFF; 74 | position: absolute; 75 | left: 0; 76 | top: 110px; 77 | display: block; 78 | width: 100px; 79 | height: 30px; 80 | line-height: 30px; 81 | font-weight: bold; 82 | font-family: monospace; 83 | text-align: center; 84 | font-size: 1.3em; 85 | color: #000000; 86 | } 87 | 88 | #coin_flip { 89 | position: absolute; 90 | left: 0; 91 | top: 150px; 92 | display: block; 93 | width: 100px; 94 | height: 100px; 95 | font-size: 1.5em; 96 | background-color: #8E8; 97 | border: 3px solid; 98 | font-weight: bold; 99 | color: #282; 100 | border-color: #E0FFE0 #4A4 #4A4 #E0FFE0; 101 | } 102 | #coin_flip:active { 103 | border-color: #4A4 #E0FFE0 #E0FFE0 #4A4; 104 | } 105 | #coin_flip:disabled { 106 | opacity: 0.5; 107 | } 108 | 109 | .results { 110 | position: absolute; 111 | top: 0; 112 | left: 120px; 113 | width: 650px; 114 | } 115 | 116 | .result_type { 117 | display: inline-block; 118 | padding-right: 10px; 119 | } 120 | 121 | .result_value { 122 | display: inline-block; 123 | text-align: left; 124 | margin-right: 50px; 125 | } 126 | #result_total { width: 50px; } 127 | #result_heads { width: 120px; } 128 | #result_tails { width: 120px; } 129 | 130 | #results_image { 131 | position: absolute; 132 | top: 28px; 133 | border: 1px solid #CCC; 134 | width: 650px; 135 | height: 220px; 136 | } 137 | 138 | /* ------- SLIDER -------- */ 139 | input[type=range].slider { 140 | -webkit-appearance: none; 141 | margin: 0px 0; 142 | background-color: transparent; 143 | } 144 | input[type=range].slider:focus { 145 | outline: none; 146 | } 147 | input[type=range].slider:disabled { 148 | opacity: 0.5; 149 | } 150 | input[type=range].slider::-webkit-slider-runnable-track { 151 | width: 100%; 152 | height: 32px; 153 | cursor: pointer; 154 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 155 | background: #e8e8e8; 156 | border-radius: 0px; 157 | border: 0px solid #010101; 158 | } 159 | input[type=range].slider::-webkit-slider-thumb { 160 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 161 | border: 1.9px solid rgba(0, 30, 0, 0.57); 162 | height: 32px; 163 | width: 32px; 164 | border-radius: 4px; 165 | background: #cccccc; 166 | cursor: pointer; 167 | -webkit-appearance: none; 168 | margin-top: 0px; 169 | } 170 | input[type=range].slider:focus::-webkit-slider-runnable-track { 171 | background: #fcfcfc; 172 | } 173 | input[type=range].slider::-moz-range-track { 174 | width: 100%; 175 | height: 32px; 176 | cursor: pointer; 177 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 178 | background: #e8e8e8; 179 | border-radius: 0px; 180 | border: 0px solid #010101; 181 | } 182 | input[type=range].slider::-moz-range-thumb { 183 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 184 | border: 1.9px solid rgba(0, 30, 0, 0.57); 185 | height: 32px; 186 | width: 32px; 187 | border-radius: 4px; 188 | background: #cccccc; 189 | cursor: pointer; 190 | } 191 | input[type=range].slider::-ms-track { 192 | width: 100%; 193 | height: 32px; 194 | cursor: pointer; 195 | background: transparent; 196 | border-color: transparent; 197 | color: transparent; 198 | } 199 | input[type=range].slider::-ms-fill-lower { 200 | background: #d4d4d4; 201 | border: 0px solid #010101; 202 | border-radius: 0px; 203 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 204 | } 205 | input[type=range].slider::-ms-fill-upper { 206 | background: #e8e8e8; 207 | border: 0px solid #010101; 208 | border-radius: 0px; 209 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 210 | } 211 | input[type=range].slider::-ms-thumb { 212 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 213 | border: 1.9px solid rgba(0, 30, 0, 0.57); 214 | width: 32px; 215 | border-radius: 4px; 216 | background: #cccccc; 217 | cursor: pointer; 218 | height: 32px; 219 | } 220 | input[type=range].slider:focus::-ms-fill-lower { 221 | background: #e8e8e8; 222 | } 223 | input[type=range].slider:focus::-ms-fill-upper { 224 | background: #fcfcfc; 225 | } 226 | -------------------------------------------------------------------------------- /probability/03_birthday.css: -------------------------------------------------------------------------------- 1 | * { 2 | /* Prevent text highlight */ 3 | -webkit-tap-highlight-color: rgba(0,0,0,0); 4 | -webkit-touch-callout: none; 5 | -webkit-user-select: none; 6 | -khtml-user-select: none; 7 | -moz-user-select: none; 8 | -ms-user-select: none; 9 | user-select: none; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | padding: 0; 15 | font-family: sans-serif; 16 | font-size: 16px; 17 | } 18 | html, body { width: 820px; height: 100%; overflow-x: hidden; } 19 | 20 | #app { 21 | width: 780px; 22 | height: 400px; 23 | background-color: #F0F0F0; 24 | font-size: 20px; 25 | padding: 20px; 26 | } 27 | h1 { 28 | font-family: sans-serif; 29 | font-weight: bold; 30 | margin: 0; 31 | padding: 0; 32 | font-size: 1.5em; 33 | text-align: center; 34 | } 35 | 36 | .upperpanel { 37 | margin: 10px 5px; 38 | position: relative; 39 | height: 300px; 40 | } 41 | 42 | .lowerpanel { 43 | margin: 10px 5px; 44 | position: relative; 45 | } 46 | 47 | .graph { 48 | position: absolute; 49 | outline: 1px solid #CCC; 50 | position: absolute; 51 | top: 0; 52 | left: 0; 53 | } 54 | #graph { z-index: 0; } 55 | #overlay { z-index: 1; } 56 | 57 | #numpeople_slider { 58 | width: 615px; 59 | margin: 0; 60 | } 61 | 62 | #numpeople_value { 63 | margin: 0; 64 | width: 60px; 65 | height: 22px; 66 | padding: 5px; 67 | } 68 | 69 | input, button { 70 | vertical-align: middle; 71 | } 72 | input[type="range"] { 73 | color: #000000; 74 | width: 300px; 75 | height: 50px; 76 | } 77 | input[type="number"] { 78 | color: #000000; 79 | width: 75px; 80 | height: 20px; 81 | padding: 5px; 82 | text-align: left; 83 | font-size: 1em; 84 | } 85 | 86 | 87 | #bt_compute { 88 | position: absolute; 89 | left: 540px; 90 | top: 185px; 91 | display: block; 92 | width: 180px; 93 | height: 70px; 94 | font-size: 1em; 95 | text-align: center; 96 | background-color: #8E8; 97 | border: 3px solid; 98 | font-weight: bold; 99 | color: #282; 100 | border-color: #E0FFE0 #4A4 #4A4 #E0FFE0; 101 | z-index: 10; 102 | } 103 | #bt_compute:active { 104 | border-color: #4A4 #E0FFE0 #E0FFE0 #4A4; 105 | } 106 | #bt_compute:disabled { 107 | opacity: 0.5; 108 | } 109 | 110 | #bt_show { 111 | position: absolute; 112 | left: 540px; 113 | top: 185px; 114 | display: none; 115 | width: 180px; 116 | height: 70px; 117 | font-size: 1em; 118 | text-align: center; 119 | background-color: #88E; 120 | border: 3px solid; 121 | font-weight: bold; 122 | color: #228; 123 | border-color: #E0E0FF #44A #44A #E0E0FF; 124 | z-index: 9; 125 | } 126 | #bt_show:active { 127 | border-color: #44A #E0E0FF #E0E0FF #44A; 128 | } 129 | #bt_show:disabled { 130 | opacity: 0.5; 131 | } 132 | 133 | #probability { 134 | position: absolute; 135 | left: 540px; 136 | top: 150px; 137 | width: 160px; 138 | height: 30px; 139 | padding: 0 9px; 140 | line-height: 30px; 141 | border: 1px solid #CCC; 142 | background-color: #FFF; 143 | z-index: 11; 144 | } 145 | 146 | 147 | /* ------- SLIDER -------- */ 148 | input[type=range].slider { 149 | -webkit-appearance: none; 150 | margin: 0px 0; 151 | background-color: transparent; 152 | } 153 | input[type=range].slider:focus { 154 | outline: none; 155 | } 156 | input[type=range].slider:disabled { 157 | opacity: 0.5; 158 | } 159 | input[type=range].slider::-webkit-slider-runnable-track { 160 | width: 100%; 161 | height: 32px; 162 | cursor: pointer; 163 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 164 | background: #e8e8e8; 165 | border-radius: 0px; 166 | border: 0px solid #010101; 167 | } 168 | input[type=range].slider::-webkit-slider-thumb { 169 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 170 | border: 1.9px solid rgba(0, 30, 0, 0.57); 171 | height: 32px; 172 | width: 32px; 173 | border-radius: 4px; 174 | background: #cccccc; 175 | cursor: pointer; 176 | -webkit-appearance: none; 177 | margin-top: 0px; 178 | } 179 | input[type=range].slider:focus::-webkit-slider-runnable-track { 180 | background: #fcfcfc; 181 | } 182 | input[type=range].slider::-moz-range-track { 183 | width: 100%; 184 | height: 32px; 185 | cursor: pointer; 186 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 187 | background: #e8e8e8; 188 | border-radius: 0px; 189 | border: 0px solid #010101; 190 | } 191 | input[type=range].slider::-moz-range-thumb { 192 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 193 | border: 1.9px solid rgba(0, 30, 0, 0.57); 194 | height: 32px; 195 | width: 32px; 196 | border-radius: 4px; 197 | background: #cccccc; 198 | cursor: pointer; 199 | } 200 | input[type=range].slider::-ms-track { 201 | width: 100%; 202 | height: 32px; 203 | cursor: pointer; 204 | background: transparent; 205 | border-color: transparent; 206 | color: transparent; 207 | } 208 | input[type=range].slider::-ms-fill-lower { 209 | background: #d4d4d4; 210 | border: 0px solid #010101; 211 | border-radius: 0px; 212 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 213 | } 214 | input[type=range].slider::-ms-fill-upper { 215 | background: #e8e8e8; 216 | border: 0px solid #010101; 217 | border-radius: 0px; 218 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 219 | } 220 | input[type=range].slider::-ms-thumb { 221 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 222 | border: 1.9px solid rgba(0, 30, 0, 0.57); 223 | width: 32px; 224 | border-radius: 4px; 225 | background: #cccccc; 226 | cursor: pointer; 227 | height: 32px; 228 | } 229 | input[type=range].slider:focus::-ms-fill-lower { 230 | background: #e8e8e8; 231 | } 232 | input[type=range].slider:focus::-ms-fill-upper { 233 | background: #fcfcfc; 234 | } 235 | -------------------------------------------------------------------------------- /qed/free.js: -------------------------------------------------------------------------------- 1 | /* STANDARD SETUP */ 2 | var clocksBoxHeight = 150; // helps set up overall height 3 | 4 | // Main object definitions 5 | var app = Engine.create("experiment", 1000, 430 + clocksBoxHeight); 6 | $(document.body).append(app.div); 7 | 8 | var exp = new Engine.ExperimentBox("exp", 600, 400); 9 | app.add(exp, 10, 10); 10 | 11 | var graph = new Engine.GraphBox("graph", 120, 400); 12 | app.add(graph, 620, 10); 13 | 14 | var amps = new Engine.AmplitudeBox("amps", 240, 240); 15 | app.add(amps, 750, 10); 16 | 17 | var msg = new Engine.MessageBox("msg", 240, 60 + clocksBoxHeight); 18 | app.add(msg, 750, 360); 19 | 20 | var clocks = new Engine.ClockBox("clocks", 730, clocksBoxHeight); 21 | clocks.setRadius(30); 22 | app.add(clocks, 10, 420); 23 | 24 | // Button in containers, necessary to make it above the label overlay 25 | var btHelpContainer = new Engine.RaphaelPaper("btHelpContainer", 50, 40); 26 | var btHelp = new Engine.ButtonHelp(); 27 | btHelpContainer.add(btHelp, 0, 0); 28 | app.add(btHelpContainer, 940, 260); 29 | 30 | var btGoContainer = new Engine.RaphaelPaper("btGoContainer", 50, 40); 31 | var btGo = new Engine.ButtonGo(); 32 | btGoContainer.add(btGo, 0, 0); 33 | app.add(btGoContainer, 750, 260); 34 | 35 | var btNextContainer = new Engine.RaphaelPaper("btNextContainer", 120, 40); 36 | var btNext = new Engine.ButtonNext(); 37 | btNextContainer.add(btNext, 0, 0); 38 | app.add(btNextContainer, 810, 260); 39 | 40 | var btGraph = new Engine.ButtonGraph(); 41 | amps.add(btGraph, amps.width-btGraph.width-5, amps.height-btGraph.height-5); 42 | 43 | var btReset = new Engine.ButtonKill(); 44 | exp.add(btReset, 10, 350); 45 | btReset.setOpacity(0.5); 46 | 47 | // var btUndo = new Engine.ButtonUndo(); 48 | // app.add(btUndo, 940, 310); 49 | 50 | var btFast = new Engine.ButtonFast(); 51 | app.add(btFast, 750, 310); 52 | 53 | 54 | /* ------------------------------ BEGIN EXPERIMENT ------------------------------ */ 55 | 56 | // Source 57 | var photonSource = new Engine.PhotonSource("source", 0); 58 | photonSource.setTitle("Source"); 59 | exp.add(photonSource, 30, 200); 60 | photonSource.setCursor("pointer"); 61 | 62 | // Detector rail 63 | var rail = new Engine.Rail(10,360); 64 | exp.add(rail, 560, 20); 65 | 66 | // Detector 67 | var detector = new Engine.Detector("detector"); 68 | detector.snap = 10; 69 | detector.setTitle("Detector"); 70 | exp.add(detector, 560, 200); 71 | detector.setDragBounds([560, 560], [30, 370]); 72 | detector.enableDrag(); 73 | detector.setCursor("ns-resize"); 74 | 75 | var lightLayer = new Engine.LightLayer( 76 | Engine.STYLE.Colors[photonSource.color].color, 77 | Engine.STYLE.Colors[photonSource.color].frequency 78 | ); 79 | exp.addLightLayer(lightLayer); 80 | var glowManager = new Engine.GlowManager({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 81 | 82 | function startfreemode() { 83 | 84 | resetFreeMode(); 85 | 86 | msg.setMessage("This is the free mode of the engine.\n\nCreate any collection of paths you wish and see the results of the simulation."); 87 | 88 | Engine.addEvent({ source: exp, node: exp.div }, "click", function(e) { 89 | var p = Engine.getEventMousePos(e, e.data.source.div); 90 | Engine.snap(p, 10); 91 | var area = [300-20, 300+20]; 92 | if (p.x >= area[0] && p.x <= area[1]) { 93 | createLightPath(p.y); 94 | } 95 | }); 96 | 97 | btGo.onPress(function(){ 98 | buildWalls(); 99 | lightLayer.shootAllPhotons(computeResults); 100 | btGo.disable(); 101 | btFast.disable(); 102 | detector.disableDrag(); 103 | }); 104 | 105 | // Engine.addEvent({ source: detector, node: detector.container.node }, "firstdrag", evFirstDrag); 106 | Engine.addEvent({ source: detector, node: detector.container.node }, "moved", evDrag); 107 | Engine.addEvent({ source: photonSource, node: photonSource.container.node }, "press", changeColor); 108 | 109 | btReset.enable(); 110 | btReset.onPress(resetFreeMode); 111 | 112 | btFast.onToggle(function(){ 113 | btFast.state = !btFast.state; 114 | Engine.ANIMATION_SPEED = (btFast.state ? 10 : 1 ); 115 | btFast.setSign(btFast.state); 116 | }); 117 | 118 | btGraph.onPress(function(){ 119 | amps.setTotalAmplitudeColor(photonSource.color); 120 | amps.toggleTotalAmplitudeArrow(); 121 | drawProbability(detector.y); 122 | }); 123 | } 124 | 125 | var usedLocations = {}; 126 | function drawProbability(y) { 127 | if (usedLocations[y]) return; 128 | usedLocations[y] = 1; 129 | graph.drawBar(y - exp.height/2, Math.pow(amps.getNormalizedTotalAmplitude(),2), photonSource.color); 130 | } 131 | 132 | function resetFreeMode() { 133 | 134 | btGraph.disable(); 135 | btHelp.disable(); 136 | btNext.disable(); 137 | btGo.disable(); 138 | btFast.disable(); 139 | 140 | clocks.clear(); 141 | lightLayer.clear(); 142 | amps.clear(); 143 | graph.clear(); 144 | 145 | detector.enableDrag(); 146 | usedPaths = {total:0}; 147 | usedProbs = {}; 148 | 149 | glowManager.assign({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 150 | 151 | buildWalls(); 152 | } 153 | 154 | function changeColor() { 155 | if (usedPaths.total) return; 156 | photonSource.setColor((photonSource.color+1)%3); 157 | lightLayer.setGlobalColor(Engine.STYLE.Colors[photonSource.color].color); 158 | } 159 | 160 | function computeResults() { 161 | btFast.enable(); 162 | amps.drawAmplitudes(lightLayer); 163 | detector.blink(); 164 | glowManager.enable(); 165 | btGraph.enable(); 166 | detector.enableDrag(); 167 | } 168 | 169 | var usedPaths = {total:0}; 170 | function createLightPath(y) { 171 | if (lightLayer.timer) return; 172 | if (y < 20 || y > 380) return; 173 | if (usedPaths[y]) return; 174 | // clearWalls(); 175 | var path = new Engine.LightPath(1); 176 | path.addPoint({ x: photonSource.x, y: photonSource.y }); 177 | path.addPoint({ x: 300, y: y }); 178 | path.addPoint({ x: detector.x, y: detector.y }); 179 | path.updateData(); // call this manually so the Engine knows when to compute the entire path, instead of computing every new point 180 | lightLayer.addPath(path); 181 | path.clock = clocks.addClock(photonSource.color); 182 | usedPaths[y] = 1; 183 | usedPaths.total++; 184 | graph.clear(); 185 | usedLocations = {}; 186 | // amps.viewportScale = 30 * (usedPaths.total * Engine.STYLE.AmplitudeArrowLength) / (Engine.STYLE.AmplitudeBoxUsefulArea * amps.width); 187 | if (usedPaths.total) { 188 | detector.enableDrag(); 189 | btGo.enable(); 190 | btFast.enable(); 191 | } 192 | buildWalls(); 193 | } 194 | 195 | function evDrag(ev) { 196 | if (lightLayer.lightPaths.length) { 197 | lightLayer.changePointAllPaths(2, detector); 198 | } 199 | if (usedLocations[detector.y]) { 200 | btGo.disable(); 201 | btGraph.enable(); 202 | amps.drawAmplitudes(lightLayer); 203 | amps.showTotalAmplitudeArrow(); 204 | lightLayer.setFinalState(); 205 | glowManager.assign({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 206 | glowManager.enable(); 207 | } else { 208 | clocks.reset(); 209 | amps.clear(); 210 | if (lightLayer.lightPaths.length) btGo.enable(); 211 | btGraph.disable(); 212 | glowManager.disable(); 213 | } 214 | } 215 | 216 | var walls = []; 217 | function buildWalls() { 218 | clearWalls(); 219 | var start = NaN; 220 | var w; 221 | for(var y = -10; y <= 410; y += 10) { 222 | if (usedPaths[y] || y == 410) { 223 | if (!isNaN(start)) { 224 | w = new Engine.Wall(10, y-start-10, 0); 225 | exp.add(w, exp.width/2 - 5, start+5); 226 | walls.push(w) 227 | start = NaN; 228 | } 229 | } 230 | if (isNaN(start)) start = y; 231 | } 232 | } 233 | 234 | function clearWalls() { 235 | for(var i in walls) { 236 | exp.remove(walls[i]); 237 | delete walls[i]; 238 | } 239 | } 240 | 241 | startfreemode(); -------------------------------------------------------------------------------- /probability/02_penney.css: -------------------------------------------------------------------------------- 1 | * { 2 | /* Prevent text highlight */ 3 | -webkit-tap-highlight-color: rgba(0,0,0,0); 4 | -webkit-touch-callout: none; 5 | -webkit-user-select: none; 6 | -khtml-user-select: none; 7 | -moz-user-select: none; 8 | -ms-user-select: none; 9 | user-select: none; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | padding: 0; 15 | font-family: sans-serif; 16 | font-size: 16px; 17 | } 18 | html, body { width: 820px; height: 100%; overflow-x: hidden; } 19 | 20 | #app { 21 | width: 780px; 22 | height: 400px; 23 | background-color: #F0F0F0; 24 | font-size: 20px; 25 | padding: 20px; 26 | } 27 | h1 { 28 | font-family: sans-serif; 29 | font-weight: bold; 30 | margin: 0; 31 | padding: 0; 32 | font-size: 1.5em; 33 | text-align: center; 34 | } 35 | 36 | .selections { 37 | position: relative; 38 | height: 130px; 39 | margin-top: 10px; 40 | } 41 | 42 | .selections .vs { 43 | position: absolute; 44 | top: 60px; 45 | left: 350px; 46 | font-size: 1.75em; 47 | width: 80px; 48 | text-align: center; 49 | font-style: italic; 50 | } 51 | 52 | .selection { 53 | position: absolute; 54 | width: 340px; 55 | height: 130px; 56 | top: 0; 57 | background-color: #E0E0E0; 58 | border: 1px solid #C8C8C8; 59 | } 60 | 61 | .selection_player { 62 | left: 0px; 63 | } 64 | 65 | .selection_pc { 66 | left: 440px; 67 | } 68 | 69 | .player { 70 | display: block; 71 | text-align: right; 72 | font-size: 1.75em; 73 | text-align: center; 74 | width: 280px; 75 | } 76 | 77 | .selection_player .player { 78 | float: left; 79 | } 80 | .selection_pc .player { 81 | float: right; 82 | } 83 | 84 | .score { 85 | position: absolute; 86 | border: 2px inset #CCC; 87 | width: 70px; 88 | height: 50px; 89 | background-color: #000; 90 | color: #00E000; 91 | text-align: center; 92 | font-size: 2em; 93 | line-height: 50px; 94 | top: 55px; 95 | font-family: monospace; 96 | } 97 | 98 | .selection_player .score { 99 | left: 290px; 100 | } 101 | .selection_pc .score { 102 | left: -22px; 103 | } 104 | 105 | .coin { 106 | position: absolute; 107 | background-color: #FFFFFF; 108 | width: 80px; 109 | height: 80px; 110 | outline: 1px solid #CCC; 111 | top: 40px; 112 | } 113 | 114 | .selection_player .coin { 115 | cursor: pointer; 116 | } 117 | 118 | .selection_player .coin1 { 119 | left: 10px; 120 | } 121 | .selection_player .coin2 { 122 | left: 100px; 123 | } 124 | .selection_player .coin3 { 125 | left: 190px; 126 | } 127 | 128 | .selection_pc .coin1 { 129 | left: 70px; 130 | } 131 | .selection_pc .coin2 { 132 | left: 160px; 133 | } 134 | .selection_pc .coin3 { 135 | left: 250px; 136 | } 137 | 138 | .lowerpanel { 139 | position: relative; 140 | margin-top: 10px; 141 | } 142 | 143 | #game { 144 | background-color: #FFF; 145 | width: 520px; 146 | height: 210px; 147 | border: 1px solid #CCC; 148 | } 149 | 150 | .sidepanel { 151 | position: absolute; 152 | left: 540px; 153 | top: 0; 154 | width: 240px; 155 | height: 210px; 156 | } 157 | .sidepanel .stats { 158 | padding: 5px 5px; 159 | } 160 | .sidepanel .stats p { 161 | margin: 0 0 10px 0; 162 | padding: 0; 163 | } 164 | 165 | #winner { 166 | font-weight: bold; 167 | } 168 | 169 | #play { 170 | position: absolute; 171 | left: 0; 172 | top: 162px; 173 | display: block; 174 | width: 240px; 175 | height: 50px; 176 | font-size: 1.5em; 177 | background-color: #8E8; 178 | border: 3px solid; 179 | font-weight: bold; 180 | color: #282; 181 | border-color: #E0FFE0 #4A4 #4A4 #E0FFE0; 182 | } 183 | #play:active { 184 | border-color: #4A4 #E0FFE0 #E0FFE0 #4A4; 185 | } 186 | #play:disabled { 187 | opacity: 0.5; 188 | } 189 | 190 | /* 191 | input, button { 192 | vertical-align: middle; 193 | } 194 | input[type="range"] { 195 | color: #000000; 196 | width: 300px; 197 | height: 50px; 198 | } 199 | input[type="number"] { 200 | color: #000000; 201 | width: 75px; 202 | height: 20px; 203 | padding: 5px; 204 | text-align: left; 205 | font-size: 1em; 206 | } 207 | button { 208 | } 209 | #bt_fair { 210 | font-size: 1.25em; 211 | height: 35px; 212 | width: 150px; 213 | } 214 | label { 215 | font-size: 1.1em; 216 | display: inline-block; 217 | margin-right: 10px; 218 | width: 200px; 219 | } 220 | .lowerpanel { 221 | margin-top: 10px; 222 | width: 780px; 223 | position: relative; 224 | } 225 | .sidebar { 226 | position: relative; 227 | } 228 | 229 | #coin_image { 230 | border: 1px solid #CCC; 231 | position: absolute; 232 | left: 0; 233 | top: 0; 234 | width: 100px; 235 | height: 100px; 236 | } 237 | 238 | #coin_result { 239 | border: 1px solid #CCC; 240 | background-color: #FFF; 241 | position: absolute; 242 | left: 0; 243 | top: 110px; 244 | display: block; 245 | width: 100px; 246 | height: 30px; 247 | line-height: 30px; 248 | font-weight: bold; 249 | font-family: monospace; 250 | text-align: center; 251 | font-size: 1.3em; 252 | color: #000000; 253 | } 254 | 255 | #coin_flip { 256 | position: absolute; 257 | left: 0; 258 | top: 150px; 259 | display: block; 260 | width: 100px; 261 | height: 100px; 262 | font-size: 1.5em; 263 | background-color: #8E8; 264 | border: 3px solid; 265 | font-weight: bold; 266 | color: #282; 267 | border-color: #E0FFE0 #4A4 #4A4 #E0FFE0; 268 | } 269 | #coin_flip:active { 270 | border-color: #4A4 #E0FFE0 #E0FFE0 #4A4; 271 | } 272 | #coin_flip:disabled { 273 | opacity: 0.5; 274 | } 275 | 276 | .results { 277 | position: absolute; 278 | top: 0; 279 | left: 120px; 280 | width: 650px; 281 | } 282 | 283 | .result_type { 284 | display: inline-block; 285 | padding-right: 10px; 286 | } 287 | 288 | .result_value { 289 | display: inline-block; 290 | text-align: left; 291 | margin-right: 50px; 292 | } 293 | #result_total { width: 50px; } 294 | #result_heads { width: 120px; } 295 | #result_tails { width: 120px; } 296 | 297 | #results_image { 298 | position: absolute; 299 | top: 28px; 300 | border: 1px solid #CCC; 301 | width: 650px; 302 | height: 220px; 303 | } 304 | */ 305 | 306 | /* ------- SLIDER -------- */ 307 | input[type=range].slider { 308 | -webkit-appearance: none; 309 | margin: 0px 0; 310 | background-color: transparent; 311 | } 312 | input[type=range].slider:focus { 313 | outline: none; 314 | } 315 | input[type=range].slider:disabled { 316 | opacity: 0.5; 317 | } 318 | input[type=range].slider::-webkit-slider-runnable-track { 319 | width: 100%; 320 | height: 32px; 321 | cursor: pointer; 322 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 323 | background: #e8e8e8; 324 | border-radius: 0px; 325 | border: 0px solid #010101; 326 | } 327 | input[type=range].slider::-webkit-slider-thumb { 328 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 329 | border: 1.9px solid rgba(0, 30, 0, 0.57); 330 | height: 32px; 331 | width: 32px; 332 | border-radius: 4px; 333 | background: #cccccc; 334 | cursor: pointer; 335 | -webkit-appearance: none; 336 | margin-top: 0px; 337 | } 338 | input[type=range].slider:focus::-webkit-slider-runnable-track { 339 | background: #fcfcfc; 340 | } 341 | input[type=range].slider::-moz-range-track { 342 | width: 100%; 343 | height: 32px; 344 | cursor: pointer; 345 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 346 | background: #e8e8e8; 347 | border-radius: 0px; 348 | border: 0px solid #010101; 349 | } 350 | input[type=range].slider::-moz-range-thumb { 351 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 352 | border: 1.9px solid rgba(0, 30, 0, 0.57); 353 | height: 32px; 354 | width: 32px; 355 | border-radius: 4px; 356 | background: #cccccc; 357 | cursor: pointer; 358 | } 359 | input[type=range].slider::-ms-track { 360 | width: 100%; 361 | height: 32px; 362 | cursor: pointer; 363 | background: transparent; 364 | border-color: transparent; 365 | color: transparent; 366 | } 367 | input[type=range].slider::-ms-fill-lower { 368 | background: #d4d4d4; 369 | border: 0px solid #010101; 370 | border-radius: 0px; 371 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 372 | } 373 | input[type=range].slider::-ms-fill-upper { 374 | background: #e8e8e8; 375 | border: 0px solid #010101; 376 | border-radius: 0px; 377 | box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; 378 | } 379 | input[type=range].slider::-ms-thumb { 380 | box-shadow: 0.8px 0.8px 1.9px rgba(0, 0, 62, 0.67), 0px 0px 0.8px rgba(0, 0, 88, 0.67); 381 | border: 1.9px solid rgba(0, 30, 0, 0.57); 382 | width: 32px; 383 | border-radius: 4px; 384 | background: #cccccc; 385 | cursor: pointer; 386 | height: 32px; 387 | } 388 | input[type=range].slider:focus::-ms-fill-lower { 389 | background: #e8e8e8; 390 | } 391 | input[type=range].slider:focus::-ms-fill-upper { 392 | background: #fcfcfc; 393 | } 394 | -------------------------------------------------------------------------------- /probability/03_birthday.js: -------------------------------------------------------------------------------- 1 | // ################################################################################################# 2 | // Birthday Problem graph 3 | // Georgetown University 4 | // Department of Physics - Washington, DC, USA 5 | // Written by Lucas Vieira (https://github.com/1ucasvb) 6 | // 7 | // This software is licensed CC BY-SA 8 | // https://creativecommons.org/licenses/by-sa/2.0/ 9 | // 10 | // ################################################################################################# 11 | 12 | var BirthdayProblem = {}; 13 | BirthdayProblem.numSamples = 10; 14 | BirthdayProblem.pad = 40; 15 | BirthdayProblem.realProbs = [ 0, 16 | 0.002740, 0.008204, 0.016356, 0.027136, 0.040462, 0.056236, 0.074335, 0.094624, 0.116948, 17 | 0.141141, 0.167025, 0.194410, 0.223103, 0.252901, 0.283604, 0.315008, 0.346911, 0.379119, 18 | 0.411438, 0.443688, 0.475695, 0.507297, 0.538344, 0.568700, 0.598241, 0.626859, 0.654461, 19 | 0.680969, 0.706316, 0.730455, 0.753348, 0.774972, 0.795317, 0.814383, 0.832182, 0.848734, 20 | 0.864068, 0.878220, 0.891232, 0.903152, 0.914030, 0.923923, 0.932885, 0.940976, 0.948253, 21 | 0.954774, 0.960598, 0.965780, 0.970374, 0.974432, 0.978005, 0.981138, 0.983877, 0.986262, 22 | 0.988332, 0.990122, 0.991665, 0.992989, 0.994123, 0.995089, 0.995910, 0.996604, 0.997190, 23 | 0.997683, 0.998096, 0.998440, 0.998726, 0.998964, 0.999160, 0.999321, 0.999453, 0.999561, 24 | 0.999649, 0.999720, 0.999777, 0.999824, 0.999861, 0.999891, 0.999914, 0.999933, 0.999948, 25 | 0.999960, 0.999969, 0.999976, 0.999982, 0.999986, 0.999989, 0.999992, 0.999994, 0.999995, 26 | 0.999997, 0.999997, 0.999998, 0.999999, 0.999999, 0.999999, 0.999999 27 | ]; 28 | BirthdayProblem.init = function() { 29 | this.graph = document.getElementById("graph"); 30 | this.graphContext = this.graph.getContext("2d"); 31 | this.points = document.getElementById("points"); 32 | this.pointsContext = this.points.getContext("2d"); 33 | this.overlay = document.getElementById("overlay"); 34 | this.overlayContext = this.overlay.getContext("2d"); 35 | 36 | this.graphContext.translate(0.5, 0.5); 37 | this.pointsContext.translate(0.5, 0.5); 38 | this.overlayContext.translate(0.5, 0.5); 39 | 40 | // Text measurement 41 | this.__span = document.createElement("span"); 42 | this.__span.style.position = "absolute"; 43 | this.__span.style.top = "-9999px"; 44 | this.__span.style.left = "-9999px"; 45 | this.__span.style.visibility = "hidden"; 46 | document.body.append(this.__span); 47 | 48 | this.btCompute = document.getElementById("bt_compute"); 49 | this.btCompute.addEventListener("click", function(){ BirthdayProblem.computeProbability(); }); 50 | 51 | this.btShow = document.getElementById("bt_show"); 52 | this.btShow.addEventListener("click", function(){ BirthdayProblem.showProbability(); }); 53 | this.btShow.style.display = "none"; 54 | 55 | this.labelPeople = document.getElementById("numpeople_label"); 56 | this.probability = document.getElementById("probability"); 57 | 58 | this.sliderPeople = document.getElementById("numpeople_slider"); 59 | this.valuePeople = document.getElementById("numpeople_value"); 60 | this.sliderPeople.addEventListener("change", function(){ BirthdayProblem.updatevaluePeople(); }); 61 | this.sliderPeople.addEventListener("input", function(){ BirthdayProblem.updatevaluePeople(); }); 62 | this.valuePeople.addEventListener("change", function(){ BirthdayProblem.updatesliderPeople(); }); 63 | this.valuePeople.addEventListener("input", function(){ BirthdayProblem.updatesliderPeople(); }); 64 | 65 | this.w = this.graph.width; 66 | this.h = this.graph.height; 67 | 68 | this.probabilities = {}; 69 | 70 | Object.defineProperty(this, 'numPeople', { 71 | get: function() { 72 | return this.valuePeople.value; 73 | } 74 | }); 75 | 76 | this.reset(); 77 | this.updateOverlay(); 78 | } 79 | BirthdayProblem.getProbability = function(n) { 80 | if (n == 1) return 0; 81 | n -= 1; 82 | if (n < this.realProbs.length) { 83 | return this.realProbs[n]; 84 | } else { 85 | return 1; 86 | } 87 | } 88 | BirthdayProblem.updatevaluePeople = function() { 89 | this.valuePeople.value = this.sliderPeople.value; 90 | this.labelPeople.innerHTML = (this.valuePeople.value == 1 ? "person" : "people"); 91 | this.updateOverlay(); 92 | } 93 | BirthdayProblem.updatesliderPeople = function() { 94 | this.sliderPeople.value = this.valuePeople.value; 95 | this.labelPeople.innerHTML = (this.valuePeople.value == 1 ? "person" : "people"); 96 | this.updateOverlay(); 97 | } 98 | BirthdayProblem.updateOverlay = function() { 99 | var x, y, px, py; 100 | 101 | x = this.pad + (this.w - 2*this.pad) * (this.numPeople/366); 102 | this.overlayContext.clearRect(0, 0, this.w, this.h); 103 | this.overlayContext.fillStyle = "rgba(255,0,0,0.25)"; 104 | this.overlayContext.fillRect(x - 3, this.pad, 6, this.h - 2*this.pad); 105 | 106 | if (this.numPeople in this.probabilities) { 107 | var t = this.getProbability(this.numPeople); 108 | if (this.numPeople == 1 || this.numPeople == 366) { 109 | t = "p = "+(Math.round(t * 1e6)/1e4) + "%"; 110 | } else { 111 | t = "p \u2248 "+(Math.round(t * 1e6)/1e4) + "%"; 112 | } 113 | this.probability.innerHTML = t; 114 | this.btCompute.disabled = true; 115 | } else { 116 | this.probability.innerHTML = ""; 117 | this.btCompute.disabled = false; 118 | } 119 | 120 | } 121 | BirthdayProblem.showProbability = function() { 122 | var ctx = this.pointsContext; 123 | 124 | //ctx.clearRect(0, 0, this.w, this.h); 125 | 126 | ctx.beginPath(); 127 | ctx.lineWidth = 2; 128 | ctx.strokeStyle = "rgba(255,0,0,1)"; 129 | ctx.moveTo(this.pad, this.h - this.pad); 130 | 131 | var px, py, x, y; 132 | 133 | for(var i = 1; i <= 366; i++) { 134 | px = i / 366; 135 | py = this.getProbability(i); 136 | this.probabilities[i] = py; 137 | x = this.pad + (this.w - 2*this.pad) * px; 138 | y = this.pad + (this.h - 2*this.pad) * (1 - py); 139 | ctx.lineTo(x, y); 140 | } 141 | 142 | ctx.stroke(); 143 | ctx.closePath(); 144 | 145 | this.btShow.style.display = "none"; 146 | } 147 | BirthdayProblem.computeProbability = function() { 148 | if (this.numPeople in this.probabilities) return; 149 | 150 | this.probabilities[this.numPeople] = this.getProbability(this.numPeople); 151 | 152 | var px = this.numPeople / 366; 153 | var py = this.getProbability(this.numPeople); 154 | var x = this.pad + (this.w - 2*this.pad) * px; 155 | var y = this.pad + (this.h - 2*this.pad) * (1 - py); 156 | 157 | this.pointsContext.beginPath(); 158 | this.pointsContext.fillStyle = "rgba(255,0,0,1)"; 159 | this.pointsContext.arc(x, y, 4, 0, 2*Math.PI); 160 | this.pointsContext.fill(); 161 | 162 | if (Object.keys(this.probabilities).length == this.numSamples) { 163 | setTimeout(function(){ 164 | BirthdayProblem.btShow.style.display = "block"; 165 | BirthdayProblem.btCompute.style.display = "none"; 166 | }, 500); 167 | } 168 | 169 | this.updateOverlay(); 170 | } 171 | BirthdayProblem.reset = function() { 172 | var gw = this.w; 173 | var gh = this.h; 174 | // Clear 175 | this.overlayContext.clearRect(0, 0, gw, gh); 176 | this.pointsContext.clearRect(0, 0, gw, gh); 177 | 178 | var ctx = this.graphContext; 179 | ctx.fillStyle = "rgba(255,255,255,1)"; 180 | ctx.fillRect(0, 0, gw, gh); 181 | 182 | ctx.strokeWidth = 5; 183 | ctx.strokeStyle = "rgba(0,0,0,0.5)"; 184 | ctx.beginPath(); 185 | this.graphContext.rect( 186 | this.pad, this.pad, 187 | this.graph.width - 2*this.pad, 188 | this.graph.height - 2*this.pad 189 | ); 190 | ctx.closePath(); 191 | ctx.stroke(); 192 | 193 | var w = this.graph.width - 2*this.pad; 194 | var h = this.graph.height - 2*this.pad; 195 | 196 | 197 | // x ticks 198 | var x, y = this.pad + h; 199 | for(var i = 0; i <= 400; i += 50) { 200 | if (i == 350) continue; 201 | if (i > 366) i = 366; 202 | x = this.pad + w * (i/366); 203 | 204 | ctx.beginPath(); 205 | ctx.strokeStyle = "rgba(0,0,0,0.1)"; 206 | ctx.moveTo(x, this.pad); 207 | ctx.lineTo(x, y); 208 | ctx.stroke(); 209 | ctx.closePath(); 210 | 211 | ctx.beginPath(); 212 | ctx.strokeStyle = "rgba(0,0,0,0.25)"; 213 | ctx.moveTo(x, y - 5); 214 | ctx.lineTo(x, y + 5); 215 | ctx.stroke(); 216 | ctx.closePath(); 217 | 218 | this.drawText( 219 | ctx, 220 | i, 221 | x, 222 | y + this.pad/2, 223 | "rgba(0,0,0,0.75)", 224 | "15px sans-serif", 225 | 0 226 | ); 227 | } 228 | 229 | // y ticks 230 | x = this.pad; 231 | for(var i = 0; i <= 100; i += 10) { 232 | y = this.pad + h * (i/100); 233 | 234 | ctx.beginPath(); 235 | ctx.strokeStyle = "rgba(0,0,0,"+(i == 50?0.25:0.1)+")"; 236 | ctx.moveTo(x, y); 237 | ctx.lineTo(x + w, y); 238 | ctx.stroke(); 239 | ctx.closePath(); 240 | 241 | ctx.beginPath(); 242 | ctx.strokeStyle = "rgba(0,0,0,0.25)"; 243 | ctx.moveTo(x - 2, y); 244 | ctx.lineTo(x + 2, y); 245 | ctx.stroke(); 246 | ctx.closePath(); 247 | 248 | this.drawText( 249 | ctx, 250 | Math.round((1-i/100)*10)/10, 251 | x - this.pad/2 - 2, 252 | y, 253 | "rgba(0,0,0,0.75)", 254 | "15px sans-serif", 255 | 0 256 | ); 257 | } 258 | 259 | this.drawText( 260 | ctx, 261 | "Probability of at least two people sharing a birthday", 262 | this.pad + w / 2, 263 | this.pad / 2, 264 | "rgba(0,0,0,1)", 265 | "20px sans-serif", 266 | 0 267 | ); 268 | } 269 | BirthdayProblem.drawText = function(ctx, text, x, y, color, fontStyle, align) { 270 | ctx.font = fontStyle; 271 | this.__span.style.font = fontStyle; 272 | this.__span.innerHTML = text; 273 | var tw = ctx.measureText(text).width; 274 | var th = this.__span.offsetHeight; 275 | if (typeof(align) == "undefined") align = 0; 276 | if (align == 0) ox = - tw / 2; 277 | if (align == -1) ox = 0; 278 | ctx.save(); // Backup context 279 | ctx.translate(x, y); // Translate to desired position 280 | ctx.fillStyle = color; 281 | ctx.textBaseline = "top"; // Baseline to top makes text height predictable 282 | ctx.beginPath(); 283 | ctx.fillText(text, ox, -th / 2 - 1); // offsets in rotated context 284 | ctx.closePath(); 285 | ctx.restore(); // restore context 286 | } 287 | BirthdayProblem.init(); -------------------------------------------------------------------------------- /probability/01_coins.js: -------------------------------------------------------------------------------- 1 | // ################################################################################################# 2 | // Coin flipping simulation 3 | // Georgetown University 4 | // Department of Physics - Washington, DC, USA 5 | // Written by Lucas Vieira (https://github.com/1ucasvb) 6 | // 7 | // This software is licensed CC BY-SA 8 | // https://creativecommons.org/licenses/by-sa/2.0/ 9 | // 10 | // ################################################################################################# 11 | 12 | var CoinFlipper = {}; 13 | CoinFlipper.init = function() { 14 | if (typeof(TweenMax) == "undefined") { 15 | setTimeout(function(){ CoinFlipper.init(); }, 100); 16 | return; 17 | } 18 | 19 | this.coin = document.getElementById("coin_image"); 20 | this.coinContext = this.coin.getContext("2d"); 21 | this.results = document.getElementById("results_image"); 22 | this.resultsContext = this.results.getContext("2d"); 23 | this.animating = false; 24 | 25 | // Coin objects 26 | this.btFlip = document.getElementById("coin_flip"); 27 | this.coinResult = document.getElementById("coin_result"); 28 | 29 | // Load coin atlas 30 | this.coinImage = new Image(); 31 | this.coinSize = 100; 32 | this.coinImage.addEventListener("load", function(){ CoinFlipper.setCoin(0); }); 33 | this.coinImage.src = "coin_atlas.png"; 34 | 35 | // Pre-reduction 36 | this.coinImageSmall = document.createElement("canvas"); 37 | this.coinImageSmallContext = this.coinImageSmall.getContext("2d"); 38 | this.coinImageSmall.width = this.coinSize / 2; 39 | this.coinImageSmall.height = this.coinSize / 2; 40 | 41 | // Clear canvases 42 | this.clearCoin(); 43 | this.clearResults(); 44 | 45 | // Create properties 46 | this.__coinRotation = 0; // even = heads, odd = tails 47 | Object.defineProperty(this, 'coinRotation', { 48 | get: function() { return this.__coinRotation; }, 49 | set: function(value) { 50 | this.__coinRotation = value; 51 | this.setCoin(Math.abs(value)); 52 | } 53 | }); 54 | Object.defineProperty(this, 'coinState', { 55 | get: function() { 56 | return Math.floor(Math.abs(this.__coinRotation)) % 2; 57 | } 58 | }); 59 | this.__coinScale = 1; 60 | Object.defineProperty(this, 'coinScale', { 61 | get: function() { return this.__coinScale; }, 62 | set: function(value) { 63 | this.__coinScale = value; 64 | this.coinRotation = this.coinRotation; 65 | } 66 | }); 67 | 68 | // Sliders and inputs 69 | this.sliderProb = document.getElementById("prob_slider"); 70 | this.valueProb = document.getElementById("prob_value"); 71 | this.sliderCoins = document.getElementById("numcoins_slider"); 72 | this.valueCoins = document.getElementById("numcoins_value"); 73 | this.valueTotal = document.getElementById("result_total"); 74 | this.valueTails = document.getElementById("result_tails"); 75 | this.valueHeads = document.getElementById("result_heads"); 76 | this.btFair = document.getElementById("bt_fair"); 77 | 78 | this.sliderProb.addEventListener("change", function(){ CoinFlipper.updatevalueProb(); }); 79 | this.sliderProb.addEventListener("input", function(){ CoinFlipper.updatevalueProb(); }); 80 | this.valueProb.addEventListener("change", function(){ CoinFlipper.updatesliderProb(); }); 81 | this.valueProb.addEventListener("input", function(){ CoinFlipper.updatesliderProb(); }); 82 | 83 | this.sliderCoins.addEventListener("change", function(){ CoinFlipper.updatevalueCoins(); }); 84 | this.sliderCoins.addEventListener("input", function(){ CoinFlipper.updatevalueCoins(); }); 85 | this.valueCoins.addEventListener("change", function(){ CoinFlipper.updatesliderCoins(); }); 86 | this.valueCoins.addEventListener("input", function(){ CoinFlipper.updatesliderCoins(); }); 87 | 88 | // Properties 89 | Object.defineProperty(this, 'probability', { 90 | get: function() { return this.valueProb.value; } 91 | }); 92 | Object.defineProperty(this, 'numCoins', { 93 | get: function() { return this.valueCoins.value; } 94 | }); 95 | 96 | this.btFair.addEventListener("click", function(){ CoinFlipper.makeFair(); }); 97 | this.btFlip.addEventListener("click", function(){ CoinFlipper.flipAllCoins(); }); 98 | } 99 | CoinFlipper.makeFair = function() { 100 | this.valueProb.value = 0.5; 101 | this.sliderProb.value = 0.5; 102 | } 103 | CoinFlipper.updatevalueProb = function() { this.valueProb.value = this.sliderProb.value; } 104 | CoinFlipper.updatesliderProb = function() { this.sliderProb.value = this.valueProb.value; } 105 | CoinFlipper.updatevalueCoins = function() { this.valueCoins.value = this.sliderCoins.value; } 106 | CoinFlipper.updatesliderCoins = function() { this.sliderCoins.value = this.valueCoins.value; } 107 | CoinFlipper.clearCoin = function() { 108 | var w = this.coin.width; 109 | var h = this.coin.height; 110 | this.coinContext.fillStyle = "rgba(255,255,255,1)"; 111 | this.coinContext.fillRect(-1, -1, w+2, h+2); 112 | } 113 | CoinFlipper.setCoin = function(value) { 114 | CoinFlipper.clearCoin(); 115 | var n = Math.floor(value*32) % 64; // frame number 116 | var s = this.__coinScale, w = this.coin.width, h = this.coin.height; 117 | s = 2/3 + 1/3*4*(s - Math.pow(s, 2)); 118 | this.coinContext.drawImage( 119 | this.coinImage, 120 | (n % 8) * this.coinSize, Math.floor(n / 8) * this.coinSize, this.coinSize, this.coinSize, 121 | w*(1-s)/2, h*(1-s)/2, w*s, h*s 122 | ); 123 | } 124 | CoinFlipper.addCoinResult = function(state) { 125 | state = ( state ? 32 : 0 ); 126 | var w = this.coin.width, h = this.coin.height; 127 | var aw = this.results.width, ah = this.results.height; 128 | var t = this.totalCoins; 129 | var n = this.numCoins; 130 | 131 | var s, px, py, nx, ny, ix, iy, img; 132 | 133 | // Find size of coin that will fit 134 | if (0 < n && n <= 5) { nx = n; ny = 1; s = 1; } 135 | if (5 < n && n <= 10) { nx = 5; ny = 2; s = 1; } 136 | if (10 < n && n <= 15) { nx = 5; ny = 3; s = ah/(3*h); } 137 | if (15 < n && n <= 20) { nx = 10; ny = 2; s = aw/(10*h); } 138 | if (20 < n && n <= 30) { nx = 10; ny = 3; s = aw/(10*h); } 139 | if (30 < n && n <= 40) { nx = 10; ny = 4; s = ah/(4*h); } 140 | if (40 < n && n <= 50) { nx = 10; ny = 5; s = ah/(5*h); } 141 | if (50 < n && n <= 60) { nx = 15; ny = 4; s = aw/(15*h); } 142 | if (60 < n && n <= 75) { nx = 15; ny = 5; s = aw/(15*h); } 143 | if (75 < n && n <= 90) { nx = 15; ny = 6; s = ah/(6*h); } 144 | if (90 < n && n <= 100) { nx = 20; ny = 5; s = aw/(20*h); } 145 | 146 | ix = (t - 1) % nx; 147 | iy = Math.floor((t - 1) / nx); 148 | px = aw/2 - nx*w*s/2 + w*s/2 + w*s*ix; 149 | py = ah/2 - ny*h*s/2 + h*s/2 + h*s*iy; 150 | 151 | if (s <= 0.5) { 152 | this.coinImageSmallContext.clearRect(0, 0, this.coinImageSmall.width, this.coinImageSmall.height); 153 | this.coinImageSmallContext.drawImage( 154 | this.coinImage, 155 | (state % 8) * this.coinSize, Math.floor(state / 8) * this.coinSize, this.coinSize, this.coinSize, 156 | 0, 0, this.coinImageSmall.width, this.coinImageSmall.height 157 | ); 158 | this.resultsContext.drawImage( 159 | this.coinImageSmall, 160 | 0, 0, this.coinSize/2, this.coinSize/2, 161 | px - w*s/2, py - h*s/2, w*s, h*s 162 | ); 163 | } else { 164 | this.resultsContext.drawImage( 165 | this.coinImage, 166 | (state % 8) * this.coinSize, Math.floor(state / 8) * this.coinSize, this.coinSize, this.coinSize, 167 | px - w*s/2, py - h*s/2, w*s, h*s 168 | ); 169 | } 170 | } 171 | CoinFlipper.updateResult = function() { 172 | this.coinResult.innerHTML = ( this.coinState ? "TAILS" : "HEADS" ); 173 | this.coinResult.style.color = ( this.coinState ? "#5481FC" : "#E3673B" ); 174 | this.totalCoins++; 175 | if (this.coinState) { 176 | this.totalTails++; 177 | } else { 178 | this.totalHeads++; 179 | } 180 | if (this.totalCoins < this.numCoins) { 181 | clearTimeout(this.timer); 182 | var w = Math.pow(10, (-this.totalCoins * 0.1)); 183 | this.timer = setTimeout( 184 | function(){ 185 | CoinFlipper.flipAgain(); 186 | }, 187 | parseInt(50 + 500 * w) 188 | ); 189 | } else { 190 | this.enable(); 191 | } 192 | 193 | this.addCoinResult(this.coinState); 194 | 195 | this.valueTotal.innerHTML = this.totalCoins; 196 | this.valueHeads.innerHTML = this.totalHeads + " ("+Math.round(100 * (this.totalHeads / this.totalCoins))+"%)"; 197 | this.valueTails.innerHTML = this.totalTails + " ("+Math.round(100 * (1 - this.totalHeads / this.totalCoins))+"%)"; 198 | } 199 | CoinFlipper.flipAgain = function() { 200 | var w = Math.pow(10 , (-(this.totalCoins * 0.1))); 201 | this.flipCoin( w ); 202 | } 203 | CoinFlipper.enable = function() { 204 | this.btFlip.disabled = false; 205 | this.sliderProb.disabled = false; 206 | this.valueProb.disabled = false; 207 | this.sliderCoins.disabled = false; 208 | this.valueCoins.disabled = false; 209 | this.btFair.disabled = false; 210 | } 211 | CoinFlipper.disable = function() { 212 | this.btFlip.disabled = true; 213 | this.sliderProb.disabled = true; 214 | this.valueProb.disabled = true; 215 | this.sliderCoins.disabled = true; 216 | this.valueCoins.disabled = true; 217 | this.btFair.disabled = true; 218 | } 219 | CoinFlipper.flipCoinDone = function() { 220 | this.animating = false; 221 | this.updateResult(); 222 | } 223 | CoinFlipper.flipCoin = function(time) { 224 | if (this.animating) return; 225 | var result = ( Math.random() < this.probability ? 0 : 1 ); 226 | this.coinRotation = this.coinState; 227 | this.coinScale = 0; 228 | this.coinResult.innerHTML = ""; 229 | CoinFlipper.disable(); 230 | if (time > 0.01) { 231 | this.animating = true; 232 | TweenMax.to(CoinFlipper, time, { 233 | coinRotation: Math.max(10,Math.ceil(10*time)) + result, 234 | ease: Circ.easeOut 235 | }); 236 | TweenMax.to(CoinFlipper, time, { 237 | coinScale: 1, 238 | ease: Power0.easeNone, 239 | onComplete: function() { 240 | CoinFlipper.flipCoinDone(); 241 | } 242 | }); 243 | } else { 244 | this.coinRotation = result; 245 | this.flipCoinDone(); 246 | } 247 | return result; 248 | } 249 | CoinFlipper.clearResults = function() { 250 | var w = this.results.width; 251 | var h = this.results.height; 252 | this.resultsContext.fillStyle = "rgba(255,255,255,1)"; 253 | this.resultsContext.fillRect(-1, -1, w+2, h+2); 254 | } 255 | CoinFlipper.flipAllCoins = function() { 256 | this.valueTotal.innerHTML = "0"; 257 | this.valueHeads.innerHTML = "0 (0%)"; 258 | this.valueTails.innerHTML = "0 (0%)"; 259 | this.totalCoins = 0; 260 | this.totalHeads = 0; 261 | this.totalTails = 0; 262 | this.clearResults(); 263 | this.flipCoin(1); 264 | } 265 | CoinFlipper.init(); -------------------------------------------------------------------------------- /photons/lib/mirror.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Slayvin / http://slayvin.net 3 | */ 4 | 5 | THREE.ShaderLib[ 'mirror' ] = { 6 | 7 | uniforms: { "mirrorColor": { type: "c", value: new THREE.Color( 0x7F7F7F ) }, 8 | "mirrorSampler": { type: "t", value: null }, 9 | "textureMatrix" : { type: "m4", value: new THREE.Matrix4() } 10 | }, 11 | 12 | vertexShader: [ 13 | 14 | "uniform mat4 textureMatrix;", 15 | 16 | "varying vec4 mirrorCoord;", 17 | 18 | "void main() {", 19 | 20 | "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", 21 | "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", 22 | "mirrorCoord = textureMatrix * worldPosition;", 23 | 24 | "gl_Position = projectionMatrix * mvPosition;", 25 | 26 | "}" 27 | 28 | ].join( "\n" ), 29 | 30 | fragmentShader: [ 31 | 32 | "uniform vec3 mirrorColor;", 33 | "uniform sampler2D mirrorSampler;", 34 | 35 | "varying vec4 mirrorCoord;", 36 | 37 | "float blendOverlay(float base, float blend) {", 38 | "return( base < 0.5 ? ( 2.0 * base * blend ) : (1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );", 39 | "}", 40 | 41 | "void main() {", 42 | 43 | "vec4 color = texture2DProj(mirrorSampler, mirrorCoord);", 44 | "color = vec4(blendOverlay(mirrorColor.r, color.r), blendOverlay(mirrorColor.g, color.g), blendOverlay(mirrorColor.b, color.b), 1.0);", 45 | 46 | "gl_FragColor = color;", 47 | 48 | "}" 49 | 50 | ].join( "\n" ) 51 | 52 | }; 53 | 54 | THREE.Mirror = function ( renderer, camera, options ) { 55 | 56 | THREE.Object3D.call( this ); 57 | 58 | this.name = 'mirror_' + this.id; 59 | 60 | options = options || {}; 61 | 62 | this.matrixNeedsUpdate = true; 63 | 64 | var width = options.textureWidth !== undefined ? options.textureWidth : 512; 65 | var height = options.textureHeight !== undefined ? options.textureHeight : 512; 66 | 67 | this.clipBias = options.clipBias !== undefined ? options.clipBias : 0.0; 68 | 69 | var mirrorColor = options.color !== undefined ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F ); 70 | 71 | this.renderer = renderer; 72 | this.mirrorPlane = new THREE.Plane(); 73 | this.normal = new THREE.Vector3( 0, 0, 1 ); 74 | this.mirrorWorldPosition = new THREE.Vector3(); 75 | this.cameraWorldPosition = new THREE.Vector3(); 76 | this.rotationMatrix = new THREE.Matrix4(); 77 | this.lookAtPosition = new THREE.Vector3( 0, 0, - 1 ); 78 | this.clipPlane = new THREE.Vector4(); 79 | 80 | // For debug only, show the normal and plane of the mirror 81 | var debugMode = options.debugMode !== undefined ? options.debugMode : false; 82 | 83 | if ( debugMode ) { 84 | 85 | var arrow = new THREE.ArrowHelper( new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, 0 ), 10, 0xffff80 ); 86 | var planeGeometry = new THREE.Geometry(); 87 | planeGeometry.vertices.push( new THREE.Vector3( - 10, - 10, 0 ) ); 88 | planeGeometry.vertices.push( new THREE.Vector3( 10, - 10, 0 ) ); 89 | planeGeometry.vertices.push( new THREE.Vector3( 10, 10, 0 ) ); 90 | planeGeometry.vertices.push( new THREE.Vector3( - 10, 10, 0 ) ); 91 | planeGeometry.vertices.push( planeGeometry.vertices[ 0 ] ); 92 | var plane = new THREE.Line( planeGeometry, new THREE.LineBasicMaterial( { color: 0xffff80 } ) ); 93 | 94 | this.add( arrow ); 95 | this.add( plane ); 96 | 97 | } 98 | 99 | if ( camera instanceof THREE.PerspectiveCamera ) { 100 | 101 | this.camera = camera; 102 | 103 | } else { 104 | 105 | this.camera = new THREE.PerspectiveCamera(); 106 | console.log( this.name + ': camera is not a Perspective Camera!' ); 107 | 108 | } 109 | 110 | this.textureMatrix = new THREE.Matrix4(); 111 | 112 | this.mirrorCamera = this.camera.clone(); 113 | this.mirrorCamera.matrixAutoUpdate = true; 114 | 115 | var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false }; 116 | 117 | this.texture = new THREE.WebGLRenderTarget( width, height, parameters ); 118 | this.tempTexture = new THREE.WebGLRenderTarget( width, height, parameters ); 119 | 120 | var mirrorShader = THREE.ShaderLib[ "mirror" ]; 121 | var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms ); 122 | 123 | this.material = new THREE.ShaderMaterial( { 124 | 125 | fragmentShader: mirrorShader.fragmentShader, 126 | vertexShader: mirrorShader.vertexShader, 127 | uniforms: mirrorUniforms 128 | 129 | } ); 130 | 131 | this.material.uniforms.mirrorSampler.value = this.texture; 132 | this.material.uniforms.mirrorColor.value = mirrorColor; 133 | this.material.uniforms.textureMatrix.value = this.textureMatrix; 134 | 135 | if ( ! THREE.Math.isPowerOfTwo( width ) || ! THREE.Math.isPowerOfTwo( height ) ) { 136 | 137 | this.texture.generateMipmaps = false; 138 | this.tempTexture.generateMipmaps = false; 139 | 140 | } 141 | 142 | this.updateTextureMatrix(); 143 | this.render(); 144 | 145 | }; 146 | 147 | THREE.Mirror.prototype = Object.create( THREE.Object3D.prototype ); 148 | THREE.Mirror.prototype.constructor = THREE.Mirror; 149 | 150 | THREE.Mirror.prototype.renderWithMirror = function ( otherMirror ) { 151 | 152 | // update the mirror matrix to mirror the current view 153 | this.updateTextureMatrix(); 154 | this.matrixNeedsUpdate = false; 155 | 156 | // set the camera of the other mirror so the mirrored view is the reference view 157 | var tempCamera = otherMirror.camera; 158 | otherMirror.camera = this.mirrorCamera; 159 | 160 | // render the other mirror in temp texture 161 | otherMirror.renderTemp(); 162 | otherMirror.material.uniforms.mirrorSampler.value = otherMirror.tempTexture; 163 | 164 | // render the current mirror 165 | this.render(); 166 | this.matrixNeedsUpdate = true; 167 | 168 | // restore material and camera of other mirror 169 | otherMirror.material.uniforms.mirrorSampler.value = otherMirror.texture; 170 | otherMirror.camera = tempCamera; 171 | 172 | // restore texture matrix of other mirror 173 | otherMirror.updateTextureMatrix(); 174 | 175 | }; 176 | 177 | THREE.Mirror.prototype.updateTextureMatrix = function () { 178 | 179 | this.updateMatrixWorld(); 180 | this.camera.updateMatrixWorld(); 181 | 182 | this.mirrorWorldPosition.setFromMatrixPosition( this.matrixWorld ); 183 | this.cameraWorldPosition.setFromMatrixPosition( this.camera.matrixWorld ); 184 | 185 | this.rotationMatrix.extractRotation( this.matrixWorld ); 186 | 187 | this.normal.set( 0, 0, 1 ); 188 | this.normal.applyMatrix4( this.rotationMatrix ); 189 | 190 | var view = this.mirrorWorldPosition.clone().sub( this.cameraWorldPosition ); 191 | view.reflect( this.normal ).negate(); 192 | view.add( this.mirrorWorldPosition ); 193 | 194 | this.rotationMatrix.extractRotation( this.camera.matrixWorld ); 195 | 196 | this.lookAtPosition.set( 0, 0, - 1 ); 197 | this.lookAtPosition.applyMatrix4( this.rotationMatrix ); 198 | this.lookAtPosition.add( this.cameraWorldPosition ); 199 | 200 | var target = this.mirrorWorldPosition.clone().sub( this.lookAtPosition ); 201 | target.reflect( this.normal ).negate(); 202 | target.add( this.mirrorWorldPosition ); 203 | 204 | this.up.set( 0, - 1, 0 ); 205 | this.up.applyMatrix4( this.rotationMatrix ); 206 | this.up.reflect( this.normal ).negate(); 207 | 208 | this.mirrorCamera.position.copy( view ); 209 | this.mirrorCamera.up = this.up; 210 | this.mirrorCamera.lookAt( target ); 211 | 212 | this.mirrorCamera.updateProjectionMatrix(); 213 | this.mirrorCamera.updateMatrixWorld(); 214 | this.mirrorCamera.matrixWorldInverse.getInverse( this.mirrorCamera.matrixWorld ); 215 | 216 | // Update the texture matrix 217 | this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5, 218 | 0.0, 0.5, 0.0, 0.5, 219 | 0.0, 0.0, 0.5, 0.5, 220 | 0.0, 0.0, 0.0, 1.0 ); 221 | this.textureMatrix.multiply( this.mirrorCamera.projectionMatrix ); 222 | this.textureMatrix.multiply( this.mirrorCamera.matrixWorldInverse ); 223 | 224 | // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html 225 | // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf 226 | this.mirrorPlane.setFromNormalAndCoplanarPoint( this.normal, this.mirrorWorldPosition ); 227 | this.mirrorPlane.applyMatrix4( this.mirrorCamera.matrixWorldInverse ); 228 | 229 | this.clipPlane.set( this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant ); 230 | 231 | var q = new THREE.Vector4(); 232 | var projectionMatrix = this.mirrorCamera.projectionMatrix; 233 | 234 | q.x = ( Math.sign( this.clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ]; 235 | q.y = ( Math.sign( this.clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ]; 236 | q.z = - 1.0; 237 | q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ]; 238 | 239 | // Calculate the scaled plane vector 240 | var c = new THREE.Vector4(); 241 | c = this.clipPlane.multiplyScalar( 2.0 / this.clipPlane.dot( q ) ); 242 | 243 | // Replacing the third row of the projection matrix 244 | projectionMatrix.elements[ 2 ] = c.x; 245 | projectionMatrix.elements[ 6 ] = c.y; 246 | projectionMatrix.elements[ 10 ] = c.z + 1.0 - this.clipBias; 247 | projectionMatrix.elements[ 14 ] = c.w; 248 | 249 | }; 250 | 251 | THREE.Mirror.prototype.render = function () { 252 | 253 | if ( this.matrixNeedsUpdate ) this.updateTextureMatrix(); 254 | 255 | this.matrixNeedsUpdate = true; 256 | 257 | // Render the mirrored view of the current scene into the target texture 258 | var scene = this; 259 | 260 | while ( scene.parent ) { 261 | 262 | scene = scene.parent; 263 | 264 | } 265 | 266 | if ( scene !== undefined && scene instanceof THREE.Scene ) { 267 | 268 | // We can't render ourself to ourself 269 | var visible = this.material.visible; 270 | this.material.visible = false; 271 | 272 | this.renderer.render( scene, this.mirrorCamera, this.texture, true ); 273 | 274 | this.material.visible = visible; 275 | 276 | } 277 | 278 | }; 279 | 280 | THREE.Mirror.prototype.renderTemp = function () { 281 | 282 | if ( this.matrixNeedsUpdate ) this.updateTextureMatrix(); 283 | 284 | this.matrixNeedsUpdate = true; 285 | 286 | // Render the mirrored view of the current scene into the target texture 287 | var scene = this; 288 | 289 | while ( scene.parent !== null ) { 290 | 291 | scene = scene.parent; 292 | 293 | } 294 | 295 | if ( scene !== undefined && scene instanceof THREE.Scene ) { 296 | 297 | this.renderer.render( scene, this.mirrorCamera, this.tempTexture, true ); 298 | 299 | } 300 | 301 | }; 302 | -------------------------------------------------------------------------------- /qed/free2.js: -------------------------------------------------------------------------------- 1 | /* STANDARD SETUP */ 2 | var clocksBoxHeight = 150; // helps set up overall height 3 | 4 | // Main object definitions 5 | var app = Engine.create("experiment", 1000, 430 + clocksBoxHeight); 6 | $(document.body).append(app.div); 7 | 8 | var exp = new Engine.ExperimentBox("exp", 600, 400); 9 | app.add(exp, 10, 10); 10 | 11 | var graph = new Engine.GraphBox("graph", 120, 400); 12 | app.add(graph, 620, 10); 13 | graph.drawScale(0); 14 | 15 | var amps = new Engine.AmplitudeBox("amps", 240, 240); 16 | app.add(amps, 750, 10); 17 | 18 | var msg = new Engine.MessageBox("msg", 240, 60 + clocksBoxHeight); 19 | app.add(msg, 750, 360); 20 | 21 | var clocks = new Engine.ClockBox("clocks", 730, clocksBoxHeight); 22 | clocks.setRadius(30); 23 | app.add(clocks, 10, 420); 24 | 25 | // Button in containers, necessary to make it above the label overlay 26 | var btHelpContainer = new Engine.RaphaelPaper("btHelpContainer", 50, 40); 27 | var btHelp = new Engine.ButtonHelp(); 28 | btHelpContainer.add(btHelp, 0, 0); 29 | app.add(btHelpContainer, 940, 260); 30 | 31 | //Added for the help feature, it is a clone of the help button that removes the help layer 32 | var btHelpContainer2 = new Engine.RaphaelPaper("btHelpContainer2", 50, 40); 33 | var btHelp2 = new Engine.ButtonHelp(); 34 | btHelpContainer2.add(btHelp2, 0, 0); 35 | app.add(btHelpContainer2, 940, 260); 36 | app.hideLabelLayer([btHelpContainer]); 37 | 38 | var btGoContainer = new Engine.RaphaelPaper("btGoContainer", 50, 40); 39 | var btGo = new Engine.ButtonGo(); 40 | btGoContainer.add(btGo, 0, 0); 41 | app.add(btGoContainer, 750, 260); 42 | 43 | var btResetContainer = new Engine.RaphaelPaper("btResetContainer", 50, 40); 44 | var btReset = new Engine.ButtonKill(); 45 | btResetContainer.add(btReset, 0, 0); 46 | app.add(btResetContainer, 20, 360); 47 | btReset.setOpacity(0.5); 48 | 49 | /* ------------------------------ BEGIN EXPERIMENT ------------------------------ */ 50 | 51 | // Source 52 | var photonSource = new Engine.PhotonSource("source", 0); 53 | photonSource.setTitle("Source"); 54 | exp.add(photonSource, 30, 200); 55 | photonSource.setCursor("pointer"); 56 | 57 | // Detector rail 58 | var rail = new Engine.Rail(10,360); 59 | exp.add(rail, 560, 20); 60 | 61 | // Detector 62 | var detector = new Engine.Detector("detector"); 63 | detector.snap = 10; 64 | detector.setTitle("Detector"); 65 | exp.add(detector, 560, 200); 66 | detector.setDragBounds([560, 560], [30, 370]); 67 | detector.enableDrag(); 68 | detector.setCursor("ns-resize"); 69 | 70 | var lightLayer = new Engine.LightLayer( 71 | Engine.STYLE.Colors[photonSource.color].color, 72 | Engine.STYLE.Colors[photonSource.color].frequency 73 | ); 74 | exp.addLightLayer(lightLayer); 75 | var glowManager = new Engine.GlowManager({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 76 | 77 | function startFreeMode() { 78 | 79 | resetFreeMode(); 80 | 81 | msg.setMessage( 82 | "This is the free mode of the multislit experiment with light.\n\n"+ 83 | "Create any collection of paths you wish by clicking on the wall on the midline and press the green button to see "+ 84 | "the results of the simulation.\n\n"+ 85 | "In this experiment, each path's arrow has a length of 0.05, implying a single hole creates a uniform pattern with probability 0.25% to enter a detector at the screen.\n\n"+ 86 | "Move the detector to see how the arrows add at different locations on the screen.\n\n"+ 87 | "Change the color of the light by clicking on the source.\n\n"+ 88 | "Reset with the red X button." 89 | ); 90 | 91 | Engine.addEvent({ source: exp, node: exp.div }, "click", function(e) { 92 | var p = Engine.getEventMousePos(e, e.data.source.div); 93 | Engine.snap(p, 10); 94 | var area = [300-20, 300+20]; 95 | if (p.x >= area[0] && p.x <= area[1]) { 96 | createLightPath(p.y); 97 | } 98 | }); 99 | 100 | Engine.addEvent({ source: detector, node: detector.container.node }, "moved", evDrag); 101 | Engine.addEvent({ source: photonSource, node: photonSource.container.node }, "press", changeColor); 102 | 103 | btReset.enable(); 104 | btReset.onPress(resetFreeMode); 105 | 106 | btGo.onPress(showResults); 107 | 108 | btHelp.onPress(setLabels); 109 | btHelp2.onPress(function(){ 110 | app.hideLabelLayer([btHelpContainer]) 111 | }); 112 | 113 | } 114 | 115 | function showResults() { 116 | Engine.ANIMATION_SPEED = 1; 117 | buildWalls(); 118 | lightLayer.shootAllPhotons(computeResults); 119 | btGo.disable(); 120 | detector.disableDrag(); 121 | btReset.disable(); 122 | amps.clear(); 123 | } 124 | 125 | var usedLocations = {}; 126 | 127 | function drawProbability(y) { 128 | if (usedLocations[y]) return; 129 | usedLocations[y] = 1; 130 | graph.drawBar(y - exp.height/2, Math.pow(amps.getNormalizedTotalAmplitude(),2), photonSource.color); 131 | } 132 | 133 | function resetFreeMode() { 134 | 135 | btGo.disable(); 136 | 137 | clocks.clear(); 138 | lightLayer.clear(); 139 | amps.clear(); 140 | graph.clear(); 141 | 142 | detector.enableDrag(); 143 | usedPaths = {total:0}; 144 | usedProbs = {}; 145 | 146 | photonsFired = 0; 147 | 148 | buildWalls(); 149 | 150 | glowManager.active = 0; 151 | graph.drawScale(0); 152 | 153 | amps.clearProbabilityText(); 154 | } 155 | 156 | var usedPaths = {total:0}; 157 | function createLightPath(y) { 158 | if (lightLayer.timer) return; 159 | if (y < 20 || y > 380) return; 160 | if (usedPaths[y]) return; 161 | // clearWalls(); 162 | var path = new Engine.LightPath(0.05); 163 | path.addPoint({ x: photonSource.x, y: photonSource.y }); 164 | path.addPoint({ x: 300, y: y }); 165 | path.addPoint({ x: detector.x, y: detector.y }); 166 | path.updateData(); // call this manually so the Engine knows when to compute the entire path, instead of computing every new point 167 | lightLayer.addPath(path); 168 | path.clock = clocks.addClock(photonSource.color); 169 | usedPaths[y] = 1; 170 | usedPaths.total++; 171 | graph.clear(); 172 | usedLocations = {}; 173 | // amps.viewportScale = 30 * (usedPaths.total * Engine.STYLE.AmplitudeArrowLength) / (Engine.STYLE.AmplitudeBoxUsefulArea * amps.width); 174 | if (usedPaths.total) { 175 | detector.enableDrag(); 176 | btGo.enable(); 177 | } 178 | buildWalls(); 179 | } 180 | 181 | function changeColor() { 182 | //if (usedPaths.total) return;// 183 | if(lightLayer.timer) return; 184 | photonSource.setColor((photonSource.color+1)%3); 185 | lightLayer.setGlobalColor(Engine.STYLE.Colors[photonSource.color].color); 186 | lightLayer.setFrequency(Engine.STYLE.Colors[photonSource.color].frequency); 187 | lightLayer.drawAll(); 188 | for(var i in lightLayer.lightPaths) { 189 | lightLayer.graphics[lightLayer.lightPaths[i].uid].photon.attr('fill',Engine.STYLE.Colors[photonSource.color].color); 190 | }; 191 | for(var i in clocks.clockList) { 192 | clocks.clockList[i].setColor(photonSource.color); 193 | }; 194 | 195 | if (photonsFired) { 196 | amps.clearProbabilityText(); 197 | amps.drawAmplitudes(lightLayer); 198 | amps.showTotalAmplitudeArrow(); 199 | lightLayer.setFinalState(); 200 | plotProbability(); 201 | glowManager.assign({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 202 | glowManager.enable(); 203 | amps.drawProbabilityText(); 204 | }; 205 | } 206 | 207 | var normConstant = 0; 208 | function plotProbability() { 209 | graph.clear(); 210 | var y; 211 | var probabilityScale; 212 | var probabilities = []; 213 | 214 | for(var i = 3; i < exp.height/10-2; i++) { 215 | y = i*10; 216 | lightLayer.changePointAllPaths(2, { x: detector.x, y: y }, true); 217 | probabilities.push(Math.pow(lightLayer.getTotalAmplitude().amplitude,2)); 218 | 219 | } 220 | 221 | probabilityScale = Math.max.apply(null, probabilities); 222 | 223 | probabilityScale = Math.round(probabilityScale * 100) / 100; 224 | graph.probabilityScale = probabilityScale + 0.01; 225 | graph.drawScale(100 * probabilityScale + 1); 226 | 227 | amps.drawProbabilityText(); 228 | 229 | for(var i = 0; i < probabilities.length; i++ ) { 230 | y = (i+3) * 10; 231 | lightLayer.changePointAllPaths(2, { x: detector.x, y: y }, true); 232 | graph.drawBar(y - exp.height/2, probabilities[i], photonSource.color); 233 | } 234 | lightLayer.changePointAllPaths(2, {x: detector.x, y: detector.y }, true); 235 | } 236 | 237 | var photonsFired = 0; 238 | function computeResults() { 239 | amps.drawAmplitudes(lightLayer); 240 | detector.blink(); 241 | glowManager.enable(); 242 | detector.enableDrag(); 243 | btReset.enable(); 244 | amps.setTotalAmplitudeColor(photonSource.color); 245 | amps.toggleTotalAmplitudeArrow(); 246 | plotProbability(); 247 | photonsFired = 1; 248 | } 249 | 250 | function evDrag(ev) { 251 | if (lightLayer.lightPaths.length) { 252 | lightLayer.changePointAllPaths(2, detector); 253 | } 254 | if (photonsFired) { 255 | amps.clearProbabilityText(); 256 | amps.drawAmplitudes(lightLayer); 257 | amps.showTotalAmplitudeArrow(); 258 | lightLayer.setFinalState(); 259 | glowManager.assign({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 260 | glowManager.enable(); 261 | amps.drawProbabilityText(); 262 | }; 263 | } 264 | 265 | var walls = []; 266 | function buildWalls() { 267 | clearWalls(); 268 | var start = NaN; 269 | var w; 270 | for(var y = -10; y <= 410; y += 10) { 271 | if (usedPaths[y] || y == 410) { 272 | if (!isNaN(start)) { 273 | w = new Engine.Wall(10, y-start-10, 0); 274 | exp.add(w, exp.width/2 - 5, start+5); 275 | walls.push(w) 276 | start = NaN; 277 | } 278 | } 279 | if (isNaN(start)) start = y; 280 | } 281 | } 282 | 283 | function clearWalls() { 284 | for(var i in walls) { 285 | exp.remove(walls[i]); 286 | delete walls[i]; 287 | } 288 | } 289 | 290 | function setLabels() { 291 | app.labelManager.clearLabels(); 292 | 293 | app.labelManager.addLabel({ at: photonSource, align: [-1,0] }, 294 | "Photon\nsource\nPress to\nchange color"); 295 | 296 | app.labelManager.addLabel({ at: {x: photonSource.x, y: photonSource.y+180}, align: [-1,0] }, 297 | "Press to\nreset"); 298 | 299 | app.labelManager.addLabel({ at: detector, align: [1,0] }, 300 | "Photon\ndetector.\nDrag to\nchange\nposition"); 301 | 302 | app.labelManager.addLabel({ at: {x: exp.x + exp.width/2, y: exp.y + exp.height*0.7}, align: [0,0] }, 303 | "Press along the line\nbetween the\nbarriers to\ncreate paths"); 304 | 305 | app.labelManager.addLabel({ at: {x: clocks.x + clocks.width/2, y: clocks.y + clocks.height/2 }, align: [0,0] }, 306 | "Clock for each path\nMouse over or long tap to highlight an\nindividual path and its respective clock and arrow"); 307 | 308 | app.labelManager.addLabel({ at: {x: btGoContainer.x + 75, y: btGoContainer.y+75 }, align: [1,1] }, 309 | "Show\nresults"); 310 | 311 | app.labelManager.addLabel({ at: {x: graph.x+graph.width/2, y: graph.y }, align: [0,-1] }, 312 | "Probability\nof photon\ndetection\n(horizontal)\nvs.\nposition of\nthe detector\n(vertical)"); 313 | 314 | app.labelManager.addLabel({ at: {x: amps.x+amps.width/2, y: amps.y+20 }, align: [0,-1] }, 315 | "Computation\nwindow with\nprobability\namplitude\narrows"); 316 | 317 | app.labelManager.addLabel({ at: {x: msg.x+msg.width/2, y: msg.y+msg.height/2 }, align: [0,0] }, 318 | "Instruction box"); 319 | 320 | app.showLabelLayer([btHelpContainer2]); 321 | } 322 | 323 | startFreeMode(); 324 | -------------------------------------------------------------------------------- /qed/exp_30_normalization_lens.js: -------------------------------------------------------------------------------- 1 | // ################################################################################################# 2 | // QED Engine tutorials 3 | // Copyright (C) 2015-2016 Georgetown University 4 | // Department of Physics - Washington, DC, USA 5 | // Written by Lucas Vieira (https://github.com/1ucasvb) 6 | // Dylan Cutler (https://github.com/DCtheTall) 7 | // 8 | // -------------------------------------- LICENSE -------------------------------------------------- 9 | // 10 | // This library is free software; you can redistribute it and/or 11 | // modify it under the terms of the GNU Lesser General Public 12 | // License as published by the Free Software Foundation; either 13 | // version 2.1 of the License, or (at your option) any later version. 14 | // 15 | // This library is distributed in the hope that it will be useful, 16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | // Lesser General Public License for more details. 19 | // 20 | // You should have received a copy of the GNU Lesser General Public 21 | // License along with this library; if not, write to the Free Software 22 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | // 24 | // ################################################################################################# 25 | 26 | 27 | /* STfANDARD SETUP */ 28 | var clocksBoxHeight = 150; // helps set up overall height 29 | 30 | // Main object definitions 31 | var app = Engine.create("experiment", 1000, 430 + clocksBoxHeight); 32 | $(document.body).append(app.div); 33 | 34 | var exp = new Engine.ExperimentBox("exp", 600, 400); 35 | app.add(exp, 10, 10); 36 | 37 | var graph = new Engine.GraphBox("graph", 120, 400, 40); 38 | app.add(graph, 620, 10); 39 | 40 | var amps = new Engine.AmplitudeBox("amps", 240, 240); 41 | app.add(amps, 750, 10); 42 | 43 | var msg = new Engine.MessageBox("msg", 240, 60 + clocksBoxHeight); 44 | app.add(msg, 750, 360); 45 | 46 | var clocks = new Engine.ClockBox("clocks", 730, clocksBoxHeight); 47 | clocks.setRadius(30); 48 | app.add(clocks, 10, 420); 49 | 50 | // Button in containers, necessary to make it above the label overlay 51 | var btHelpContainer = new Engine.RaphaelPaper("btHelpContainer", 50, 40); 52 | var btHelp = new Engine.ButtonHelp(); 53 | btHelpContainer.add(btHelp, 0, 0); 54 | app.add(btHelpContainer, 940, 260); 55 | 56 | var btGoContainer = new Engine.RaphaelPaper("btGoContainer", 50, 40); 57 | var btGo = new Engine.ButtonGo(); 58 | btGoContainer.add(btGo, 0, 0); 59 | app.add(btGoContainer, 750, 260); 60 | 61 | var btNextContainer = new Engine.RaphaelPaper("btNextContainer", 120, 40); 62 | var btNext = new Engine.ButtonNext(); 63 | btNextContainer.add(btNext, 0, 0); 64 | app.add(btNextContainer, 810, 260); 65 | 66 | var btGraph = new Engine.ButtonGraph(); 67 | amps.add(btGraph, amps.width-btGraph.width-5, amps.height-btGraph.height-5); 68 | 69 | /* ------------------------------ BEGIN EXPERIMENT ------------------------------ */ 70 | 71 | var TEXT = { 72 | 'welcome': 73 | "

This interactive tutorial on\nthe concept of normalization\n\n"+ 74 | "To continue, press

" 75 | , 76 | 'lens': 77 | "This tutorial is on the concept of lenses.\n\n"+ 78 | "Press to see how a lens works.\n\n"+ 79 | "Press to plot the resulting probability distribution.\n\n"+ 80 | "Move the detector to see how the arrows cancel nearly everywhere else. Recall the glass retards the arrows by just the right amount for all clocks to align. This is how the lens focuses." 81 | } 82 | 83 | // Source 84 | var photonSource = new Engine.PhotonSource("source", 0); 85 | photonSource.setTitle("Source"); 86 | exp.add(photonSource, 30, 200); 87 | 88 | // Detector rail 89 | var rail = new Engine.Rail(10,360); 90 | exp.add(rail, 560, 20); 91 | 92 | // Detector 93 | var detector = new Engine.Detector("detector"); 94 | detector.snap = 10; 95 | detector.setTitle("Detector"); 96 | exp.add(detector, 560, 200); 97 | detector.setDragBounds([560, 560], [30, 370]); 98 | 99 | function buildWalls() { 100 | var a = (SLIT_SIZE+1)*10; 101 | var h = exp.height/2 - a/2; 102 | var w = 10; 103 | exp.add(new Engine.Wall(w,h,5), exp.width/2-w/2, 0); 104 | exp.add(new Engine.Wall(w,h,5), exp.width/2-w/2, exp.height - h); 105 | } 106 | 107 | /* Labels */ 108 | 109 | function setLabels1() { 110 | app.labelManager.clearLabels(); 111 | app.labelManager.addLabel({ at: {x: app.width/2-20, y: app.height/2}, align: [0,0] }, TEXT['welcome'] ); 112 | } 113 | 114 | function setDefaultLabels() { 115 | app.labelManager.clearLabels(); 116 | app.arrowManager.clearArrows(); 117 | 118 | app.labelManager.addLabel({ at: {x: 10, y: 10}, align: [-1,-1] }, 119 | "

press again to exit the help window.

"); 120 | 121 | // app.labelManager.addLabel({ at: photonSource, align: [-1,0] }, 122 | // "Photon\nsource\nPress to\nchange color"); 123 | 124 | app.labelManager.addLabel({ at: detector, align: [1,0] }, 125 | "Photon\ndetector\nDrag to\nchange\nposition"); 126 | 127 | app.labelManager.addLabel({ at: {x: clocks.x + clocks.width/2, y: clocks.y + clocks.height/2 }, align: [0,0] }, 128 | "Clock for each path\nMouse over or long tap to highlight an\nindividual path and its respective clock and arrow"); 129 | 130 | app.labelManager.addLabel({ at: {x: btGoContainer.x + 15, y: btGoContainer.y+25 }, align: [1,1] }, 131 | "Emit\nphoton"); 132 | 133 | app.labelManager.addLabel({ at: {x: btNextContainer.x + 60, y: btNextContainer.y+25 }, align: [0,1] }, 134 | "Go to\nnext step"); 135 | 136 | app.labelManager.addLabel({ at: {x: graph.x+graph.width/2, y: graph.y }, align: [0,-1] }, 137 | "Probability\nof photon\ndetection\n(horizontal)\nvs.\nposition of\nthe detector\n(vertical)"); 138 | 139 | app.labelManager.addLabel({ at: {x: amps.x+amps.width/2, y: amps.y+20 }, align: [0,-1] }, 140 | "Computation\nwindow with\nprobability\namplitude\narrows"); 141 | 142 | app.labelManager.addLabel({ at: {x: msg.x+msg.width/2, y: msg.y+msg.height/2 }, align: [0,0] }, 143 | "Instruction box"); 144 | } 145 | 146 | /* Steps */ 147 | 148 | /* ------ INTRO ---------- */ 149 | 150 | function intro() { 151 | app.labelManager.addLabel({ at: {x: app.width/2-20, y: app.height/2}, align: [0,0] }, 152 | "

Welcome to the tutorial on converging lenses.\n\n"+ 153 | "We will explore how\na lens focuses light\n\n"+ 154 | "To begin, press

"); 155 | 156 | app.showLabelLayer([btNextContainer]); 157 | btNextContainer.blinkOn(true); 158 | btNext.onPress(step1); 159 | btNext.enable(); 160 | 161 | btGraph.disable(); 162 | 163 | app.arrowManager.addArrow({ 164 | x: btNextContainer.x - 20, 165 | y: btNextContainer.y - 20 166 | },135); 167 | app.arrowManager.show(); 168 | app.arrowManager.onTop(); 169 | } 170 | 171 | /* ------- STEP 1 -------- */ 172 | function step1() { 173 | btNextContainer.blinkOff(); 174 | 175 | setLabels1(); 176 | app.showLabelLayer([btHelpContainer]); 177 | 178 | // app.arrowManager.addArrow({ x: btHelpContainer.x - 15, y: btHelpContainer - 15 }, 135 ); 179 | // app.arrowManager.show(); 180 | 181 | // Go to step 2 182 | // btHelp.onPress(step2); 183 | // btHelpContainer.blinkOn(true); 184 | 185 | for (var i = -SLIT_SIZE/2;i <= SLIT_SIZE/2;i+=1) { 186 | createLightPath(exp.height/2 + 10*i); 187 | } 188 | 189 | detector.setPosition(560, exp.height/2); 190 | lightLayer.changePointAllPaths(3, { x: detector.x, y: detector.y }, true); 191 | 192 | glowManager.enable(); 193 | // btGo.disable(); 194 | btGraph.onPress(function(){ 195 | amps.toggleTotalAmplitudeArrow(); 196 | plotProbability(); 197 | }); 198 | 199 | step2(); 200 | } 201 | // This function creates a path with a point on the slit 202 | // It also creates the associated clock and the associations 203 | function createLightPath(y) { 204 | //x = h - y² / w² 205 | var ior = IOR; 206 | var path = new Engine.LightPath(1); 207 | path.addPoint({ x: photonSource.x, y: photonSource.y }); 208 | path.addPoint({ 209 | x: exp.width/2 - LENS_WIDTH/2 + Math.pow((y - exp.height/2)/(SLIT_SIZE+2),2) 210 | , 211 | y: y, 212 | ior: ior 213 | }); 214 | path.addPoint({ 215 | x: exp.width/2 + LENS_WIDTH/2 - Math.pow((y - exp.height/2)/(SLIT_SIZE+2),2) 216 | , 217 | y: y, 218 | ior: 1 219 | }); 220 | path.addPoint({ x: detector.x, y: detector.y }); 221 | path.updateData(); // call this manually so the Engine knows when to compute the entire path, instead of computing every new point 222 | lightLayer.addPath(path); 223 | path.clock = clocks.addClock(photonSource.color); 224 | } 225 | 226 | function plotProbability() { 227 | graph.clear(); 228 | var a, y; 229 | for(var i = 3; i < exp.height/10-2; i++) { 230 | y = i*10; 231 | lightLayer.changePointAllPaths(3, { x: detector.x, y: y }, true); 232 | graph.drawBar(y - exp.height/2, Math.pow(lightLayer.getTotalAmplitude().amplitude / lightLayer.numPaths(),2), photonSource.color); 233 | } 234 | lightLayer.changePointAllPaths(3, detector); 235 | } 236 | 237 | /* ------- STEP 2 -------- */ 238 | function step2() { 239 | setDefaultLabels(); 240 | btHelpContainer.blinkOff(); 241 | // btNextContainer.blinkOn(true); 242 | btNext.disable(); 243 | app.hideLabelLayer(); 244 | btHelp.onPress(function(){ 245 | app.toggleLabelLayer([btHelpContainer]); 246 | }); 247 | msg.setMessage( TEXT['lens'] ); 248 | 249 | // exp.blinkOn(true); 250 | detector.onTop(); 251 | detector.enableDrag(); 252 | detector.setCursor("ns-resize"); 253 | Engine.addEvent({ source: detector, node: detector.container.node }, "moved", evDrag); 254 | btGo.enable(); 255 | btGo.onPress(function(){ 256 | detector.disableDrag(); 257 | graph.clear(); 258 | amps.clear(); 259 | lightLayer.shootAllPhotons(function(){ 260 | detector.blink(); 261 | detector.enableDrag(); 262 | amps.drawAmplitudes(lightLayer); 263 | btGraph.enable(); 264 | // amps.showTotalAmplitudeArrow(); 265 | }); 266 | }); 267 | } 268 | function evDrag(ev) { 269 | lightLayer.changePointAllPaths(3, detector); 270 | amps.drawAmplitudes(lightLayer); 271 | amps.showTotalAmplitudeArrow(); 272 | lightLayer.setFinalState(); 273 | } 274 | 275 | 276 | function changeColor(e) { 277 | photonSource.setColor(photonSource.color+1); 278 | lightLayer.setColor(Engine.STYLE.Colors[photonSource.color].color); 279 | lightLayer.setFrequency(Engine.STYLE.Colors[photonSource.color].frequency); 280 | lightLayer.drawAll(); 281 | plotProbability(); 282 | clocks.setAllColor(photonSource.color); 283 | amps.drawAmplitudes(lightLayer); 284 | amps.showTotalAmplitudeArrow(); 285 | // amps.setTotalAmplitudeColor(photonSource.color); 286 | lightLayer.setFinalState(); 287 | } 288 | 289 | /* ----------------------------------------- Start -------------------------------------------- */ 290 | amps.viewportScale = 1; 291 | SLIT_SIZE = 15; 292 | IOR = 1.55; 293 | LENS_WIDTH = 60; 294 | LENS_HEIGHT = (SLIT_SIZE+4)*10 295 | 296 | // buildWalls(); 297 | 298 | Engine.C = 150; 299 | Engine.ANIMATION_SPEED = 0.85; 300 | Engine.FREQUENCY_ADJUST = 2; 301 | 302 | var lens = new Engine.Lens(LENS_WIDTH, LENS_HEIGHT); 303 | exp.add(lens, exp.width/2, exp.height/2); 304 | 305 | photonSource.setColor(0); 306 | var lightLayer = new Engine.LightLayer( 307 | Engine.STYLE.Colors[photonSource.color].color, 308 | Engine.STYLE.Colors[photonSource.color].frequency 309 | ); 310 | exp.addLightLayer(lightLayer); 311 | var glowManager = new Engine.GlowManager({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 312 | 313 | intro(); 314 | -------------------------------------------------------------------------------- /qed/exp_11_single_slit_color.js: -------------------------------------------------------------------------------- 1 | // ################################################################################################# 2 | // QED Engine tutorials 3 | // Copyright (C) 2015-2016 Georgetown University 4 | // Department of Physics - Washington, DC, USA 5 | // Written by Lucas Vieira (https://github.com/1ucasvb) 6 | // Dylan Cutler (https://github.com/DCtheTall) 7 | // 8 | // -------------------------------------- LICENSE -------------------------------------------------- 9 | // 10 | // This library is free software; you can redistribute it and/or 11 | // modify it under the terms of the GNU Lesser General Public 12 | // License as published by the Free Software Foundation; either 13 | // version 2.1 of the License, or (at your option) any later version. 14 | // 15 | // This library is distributed in the hope that it will be useful, 16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | // Lesser General Public License for more details. 19 | // 20 | // You should have received a copy of the GNU Lesser General Public 21 | // License along with this library; if not, write to the Free Software 22 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | // 24 | // ################################################################################################# 25 | 26 | 27 | /* STANDARD SETUP */ 28 | var clocksBoxHeight = 150; // helps set up overall height 29 | 30 | // Main object definitions 31 | var app = Engine.create("experiment", 1000, 430 + clocksBoxHeight); 32 | $(document.body).append(app.div); 33 | 34 | var exp = new Engine.ExperimentBox("exp", 600, 400); 35 | app.add(exp, 10, 10); 36 | 37 | var graph = new Engine.GraphBox("graph", 120, 400, 2); 38 | app.add(graph, 620, 10); 39 | 40 | var amps = new Engine.AmplitudeBox("amps", 240, 240); 41 | app.add(amps, 750, 10); 42 | 43 | var msg = new Engine.MessageBox("msg", 240, 60 + clocksBoxHeight); 44 | app.add(msg, 750, 360); 45 | 46 | var clocks = new Engine.ClockBox("clocks", 730, clocksBoxHeight); 47 | clocks.setRadius(30); 48 | app.add(clocks, 10, 420); 49 | 50 | // Button in containers, necessary to make it above the label overlay 51 | var btHelpContainer = new Engine.RaphaelPaper("btHelpContainer", 50, 40); 52 | var btHelp = new Engine.ButtonHelp(); 53 | btHelpContainer.add(btHelp, 0, 0); 54 | app.add(btHelpContainer, 940, 260); 55 | 56 | var btGoContainer = new Engine.RaphaelPaper("btGoContainer", 50, 40); 57 | var btGo = new Engine.ButtonGo(); 58 | btGoContainer.add(btGo, 0, 0); 59 | app.add(btGoContainer, 750, 260); 60 | 61 | var btNextContainer = new Engine.RaphaelPaper("btNextContainer", 120, 40); 62 | var btNext = new Engine.ButtonNext(); 63 | btNextContainer.add(btNext, 0, 0); 64 | app.add(btNextContainer, 810, 260); 65 | 66 | var btGraph = new Engine.ButtonGraph(); 67 | amps.add(btGraph, amps.width-btGraph.width-5, amps.height-btGraph.height-5); 68 | 69 | /* ------------------------------ BEGIN EXPERIMENT ------------------------------ */ 70 | 71 | var TEXT = { 72 | 'welcome': 73 | "

This interactive tutorial is on changing\nthe color of light that\ntravels through a single slit!\n\n"+ 74 | "This time, we explore in a little more\ndetail what happens when light passes\nthrough a large slit, "+ 75 | "and how changing\nthe color of light affects the results\nof the experiment.\n\n"+ 76 | "To continue, press

" 77 | , 78 | 'move_detector': 79 | "We start from a slit with seven alternative ways the photon can go through it.\n\n"+ 80 | "We have filled the entire probability graph for all positions of the detector.\n\n"+ 81 | "Move the detector and study how the probability amplitudes change with the position of the detector.\n\n"+ 82 | "Can you describe in words what happens at the center position and at the ends?\n\n"+ 83 | "When you are done, press " 84 | , 85 | 'change_color': 86 | "Now, click on the photon source to change its color.\n\n"+ 87 | "How does the color of the photon affect the shape of the probability distribution?\n\n"+ 88 | "Knowing that the wavelengths λ (Greek letter lambda) of light of each color follow the relation:\n 
λRED > λGREEN > λBLUE
\n"+ 89 | "Explain in words how the width of the central peak of the probability curve is related to the wavelength of the light.\n\n"+ 90 | //“Recalling the results of the previous tutorial, could you find the same pattern by changing the width of the slit instead of the color of the light?\n\n"+ 91 | "That's it for this tutorial! Please, continue on to the next part of the course." 92 | } 93 | 94 | // Source 95 | var photonSource = new Engine.PhotonSource("source", 0); 96 | photonSource.setTitle("Source"); 97 | exp.add(photonSource, 30, 200); 98 | 99 | // Detector rail 100 | var rail = new Engine.Rail(10,360); 101 | exp.add(rail, 560, 20); 102 | 103 | // Detector 104 | var detector = new Engine.Detector("detector"); 105 | detector.snap = 10; 106 | detector.setTitle("Detector"); 107 | exp.add(detector, 560, 200); 108 | detector.setDragBounds([560, 560], [30, 370]); 109 | 110 | function buildWalls() { 111 | var a = (SLIT_SIZE+1)*10; 112 | var h = exp.height/2 - a/2; 113 | var w = 10; 114 | exp.add(new Engine.Wall(w,h,5), exp.width/2-w/2, 0); 115 | exp.add(new Engine.Wall(w,h,5), exp.width/2-w/2, exp.height - h); 116 | } 117 | 118 | /* Labels */ 119 | 120 | function setLabels1() { 121 | app.labelManager.clearLabels(); 122 | app.labelManager.addLabel({ at: {x: app.width/2-20, y: app.height/2}, align: [0,0] }, TEXT['welcome'] ); 123 | } 124 | 125 | function setDefaultLabels() { 126 | app.labelManager.clearLabels(); 127 | app.arrowManager.clearArrows(); 128 | 129 | app.labelManager.addLabel({ at: {x: 10, y: 10}, align: [-1,-1] }, 130 | "

press again to exit the help window.

"); 131 | 132 | app.labelManager.addLabel({ at: photonSource, align: [-1,0] }, 133 | "Photon\nsource\nPress to\nchange color"); 134 | 135 | app.labelManager.addLabel({ at: detector, align: [1,0] }, 136 | "Photon\ndetector\nDrag to\nchange\nposition"); 137 | 138 | app.labelManager.addLabel({ at: {x: clocks.x + clocks.width/2, y: clocks.y + clocks.height/2 }, align: [0,0] }, 139 | "Clock for each path\nMouse over or long tap to highlight an\nindividual path and its respective clock and arrow"); 140 | 141 | app.labelManager.addLabel({ at: {x: btGoContainer.x + 15, y: btGoContainer.y+25 }, align: [1,1] }, 142 | "Emit\nphoton"); 143 | 144 | app.labelManager.addLabel({ at: {x: btNextContainer.x + 60, y: btNextContainer.y+25 }, align: [0,1] }, 145 | "Go to\nnext step"); 146 | 147 | app.labelManager.addLabel({ at: {x: graph.x+graph.width/2, y: graph.y }, align: [0,-1] }, 148 | "Probability\nof photon\ndetection\n(horizontal)\nvs.\nposition of\nthe detector\n(vertical)"); 149 | 150 | app.labelManager.addLabel({ at: {x: amps.x+amps.width/2, y: amps.y+20 }, align: [0,-1] }, 151 | "Computation\nwindow with\nprobability\namplitude\narrows"); 152 | 153 | app.labelManager.addLabel({ at: {x: msg.x+msg.width/2, y: msg.y+msg.height/2 }, align: [0,0] }, 154 | "Instruction box"); 155 | } 156 | 157 | /* Steps */ 158 | /* ------- STEP 1 -------- */ 159 | function step1() { 160 | setLabels1(); 161 | app.showLabelLayer([btHelpContainer]); 162 | 163 | app.arrowManager.addArrow({ x: btHelpContainer.x - 15, y: btHelpContainer - 15 }, 135 ); 164 | app.arrowManager.show(); 165 | 166 | // Go to step 2 167 | btHelp.onPress(step2); 168 | btHelpContainer.blinkOn(true); 169 | 170 | for (var i = -SLIT_SIZE/2;i <= SLIT_SIZE/2;i+=1) { 171 | createLightPath(exp.height/2 + 10*i); 172 | } 173 | plotProbability(); 174 | 175 | detector.setPosition(560, exp.height/2); 176 | lightLayer.changePointAllPaths(2, { x: detector.x, y: detector.y }, true); 177 | 178 | amps.drawAmplitudes(lightLayer); 179 | amps.showTotalAmplitudeArrow(); 180 | 181 | glowManager.enable(); 182 | btGo.disable(); 183 | btGraph.onPress(function(){ amps.toggleTotalAmplitudeArrow(); }); 184 | } 185 | // This function creates a path with a point on the slit 186 | // It also creates the associated clock and the associations 187 | function createLightPath(y) { 188 | var path = new Engine.LightPath(1); 189 | path.addPoint({ x: photonSource.x, y: photonSource.y }); 190 | path.addPoint({ x: 300, y: y }); 191 | path.addPoint({ x: detector.x, y: detector.y }); 192 | path.updateData(); // call this manually so the Engine knows when to compute the entire path, instead of computing every new point 193 | lightLayer.addPath(path); 194 | path.clock = clocks.addClock(photonSource.color); 195 | } 196 | 197 | function plotProbability() { 198 | graph.clear(); 199 | var a, y; 200 | for(var i = 3; i < exp.height/10-2; i++) { 201 | y = i*10; 202 | lightLayer.changePointAllPaths(2, { x: detector.x, y: y }, true); 203 | graph.drawBar(y - exp.height/2, Math.pow(lightLayer.getTotalAmplitude().amplitude / lightLayer.numPaths(),2), photonSource.color); 204 | } 205 | lightLayer.changePointAllPaths(2, detector); 206 | } 207 | 208 | /* ------- STEP 2 -------- */ 209 | function step2() { 210 | setDefaultLabels(); 211 | btHelpContainer.blinkOff(); 212 | btNextContainer.blinkOn(true); 213 | btNext.onPress(step3); 214 | app.hideLabelLayer(); 215 | btHelp.onPress(function(){ 216 | app.toggleLabelLayer([btHelpContainer]); 217 | }); 218 | msg.setMessage( TEXT['move_detector'] ); 219 | 220 | app.arrowManager.clearArrows(); 221 | app.arrowManager.addArrow({ 222 | x: detector.x - 10, 223 | y: detector.y - 10 224 | }, 145); 225 | 226 | // exp.blinkOn(true); 227 | detector.onTop(); 228 | detector.enableDrag(); 229 | detector.setCursor("ns-resize"); 230 | Engine.addEvent({ source: detector, node: detector.container.node }, "moved", evDrag); 231 | Engine.addEvent({ source: detector, node: detector.container.node }, "startdrag", function(){ 232 | app.arrowManager.clearArrows(); 233 | amps.useTotalAmplitudeLengthText = false; 234 | if (amps.totalAmplitudeLengthText) amps.totalAmplitudeLengthText.hide(); 235 | // Engine.addEvent({ source: detector, node: document.body }, "enddrag", updateTotalAmplitude); 236 | }); 237 | } 238 | function evDrag(ev) { 239 | lightLayer.changePointAllPaths(2, detector); 240 | amps.drawAmplitudes(lightLayer); 241 | amps.showTotalAmplitudeArrow(); 242 | lightLayer.setFinalState(); 243 | } 244 | 245 | 246 | /* ------- STEP 3 -------- */ 247 | function step3() { 248 | btNextContainer.blinkOff(); 249 | btNext.disable(); 250 | btNext.offPress(); 251 | msg.setMessage( TEXT['change_color'] ); 252 | photonSource.setCursor("pointer"); 253 | 254 | app.arrowManager.clearArrows(); 255 | app.arrowManager.addArrow({ 256 | x: exp.x + photonSource.x + 20, 257 | y: exp.y + photonSource.y - 35 258 | },60); 259 | 260 | Engine.addEvent({ source: photonSource, node: photonSource.container.node }, "press", changeColor); 261 | } 262 | 263 | 264 | function changeColor(e) { 265 | photonSource.setColor(photonSource.color+1); 266 | lightLayer.setColor(Engine.STYLE.Colors[photonSource.color].color); 267 | lightLayer.setFrequency(Engine.STYLE.Colors[photonSource.color].frequency); 268 | lightLayer.drawAll(); 269 | plotProbability(); 270 | clocks.setAllColor(photonSource.color); 271 | amps.drawAmplitudes(lightLayer); 272 | amps.showTotalAmplitudeArrow(); 273 | // amps.setTotalAmplitudeColor(photonSource.color); 274 | lightLayer.setFinalState(); 275 | } 276 | 277 | /* ----------------------------------------- Start -------------------------------------------- */ 278 | amps.viewportScale = 2.5; 279 | SLIT_SIZE = 6; 280 | buildWalls(); 281 | 282 | Engine.C = 150; 283 | Engine.ANIMATION_SPEED = 1; 284 | Engine.FREQUENCY_ADJUST = 1.2; 285 | 286 | photonSource.setColor(2); 287 | var lightLayer = new Engine.LightLayer( 288 | Engine.STYLE.Colors[photonSource.color].color, 289 | Engine.STYLE.Colors[photonSource.color].frequency 290 | ); 291 | exp.addLightLayer(lightLayer); 292 | var glowManager = new Engine.GlowManager({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 293 | 294 | step1(); 295 | -------------------------------------------------------------------------------- /photons/linear-polarization-exp1.js: -------------------------------------------------------------------------------- 1 | // PHOTON Engine Tutorials 2 | // Copyright (C) 2015-2016 Georgetown University 3 | // Department of Physics - Washington, DC, USA 4 | // Written by Dylan Cutler (https://github.com/DCtheTall) 5 | // 6 | // -------------------------------------- LICENSE -------------------------------------------------- 7 | // 8 | // This library is free software; you can redistribute it and/or 9 | // modify it under the terms of the GNU Lesser General Public 10 | // License as published by the Free Software Foundation; either 11 | // version 2.1 of the License, or (at your option) any later version. 12 | // 13 | // This library is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | // Lesser General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Lesser General Public 19 | // License along with this library; if not, write to the Free Software 20 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | // 22 | // ################################################################################################# 23 | 24 | 25 | /* 26 | 27 | Linear Polarization Experiment 1: 28 | --------------------------------- 29 | Introduction to photon polarization 30 | 31 | This program uses the PHOTON engine to 32 | render an exercise which introduces users 33 | to photon polarization. 34 | 35 | Author: Dylan Cutler 36 | 37 | */ 38 | 39 | // Setting the PHOTON.experiment property to a function that starts 40 | // the exercise. 41 | PHOTON.experiment = function() { 42 | // Reference to the instance of the PHOTON app 43 | var app = PHOTON.instance 44 | // Adding a WebGL scene 45 | , scene = new PHOTON.SceneBox(10, 60, 980, 450); 46 | app.add(scene, true); 47 | // If WebGL is supported, the experiment begins 48 | if(PHOTON._WEB_GL_SUPPORT) beginExperiment(); 49 | } 50 | 51 | // This function is called when the app knows it can use WebGL 52 | // This part of the program defines the actual exercise 53 | function beginExperiment() { 54 | 55 | //== EXPERIMENT SETUP ==// 56 | 57 | // Reference to the instance of the application 58 | var app = PHOTON.instance; 59 | 60 | // Reference to object that renders the 3D scene 61 | var scene = PHOTON.children['SceneBox']; 62 | scene.placeCamera(-200, 50, 250, -200, 0, 0); 63 | scene.addPointLight({ 64 | name: 'scene-light', 65 | color: 0xffff99, 66 | decay: 2000, 67 | position: new THREE.Vector3(-100, 300, 50) 68 | }); 69 | 70 | //-- Adding message box and buttons --// 71 | 72 | // Refresh button 73 | var btRefresh = new PHOTON.ButtonRefresh(); 74 | app.add(btRefresh, true); 75 | 76 | // New window button 77 | var btWindow = new PHOTON.ButtonWindow(); 78 | app.add(btWindow, true); 79 | 80 | // Play button 81 | var btPlay = new PHOTON.ButtonPlay(720, 530, 100, 60); 82 | btPlay.disable(); 83 | app.add(btPlay, true); 84 | 85 | // Help button 86 | var btHelp = new PHOTON.ButtonHelp(850, 530, 80, 60); 87 | app.add(btHelp, true); 88 | 89 | // NEXT button 90 | var btNext = new PHOTON.ButtonNext(750, 620, 160, 50); 91 | btNext.disable(); 92 | app.add(btNext, true); 93 | 94 | // BACK button 95 | var btBack = new PHOTON.ButtonBack(750, 700, 160, 50); 96 | btBack.disable(); 97 | app.add(btBack, true); 98 | 99 | // Message box 100 | var msg = new PHOTON.MessageBox(10, 520, 650, 270); 101 | app.add(msg, true); 102 | 103 | //-- Help Layer setup --// 104 | 105 | PHOTON.helpLayerSetup = function() { 106 | var helpLayer = PHOTON.children['help-layer']; 107 | 108 | with(helpLayer) { 109 | // Rendering a clone of the help button 110 | renderHelpClone(850, 530, 80, 60); 111 | 112 | // Rendering arrows to point to various graphics elements 113 | addArrow(950, 140, 10); 114 | addArrow(800, 40, 80); 115 | addArrow(690, 470, 150); 116 | addArrow(680, 670, 80); 117 | addArrow(680, 720, 100); 118 | 119 | // Adding text which provides information about the components of the app 120 | addText(790, 150, 200, 'Press this button to restart the exercise from the beginning.'); 121 | addText(630, 10, 150, 'Press this button to open the exercise in another browser window.'); 122 | addText(590, 390, 200, 'Press this button when prompted to start animations.'); 123 | addText(485, 650, 200, 'Press these buttons when prompted to navigate through the exercise.'); 124 | addText(200, 300, 200, 'The experiment is depicted in this window.'); 125 | addText(200, 600, 200, 'This window provides helpful information about the experiment and how to navigate the exercise.'); 126 | } 127 | } 128 | 129 | //-- Elements for the experiment --// 130 | 131 | // Photon source 132 | var source = new PHOTON.PhotonSource3D(); 133 | source.translate(-200, 300, 0); 134 | 135 | // Polarizer 136 | var polarizer = new PHOTON.Polarizer(); 137 | polarizer.translate(0, 300, 0); 138 | 139 | // Screen 140 | var screen = new PHOTON.Screen3D(); 141 | screen.putInXZPlane('-x'); 142 | screen.translate(200, 300, 0); 143 | 144 | // LightPaths 145 | var lightPaths = PHOTON.lightPaths; 146 | 147 | // Photon 148 | var photon = new PHOTON.Photon3D(); 149 | photon.setPoyntingVector(1, 0); 150 | photon.polarize('random'); 151 | photon.translate(-200, 300, 0); 152 | 153 | //== TEXT FOR EXPERIMENT ==// 154 | 155 | var TEXT = { 156 | 'welcome': 157 | "Welcome to our first 3D interactive tutorial. This experiment demonstrates "+ 158 | "how individual photons travel through a polarizer onto a screen. Press the cyan "+ 159 | "help button on the right for information on how to use the simulation, press "+ 160 | "NEXT to continue.", 161 | 'introduce-photon-source': 162 | "In this experiment, we are going to be observing photons leaving a source "+ 163 | "depicted above. Press NEXT to continue.", 164 | 'introduce-screen': 165 | "The photon source emits photons onto the screen depicted above. Press NEXT "+ 166 | "to continue.", 167 | 'added-polarizer': 168 | "We have now added a polarizer in front of the photon source. This polarizer "+ 169 | "will polarize a photon to 0° from the vertical, and is labeled following "+ 170 | "this convention. Press the green play button to shoot the photon.", 171 | 'random-polarization': 172 | "When the photon leaves the source, we do not know the photon's polarization. "+ 173 | "The ring around the photon represents that the photon could have any possible "+ 174 | "polarization when it leaves the photon source. Press NEXT to continue.", 175 | 'know-polarization': 176 | "If the photon passes through the polarizer, we know that the photon "+ 177 | "is polarized to 0° from the vertical. The photon graphic has changed "+ 178 | "because now we know the photon's polarization. Press NEXT to continue.", 179 | 'conclusion': 180 | "The photon reaches the screen. The probability displayed on the right is "+ 181 | "the probability that a photon that passes through this first polarizer will "+ 182 | "and reach the screen."+ 183 | "

"+ 184 | "If we shoot a large number of photons at the screen "+ 185 | "through this setup, the screen illuminates. The brightness of the screen is "+ 186 | "related directly to the probability that each individual photon will go through."+ 187 | "

"+ 188 | "This is the end of this tutorial, press BACK if you would like to "+ 189 | "see how photons go through this setup again." 190 | } 191 | 192 | //== TUTORIAL SETUP ==// 193 | 194 | // Instance of the tutorial object 195 | var tutorial = new PHOTON.Tutorial(); 196 | 197 | // Step 0: introduce exercise and the photon source 198 | tutorial.addStep( 199 | { // Introduce exercise 200 | msg: TEXT['welcome'], 201 | blink: [ 202 | {object: btHelp, hold: true} 203 | ], 204 | trigger: 'next' 205 | }, 206 | { // Animate source into view 207 | animation: function(callback) { 208 | source.move(1, [0, -300, 0], function() { 209 | photon.translate(0, -300, 0); 210 | callback(); 211 | }) 212 | } 213 | }, 214 | { // Rotate source continuously 215 | msg: TEXT['introduce-photon-source'], 216 | continuous: true, 217 | labels: [ 218 | {object: source, dx: -100, dy: 160} 219 | ], 220 | animation: function(callback) { 221 | source.animateToAngle(4, 2*Math.PI, callback); 222 | }, 223 | endAnimation: function(callback) { 224 | source.animateToAngle(0.5, 2*Math.PI + source.theta, callback); 225 | }, 226 | trigger: 'next' 227 | } 228 | ); 229 | 230 | // Step 1: Introduce polarizer 231 | tutorial.addStep( 232 | { // Move camera 233 | // removeLabels: true, 234 | animation: function(callback) { 235 | scene.moveCamera(3, -130, 50, 300, -30, 0, 0, callback); 236 | } 237 | }, 238 | { // Introducing the screen 239 | animation: function(callback) { 240 | screen.move(1, [0, -300, 0], function() { 241 | var path = new PHOTON.LightPath3D(-200, 0, -190, 0); 242 | callback(); 243 | }); 244 | } 245 | }, 246 | { // Labeling screen and adding text 247 | animation: function(callback) { 248 | source.blink(); 249 | lightPaths[0].animateLengthChange(0.25, 390, callback); 250 | } 251 | }, 252 | { // Explain screen 253 | labels: [ 254 | {object: screen, dx: -100, dy: 160} 255 | ], 256 | msg: TEXT['introduce-screen'], 257 | trigger: 'next' 258 | } 259 | ); 260 | 261 | // Step 2: Introduce polarizer 262 | tutorial.addStep( 263 | { // Moving the polarizer into view 264 | animation: function(callback) { 265 | polarizer.move(1, [0, -300, 0], callback); 266 | } 267 | } 268 | ) 269 | 270 | // Step 3: Shoot photon into screen 271 | tutorial.addStep( 272 | { // Explaining the polarizer 273 | labels: [ 274 | {object: polarizer, dx: -90, dy: 175} 275 | ], 276 | msg: TEXT['added-polarizer'], 277 | trigger: 'play' 278 | }, 279 | { // Shoot photon with random polarization and zoom in on photon 280 | removeLabels: true, 281 | animation: function(callback) { 282 | source.blink(); 283 | photon.shoot(1.2, 120, function() { 284 | scene.moveCamera(3, -60, 50, 150, -60, 0, 0, callback); 285 | }); 286 | } 287 | }, 288 | { // Explain random polarization 289 | msg: TEXT['random-polarization'], 290 | trigger: 'next' 291 | }, 292 | { // Zoom out then shoot the photon again through the polarizer then zoom in again 293 | animation: function(callback) { 294 | scene.moveCamera(3, 130, 50, 300, 30, 0, 0, function() { 295 | photon.shoot(0.8, 80, function() { 296 | photon.polarize(0); 297 | photon.shoot(0.8, 80, function() { 298 | scene.moveCamera(3, 60, 50, 150, 60, 0, 0, callback); 299 | }) 300 | }) 301 | }); 302 | } 303 | }, 304 | { // Explain the vertical polarization 305 | msg: TEXT['know-polarization'], 306 | trigger: 'next' 307 | }, 308 | { // Shoot photon into the screen 309 | animation: function(callback) { 310 | scene.moveCamera(3, 0, 50, 250, 200, 0, 0, function() { 311 | photon.shoot(1.3, 130, function() { 312 | screen.illuminate(1); 313 | scene.moveCamera(3, 50, 50, 0, 200, 0, 0, callback) 314 | }) 315 | }); 316 | } 317 | } 318 | ); 319 | 320 | // Step 4: Conclude the exercise and give users the opportunity to go back 321 | tutorial.addStep( 322 | { // Final part 323 | msg: TEXT['conclusion'], 324 | back: function() { 325 | screen.reset() 326 | photon.translate(-410, 0, 0); 327 | photon.polarize('random'); 328 | scene.placeCamera(-130, 50, 300, -30, 0, 0); 329 | source.addLabel(-100, 160); 330 | screen.addLabel(-100, 160); 331 | } 332 | } 333 | ); 334 | 335 | // Starting the experiment 336 | setTimeout(function(){ 337 | PHOTON.children['loading'].$.fadeOut({ duration: 300 }); 338 | setTimeout(tutorial.start, 500); 339 | }, 2000); 340 | 341 | } // End of the definition of the exercise 342 | 343 | // Initializing the application when the HTML document is ready 344 | $(document).ready(function() { 345 | // Setting the intro 346 | PHOTON._INTRO_MESSAGE = "Welcome to the first exercise on
"+ 347 | "photon polarization.
"+ 348 | "Press START to begin the exercise."; 349 | 350 | // Initializating the PHOTON object 351 | PHOTON.init(1000, 800); 352 | }); 353 | -------------------------------------------------------------------------------- /qed/exp_12_single_slit_width.js: -------------------------------------------------------------------------------- 1 | // ################################################################################################# 2 | // QED Engine tutorials 3 | // Copyright (C) 2015-2016 Georgetown University 4 | // Department of Physics - Washington, DC, USA 5 | // Written by Lucas Vieira (https://github.com/1ucasvb) 6 | // Dylan Cutler (https://github.com/DCtheTall) 7 | // 8 | // -------------------------------------- LICENSE -------------------------------------------------- 9 | // 10 | // This library is free software; you can redistribute it and/or 11 | // modify it under the terms of the GNU Lesser General Public 12 | // License as published by the Free Software Foundation; either 13 | // version 2.1 of the License, or (at your option) any later version. 14 | // 15 | // This library is distributed in the hope that it will be useful, 16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | // Lesser General Public License for more details. 19 | // 20 | // You should have received a copy of the GNU Lesser General Public 21 | // License along with this library; if not, write to the Free Software 22 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | // 24 | // ################################################################################################# 25 | 26 | 27 | /* STANDARD SETUP */ 28 | var clocksBoxHeight = 150; // helps set up overall height 29 | 30 | // Main object definitions 31 | var app = Engine.create("experiment", 1000, 430 + clocksBoxHeight); 32 | $(document.body).append(app.div); 33 | 34 | var exp = new Engine.ExperimentBox("exp", 600, 400); 35 | app.add(exp, 10, 10); 36 | 37 | var graph = new Engine.GraphBox("graph", 120, 400, 2); 38 | app.add(graph, 620, 10); 39 | 40 | var amps = new Engine.AmplitudeBox("amps", 240, 240); 41 | app.add(amps, 750, 10); 42 | 43 | var msg = new Engine.MessageBox("msg", 240, 60 + clocksBoxHeight); 44 | app.add(msg, 750, 360); 45 | 46 | var clocks = new Engine.ClockBox("clocks", 730, clocksBoxHeight); 47 | clocks.setRadius(30); 48 | app.add(clocks, 10, 420); 49 | 50 | // Button in containers, necessary to make it above the label overlay 51 | var btHelpContainer = new Engine.RaphaelPaper("btHelpContainer", 50, 40); 52 | var btHelp = new Engine.ButtonHelp(); 53 | btHelpContainer.add(btHelp, 0, 0); 54 | app.add(btHelpContainer, 940, 260); 55 | 56 | var btGoContainer = new Engine.RaphaelPaper("btGoContainer", 50, 40); 57 | var btGo = new Engine.ButtonGo(); 58 | btGoContainer.add(btGo, 0, 0); 59 | app.add(btGoContainer, 750, 260); 60 | 61 | var btNextContainer = new Engine.RaphaelPaper("btNextContainer", 120, 40); 62 | var btNext = new Engine.ButtonNext(); 63 | btNextContainer.add(btNext, 0, 0); 64 | app.add(btNextContainer, 810, 260); 65 | 66 | var btGraph = new Engine.ButtonGraph(); 67 | amps.add(btGraph, amps.width-btGraph.width-5, amps.height-btGraph.height-5); 68 | 69 | /* ------------------------------ BEGIN EXPERIMENT ------------------------------ */ 70 | 71 | var TEXT = { 72 | 'welcome': 73 | "

Welcome to the next\nsingle slit tutorial.\n\n"+ 74 | "This time, we will explore what\nhappens when light passes\nthrough a slit of different widths\nand relate that to changing the color of light.\n\n\n"+ 75 | "To continue, press

" 76 | , 77 | 'slit_size': 78 | "In this tutorial, we will explore what happens when the width of the slit changes.\n\n"+ 79 | "Press the yellow buttons on the left to change the width of the slit and to see what happens to the probability distribution.\n\n"+ 80 | "Be sure to move the detector and study how the probability amplitudes change with detector position.\n\n"+ 81 | "Can you describe in words what happens when the slit becomes smaller? What about when it becomes larger? Can you explain why it behaves this way in terms of the amplitude arrows?\n\n"+ 82 | "When you are done, press " 83 | , 84 | 'change_color': 85 | "Now, we can also explore how the pattern changes with the color of the photon. Click the photon source to change its color.\n\n"+ 86 | "How does the color of the photon affect the shape of the probability distribution for slits of different sizes?\n\n"+ 87 | "Recall that the rate of rotation of the clocks is faster for blue than red.\nTry to explain in words the relation between the width of the slit for each probability curve.\n\nCan you relate the changes of the slit width for fixed photon color to those for changing the color with a fixed slit width?\n\n"+ 88 | "This concludes this tutorial." 89 | } 90 | 91 | // Source 92 | var photonSource = new Engine.PhotonSource("source", 0); 93 | photonSource.setTitle("Source"); 94 | exp.add(photonSource, 30, 200); 95 | 96 | // Detector rail 97 | var rail = new Engine.Rail(10,360); 98 | exp.add(rail, 560, 20); 99 | 100 | // Detector 101 | var detector = new Engine.Detector("detector"); 102 | detector.snap = 10; 103 | detector.setTitle("Detector"); 104 | exp.add(detector, 560, 200); 105 | detector.setDragBounds([560, 560], [30, 370]); 106 | 107 | var btSlitNarrow = new Engine.ButtonSlitNarrow(); 108 | var btSlitWide = new Engine.ButtonSlitWide(); 109 | exp.add(btSlitNarrow, 70, 290); 110 | exp.add(btSlitWide, 70+70, 290); 111 | 112 | var wall1, wall2; 113 | function buildWalls() { 114 | var a = (SLIT_SIZE+1)*10; 115 | var h = exp.height/2 - a/2; 116 | var w = 10; 117 | wall1 = new Engine.Wall(w,h,5); 118 | wall2 = new Engine.Wall(w,h,5); 119 | exp.add(wall1, exp.width/2-w/2, 0); 120 | exp.add(wall2, exp.width/2-w/2, exp.height - h); 121 | } 122 | 123 | /* Labels */ 124 | 125 | function setLabels1() { 126 | app.labelManager.clearLabels(); 127 | app.labelManager.addLabel({ at: {x: app.width/2-20, y: app.height/2}, align: [0,0] }, TEXT['welcome'] ); 128 | } 129 | 130 | function setDefaultLabels() { 131 | app.labelManager.clearLabels(); 132 | app.arrowManager.clearArrows(); 133 | 134 | app.labelManager.addLabel({ at: {x: 10, y: 10}, align: [-1,-1] }, 135 | "

press again to exit the help window.

"); 136 | 137 | app.labelManager.addLabel({ at: photonSource, align: [-1,0] }, 138 | "Photon\nsource\nPress to\nchange color"); 139 | 140 | app.labelManager.addLabel({ at: detector, align: [1,0] }, 141 | "Photon\ndetector\nDrag to\nchange\nposition"); 142 | 143 | app.labelManager.addLabel({ at: {x: clocks.x + clocks.width/2, y: clocks.y + clocks.height/2 }, align: [0,0] }, 144 | "Clock for each path\nMouse over or long tap to highlight an\nindividual path and its respective clock and arrow"); 145 | 146 | app.labelManager.addLabel({ at: {x: btGoContainer.x + 15, y: btGoContainer.y+25 }, align: [1,1] }, 147 | "Emit\nphoton"); 148 | 149 | app.labelManager.addLabel({ at: {x: btNextContainer.x + 60, y: btNextContainer.y+25 }, align: [0,1] }, 150 | "Go to\nnext step"); 151 | 152 | app.labelManager.addLabel({ at: {x: graph.x+graph.width/2, y: graph.y }, align: [0,-1] }, 153 | "Probability\nof photon\ndetection\n(horizontal)\nvs.\nposition of\nthe detector\n(vertical)"); 154 | 155 | app.labelManager.addLabel({ at: {x: amps.x+amps.width/2, y: amps.y+20 }, align: [0,-1] }, 156 | "Computation\nwindow with\nprobability\namplitude\narrows"); 157 | 158 | app.labelManager.addLabel({ at: {x: msg.x+msg.width/2, y: msg.y+msg.height/2 }, align: [0,0] }, 159 | "Instruction box"); 160 | 161 | app.labelManager.addLabel({ at: {x: btSlitWide.x + btSlitWide.width/2, y: btSlitWide.y + btSlitWide.height*0.7 }, align: [-1,0] }, 162 | "Widen\nslit"); 163 | 164 | app.labelManager.addLabel({ at: {x: btSlitNarrow.x + btSlitNarrow.width/2, y: btSlitNarrow.y + btSlitNarrow.height*0.7 }, align: [0.75,0] }, 165 | "Narrow\nslit"); 166 | } 167 | 168 | /* Steps */ 169 | /* ------- STEP 1 -------- */ 170 | function step1() { 171 | setLabels1(); 172 | app.showLabelLayer([btHelpContainer]); 173 | 174 | app.arrowManager.addArrow({ x: btHelpContainer.x - 15, y: btHelpContainer.y - 15 }, 135 ); 175 | app.arrowManager.show(); 176 | 177 | // Go to step 2 178 | btHelp.onPress(step2); 179 | btHelpContainer.blinkOn(true); 180 | 181 | for (var i = -SLIT_SIZE/2;i <= SLIT_SIZE/2;i+=1) { 182 | createLightPath(exp.height/2 + 10*i); 183 | } 184 | plotProbability(); 185 | 186 | detector.setPosition(560, exp.height/2); 187 | lightLayer.changePointAllPaths(2, { x: detector.x, y: detector.y }, true); 188 | 189 | amps.drawAmplitudes(lightLayer); 190 | amps.showTotalAmplitudeArrow(); 191 | 192 | glowManager.enable(); 193 | btGo.disable(); 194 | btGraph.onPress(function(){ amps.toggleTotalAmplitudeArrow(); }); 195 | 196 | btSlitNarrow.onPress(function(){ 197 | if (SLIT_SIZE == 2) return; 198 | SLIT_SIZE -= 2; 199 | 200 | if (SLIT_SIZE == 2) { btSlitNarrow.disable(); } 201 | btSlitWide.enable(); 202 | 203 | slitChanged(); 204 | }); 205 | btSlitWide.onPress(function(){ 206 | if (SLIT_SIZE == 8) return; 207 | SLIT_SIZE += 2; 208 | 209 | if (SLIT_SIZE == 8) { btSlitWide.disable(); } 210 | btSlitNarrow.enable(); 211 | 212 | slitChanged(); 213 | }); 214 | } 215 | 216 | function slitChanged() { 217 | lightLayer.clear(); 218 | clocks.clear(); 219 | exp.remove(wall1); exp.remove(wall2); 220 | buildWalls(); 221 | amps.clear(); 222 | for (var i = -SLIT_SIZE/2;i <= SLIT_SIZE/2;i+=1) { 223 | createLightPath(exp.height/2 + 10*i); 224 | } 225 | plotProbability(); 226 | 227 | lightLayer.changePointAllPaths(2, detector); 228 | amps.drawAmplitudes(lightLayer); 229 | amps.showTotalAmplitudeArrow(); 230 | lightLayer.setFinalState(); 231 | 232 | glowManager.assign({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 233 | glowManager.enable(); 234 | } 235 | 236 | // This function creates a path with a point on the slit 237 | // It also creates the associated clock and the associations 238 | function createLightPath(y) { 239 | var path = new Engine.LightPath(1); 240 | path.addPoint({ x: photonSource.x, y: photonSource.y }); 241 | path.addPoint({ x: 300, y: y }); 242 | path.addPoint({ x: detector.x, y: detector.y }); 243 | path.updateData(); // call this manually so the Engine knows when to compute the entire path, instead of computing every new point 244 | lightLayer.addPath(path); 245 | path.clock = clocks.addClock(photonSource.color); 246 | } 247 | 248 | function plotProbability() { 249 | graph.clear(); 250 | var a, y; 251 | for(var i = 3; i < exp.height/10-2; i++) { 252 | y = i*10; 253 | lightLayer.changePointAllPaths(2, { x: detector.x, y: y }, true); 254 | graph.drawBar(y - exp.height/2, Math.pow(lightLayer.getTotalAmplitude().amplitude / lightLayer.numPaths(),2), photonSource.color); 255 | } 256 | lightLayer.changePointAllPaths(2, detector); 257 | } 258 | 259 | /* ------- STEP 2 -------- */ 260 | function step2() { 261 | setDefaultLabels(); 262 | btHelpContainer.blinkOff(); 263 | btNextContainer.blinkOn(true); 264 | btNext.onPress(step3); 265 | app.hideLabelLayer(); 266 | btHelp.onPress(function(){ 267 | app.toggleLabelLayer([btHelpContainer]); 268 | }); 269 | msg.setMessage( TEXT['slit_size'] ); 270 | 271 | app.arrowManager.clearArrows(); 272 | app.arrowManager.addArrow({ 273 | x: detector.x - 10, 274 | y: detector.y - 10 275 | }, 145); 276 | 277 | // exp.blinkOn(true); 278 | detector.onTop(); 279 | detector.enableDrag(); 280 | detector.setCursor("ns-resize"); 281 | Engine.addEvent({ source: detector, node: detector.container.node }, "moved", evDrag); 282 | Engine.addEvent({ source: detector, node: detector.container.node }, "startdrag", function(){ 283 | app.arrowManager.clearArrows(); 284 | amps.useTotalAmplitudeLengthText = false; 285 | if (amps.totalAmplitudeLengthText) amps.totalAmplitudeLengthText.hide(); 286 | // Engine.addEvent({ source: detector, node: document.body }, "enddrag", updateTotalAmplitude); 287 | }); 288 | } 289 | function evDrag(ev) { 290 | lightLayer.changePointAllPaths(2, detector); 291 | amps.drawAmplitudes(lightLayer); 292 | amps.showTotalAmplitudeArrow(); 293 | lightLayer.setFinalState(); 294 | } 295 | 296 | 297 | /* ------- STEP 3 -------- */ 298 | function step3() { 299 | btNextContainer.blinkOff(); 300 | btNext.disable(); 301 | btNext.offPress(); 302 | msg.setMessage( TEXT['change_color'] ); 303 | photonSource.setCursor("pointer"); 304 | 305 | app.arrowManager.clearArrows(); 306 | app.arrowManager.addArrow({ 307 | x: exp.x + photonSource.x + 20, 308 | y: exp.y + photonSource.y - 35 309 | },60); 310 | 311 | Engine.addEvent({ source: photonSource, node: photonSource.container.node }, "press", changeColor); 312 | } 313 | 314 | 315 | function changeColor(e) { 316 | photonSource.setColor(photonSource.color+1); 317 | lightLayer.setColor(Engine.STYLE.Colors[photonSource.color].color); 318 | lightLayer.setFrequency(Engine.STYLE.Colors[photonSource.color].frequency); 319 | lightLayer.drawAll(); 320 | plotProbability(); 321 | clocks.setAllColor(photonSource.color); 322 | amps.drawAmplitudes(lightLayer); 323 | amps.showTotalAmplitudeArrow(); 324 | // amps.setTotalAmplitudeColor(photonSource.color); 325 | lightLayer.setFinalState(); 326 | } 327 | 328 | /* ----------------------------------------- Start -------------------------------------------- */ 329 | amps.viewportScale = 2.5; 330 | SLIT_SIZE = 6; 331 | buildWalls(); 332 | 333 | Engine.C = 150; 334 | Engine.ANIMATION_SPEED = 1; 335 | Engine.FREQUENCY_ADJUST = 1.2; 336 | 337 | var lightLayer = new Engine.LightLayer( 338 | Engine.STYLE.Colors[photonSource.color].color, 339 | Engine.STYLE.Colors[photonSource.color].frequency 340 | ); 341 | exp.addLightLayer(lightLayer); 342 | var glowManager = new Engine.GlowManager({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 343 | 344 | step1(); 345 | -------------------------------------------------------------------------------- /qed/exp_00_detectors_in_and_outside_glass.js: -------------------------------------------------------------------------------- 1 | // ################################################################################################# 2 | // QED Engine tutorials 3 | // Copyright (C) 2015-2016 Georgetown University 4 | // Department of Physics - Washington, DC, USA 5 | // Written by Lucas Vieira (https://github.com/1ucasvb) 6 | // Dylan Cutler (https://github.com/DCtheTall) 7 | // 8 | // -------------------------------------- LICENSE -------------------------------------------------- 9 | // 10 | // This library is free software; you can redistribute it and/or 11 | // modify it under the terms of the GNU Lesser General Public 12 | // License as published by the Free Software Foundation; either 13 | // version 2.1 of the License, or (at your option) any later version. 14 | // 15 | // This library is distributed in the hope that it will be useful, 16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | // Lesser General Public License for more details. 19 | // 20 | // You should have received a copy of the GNU Lesser General Public 21 | // License along with this library; if not, write to the Free Software 22 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | // 24 | // ################################################################################################# 25 | 26 | 27 | /* STANDARD SETUP */ 28 | var clocksBoxHeight = 150; // helps set up overall height 29 | 30 | // Main object definitions 31 | var app = Engine.create("experiment", 1000, 430 + clocksBoxHeight); 32 | $(document.body).append(app.div); 33 | 34 | var exp = new Engine.ExperimentBox("exp", 600, 400); 35 | app.add(exp, 10, 10); 36 | 37 | var graph = new Engine.GraphBox("graph", 120, 400); 38 | app.add(graph, 620, 10); 39 | 40 | var amps = new Engine.AmplitudeBox("amps", 240, 240); 41 | var amps2 = new Engine.AmplitudeBox("amps2", 240, 240); 42 | app.add(amps, 750, 10); 43 | 44 | 45 | var msg = new Engine.MessageBox("msg", 240, 60 + clocksBoxHeight); 46 | app.add(msg, 750, 360); 47 | 48 | var clocks = new Engine.ClockBox("clocks", 730, clocksBoxHeight); 49 | clocks.setRadius(50); 50 | app.add(clocks, 10, 420); 51 | 52 | // Button in containers, necessary to make it above the label overlay 53 | var btHelpContainer = new Engine.RaphaelPaper("btHelpContainer", 50, 40); 54 | var btHelp = new Engine.ButtonHelp(); 55 | btHelpContainer.add(btHelp, 0, 0); 56 | app.add(btHelpContainer, 940, 260); 57 | 58 | var btGoContainer = new Engine.RaphaelPaper("btGoContainer", 50, 40); 59 | var btGo = new Engine.ButtonGo(); 60 | btGoContainer.add(btGo, 0, 0); 61 | app.add(btGoContainer, 750, 260); 62 | 63 | var btNextContainer = new Engine.RaphaelPaper("btNextContainer", 120, 40); 64 | var btNext = new Engine.ButtonNext(); 65 | btNextContainer.add(btNext, 0, 0); 66 | app.add(btNextContainer, 810, 260); 67 | 68 | // var btGraph = new Engine.ButtonGraph(); 69 | // amps.add(btGraph, amps.width-btGraph.width-5, amps.height-btGraph.height-5); 70 | 71 | /* ------------------------------ BEGIN EXPERIMENT ------------------------------ */ 72 | 73 | var angle = -5; 74 | var distance = 120; 75 | 76 | amps.viewportScale = 5; 77 | amps2.viewportScale = 5; 78 | 79 | Engine.FREQUENCY_ADJUST = 0.7; 80 | 81 | // Source 82 | var photonSource = new Engine.PhotonSource("source", 0, true); 83 | 84 | // Detector 85 | var detector = new Engine.Detector("detector", false); 86 | 87 | // photonSource.setOpacity(0.5); 88 | // detector.setOpacity(0.5); 89 | 90 | Engine.ANIMATION_SPEED = 1; 91 | var spaceBelow = 40; 92 | var glassWidth = 400; 93 | var glassThickness = 10*16; 94 | var glassIOR = 2; 95 | 96 | function intro() { 97 | app.labelManager.addLabel({ at: {x: app.width/2-60, y: app.height/2}, align: [0,0] }, 98 | "

Welcome to the partial reflection tutorial.\n\n"+ 99 | "Here, we will see what happens when\nlight passes through and reflects off a piece of glass.\n\n"+ 100 | "To begin, press

"); 101 | 102 | app.showLabelLayer([btNextContainer]); 103 | btNextContainer.blinkOn(true); 104 | btNext.onPress(step1); 105 | btNext.enable(); 106 | 107 | btHelp.disable(); 108 | btGo.disable(); 109 | 110 | app.arrowManager.addArrow({ 111 | x: btNextContainer.x - 20, 112 | y: btNextContainer.y - 20 113 | },135); 114 | app.arrowManager.show(); 115 | app.arrowManager.onTop(); 116 | } 117 | 118 | function step1() { 119 | btNext.offPress(); 120 | btNext.disable(); 121 | btNextContainer.blinkOff(); 122 | 123 | app.labelManager.clearLabels(); 124 | app.labelManager.addLabel({ at: {x: app.width/2-20, y: app.height/2}, align: [0,0] }, 125 | "

Instructions will be given\nin the instruction box,\non the bottom right."+ 126 | ""+ 127 | "

" 128 | ); 129 | 130 | app.showLabelLayer([msg]); 131 | 132 | app.arrowManager.clearArrows(); 133 | app.arrowManager.addArrow({ 134 | x: msg.x - 20, 135 | y: msg.y + msg.height/2 136 | },180); 137 | app.arrowManager.onTop(); 138 | 139 | msg.setMessage( 140 | "This is the instruction box. Instructions will be shown here, and the box will glow when there's a new instruction.\n\n"+ 141 | "Press in this box to continue.", true); 142 | 143 | Engine.addEvent({ source: msg, node: msg.div }, "press", step2); 144 | } 145 | function step2() { 146 | Engine.removeEvent({ source: msg, node: msg.div }, "press"); 147 | 148 | msg.setMessage( 149 | "At the left, we see the experiment window, where the simulations will occur. Right now, it is empty.\n\n"+ 150 | "Press "+""+" to add a source of photons and a detector."); 151 | 152 | app.arrowManager.clearArrows(); 153 | app.arrowManager.hide(); 154 | 155 | btNextContainer.blinkOn(true); 156 | 157 | app.hideLabelLayer(); 158 | 159 | btNext.onPress(step3); 160 | btNext.enable(); 161 | 162 | } 163 | 164 | function step3() { 165 | btNextContainer.blinkOff(); 166 | 167 | exp.add(photonSource, 168 | exp.width/2 + distance*Math.sin(angle*Engine.DEG2RAD), 169 | exp.height/2 - distance*Math.cos(angle*Engine.DEG2RAD) 170 | ); 171 | photonSource.setRotation(90+angle); 172 | 173 | exp.add(detector, 174 | exp.width/2 + distance*Math.sin((180+angle)*Engine.DEG2RAD), 175 | exp.height/2 - distance*Math.cos((180+angle)*Engine.DEG2RAD) 176 | ); 177 | detector.setRotation(90+angle); 178 | 179 | msg.setMessage( 180 | "We re-examine the simplest possible experiment. The event is describes as follows: a photon leaves the source, moves in a straight line, and hits the detector. We consider only one alternative way for the event to occur. The path the photon will take is shown as a straight line.\n\n"+ 181 | "For each path, there will be a clock and an amplitude arrow pointing in the same direction as the clock's hand. The hand starts at 12 o'clock.\n\n"+ 182 | "When the photon is emitted, the arrow has unit length. As the photon moves through the path, the clock will rotate very rapidly according to the color of the light (36,000 revolutions per inch for red light). When the photon interacts with objects, the size of the amplitude arrow may shrink.\n\n"+ 183 | "Press "+""+" to begin the simulation."); 184 | 185 | var path = new Engine.LightPath(1); 186 | path.addPoint({ x: photonSource.x, y: photonSource.y }); 187 | path.addPoint({ x: detector.x, y: detector.y }); 188 | path.updateData(); // call this manually so the Engine knows when to compute the entire path, instead of computing every new point 189 | lightLayer.addPath(path); 190 | clocks.setRadius(60); 191 | path.clock = clocks.addClock(photonSource.color); 192 | 193 | path.clock.mockAmplitudeBox = amps; 194 | amps.drawMockAmplitude(0); 195 | 196 | btGoContainer.blinkOn(true); 197 | 198 | btNext.disable(); 199 | btGo.enable(); 200 | btGo.onPress(step4); 201 | 202 | 203 | } 204 | 205 | function step4() { 206 | btGoContainer.blinkOff(); 207 | btGo.disable(); 208 | lightLayer.shootAllPhotons(step5); 209 | } 210 | 211 | function step5() { 212 | amps.drawAmplitudes(lightLayer); 213 | detector.blink(); 214 | 215 | glowManager.assign({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 216 | glowManager.enable(); 217 | 218 | msg.setMessage( 219 | "The final position of the clock for this path is represented by an arrow in the amplitude arrows window, on the top right.\n\n"+ 220 | "Pay close attention to the direction and size of this arrow.\n\n"+ 221 | "Now, let's make things more interesting. Press "+""+" to place the detector inside a thick piece of glass." 222 | ); 223 | amps.blinkOn(true); 224 | 225 | btNext.enable(); 226 | btNext.onPress(step6); 227 | } 228 | 229 | function step6() { 230 | exp.add(new Engine.Glass(glassWidth, glassThickness), exp.width/2-glassWidth/2, exp.height - glassThickness - spaceBelow, true); 231 | 232 | exp.add(new Engine.Marker("Air"), exp.width/2-glassWidth/2, exp.height/2 - 20); 233 | exp.add(new Engine.Marker("Glass"), exp.width/2-glassWidth/2, exp.height/2 + 20); 234 | 235 | amps.blinkOff(); 236 | 237 | lightLayer.clear(); 238 | clocks.clear(); 239 | app.remove(amps); 240 | app.add(amps2, 750, 10); 241 | 242 | var path; 243 | path = new Engine.LightPath(1); 244 | path.addPoint({ x: photonSource.x, y: photonSource.y }); 245 | path.addPoint({ x: exp.width/2, y: exp.height/2, ior: 2, trigger: function() { 246 | msg.setMessage( 247 | "This path leads the photon to hit the surface of the glass before being transmitted through it.\n\n"+ 248 | "Therefore, for this path, we must shrink the amplitude arrow by a factor representing the chance that it does get transmitted, instead of reflected.\n\n"+ 249 | "The arrow shrinks slightly to 98% of its original size, since 0.98² ≈ 96%, the chance of transmission. However, this is hard to see.\n\n"+ 250 | "Press "+""+" to continue." 251 | ); 252 | lightLayer.pauseAnimation(); 253 | btGo.enable(); 254 | btGo.onPress(function(){ 255 | btGo.disable(); 256 | lightLayer.resumeAnimation(); 257 | }); 258 | } }); 259 | path.addPoint({ x: detector.x, y: detector.y }); 260 | path.updateData(); // call this manually so the Engine knows when to compute the entire path, instead of computing every new point 261 | lightLayer.addPath(path); 262 | path.clock = clocks.addClock(photonSource.color); 263 | 264 | msg.setMessage( 265 | "The photon will take the same path, but inside the glass it will move more slowly. The clock, however, spins at the same rate.\n\n"+ 266 | "How will this affect the final amplitude arrow? Find out by pressing "+""+" again." 267 | ); 268 | 269 | amps2.drawMockAmplitude(0); 270 | path.clock.mockAmplitudeBox = amps2; 271 | 272 | btNext.disable(); 273 | btGo.enable(); 274 | btGo.onPress(function(){ 275 | btGo.disable(); 276 | lightLayer.shootAllPhotons(step7); 277 | }); 278 | } 279 | 280 | function step7() { 281 | amps2.drawAmplitudes(lightLayer); 282 | detector.blink(); 283 | 284 | glowManager.assign({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps2 }); 285 | glowManager.enable(); 286 | 287 | amps2.blinkOn(true); 288 | msg.setMessage( 289 | "Now check the new amplitude arrow. How does it compare to the previous one?\n\n"+ 290 | "Keep in mind what happens in the presence of the glass.\n\n"+ 291 | "Press "+""+" to find out if you are right." 292 | ); 293 | 294 | btNext.enable(); 295 | btNext.onPress(step8); 296 | } 297 | 298 | function step8() { 299 | btNext.disable(); 300 | 301 | amps.resetBlink(); 302 | app.add(amps, amps.x, amps.y); 303 | amps.drawLabel("Without glass") 304 | app.add(amps, app.width/2 - amps.width/2 - amps.width/2*1.25, app.height/2 - amps.height ); 305 | $(amps.div).hide().fadeIn(); 306 | amps.onTop(); 307 | amps.blinkOn(true); 308 | 309 | amps2.resetBlink(); 310 | $(amps2.div).animate({ left: app.width/2 - amps.width/2 + amps.width/2*1.25, top: app.height/2 - amps.height }); 311 | amps2.drawLabel("With glass"); 312 | amps2.onTop(); 313 | amps2.blinkOn(true); 314 | 315 | app.labelManager.clearLabels(); 316 | app.showLabelLayer([amps,amps2,msg]); 317 | 318 | msg.setMessage( 319 | "Now compare both arrows. How are they similar and how are they different?\n\n"+ 320 | "This concludes this tutorial. Please, proceed to the next section of the course." 321 | ); 322 | 323 | } 324 | 325 | function step() { 326 | app.labelManager.clearLabels(); 327 | app.labelManager.addLabel({ at: {x: app.width/2-20, y: app.height/2}, align: [0,0] }, 328 | "

Welcome to the first interactive tutorial.\n\n"+ 329 | "Here, we will see what happens when\nlight passes through a piece of glass.\n\n"+ 330 | "To begin, press

"); 331 | 332 | exp.add(photonSource, 333 | exp.width/2 + distance*Math.sin(angle*Engine.DEG2RAD), 334 | exp.height/2 - distance*Math.cos(angle*Engine.DEG2RAD) 335 | ); 336 | photonSource.setRotation(90+angle); 337 | 338 | exp.add(detector, 339 | exp.width/2 + distance*Math.sin((180+angle)*Engine.DEG2RAD), 340 | exp.height/2 - distance*Math.cos((180+angle)*Engine.DEG2RAD) 341 | ); 342 | detector.setRotation(90+angle); 343 | 344 | } 345 | 346 | var lightLayer = new Engine.LightLayer( 347 | Engine.STYLE.Colors[photonSource.color].color, 348 | Engine.STYLE.Colors[photonSource.color].frequency 349 | ); 350 | exp.addLightLayer(lightLayer); 351 | var glowManager = new Engine.GlowManager({ 'paths': lightLayer, 'clocks': clocks, 'amplitudes': amps }); 352 | 353 | 354 | intro(); 355 | --------------------------------------------------------------------------------