Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create basic model and view for Screen 4: Bloch Sphere #54

Open
AgustinVallejo opened this issue Oct 9, 2024 · 3 comments
Open

Create basic model and view for Screen 4: Bloch Sphere #54

AgustinVallejo opened this issue Oct 9, 2024 · 3 comments
Assignees

Comments

@AgustinVallejo
Copy link
Contributor

No description provided.

@AgustinVallejo AgustinVallejo self-assigned this Oct 9, 2024
@AgustinVallejo AgustinVallejo changed the title Create basic model and view for Screen 4: Bloch Create basic model and view for Screen 4: Bloch Sphere Jan 2, 2025
jbphet added a commit that referenced this issue Jan 3, 2025
jbphet added a commit that referenced this issue Jan 3, 2025
AgustinVallejo added a commit that referenced this issue Jan 3, 2025
jbphet added a commit that referenced this issue Jan 3, 2025
jbphet added a commit that referenced this issue Jan 3, 2025
AgustinVallejo added a commit that referenced this issue Jan 4, 2025
jbphet added a commit that referenced this issue Jan 9, 2025
jbphet added a commit that referenced this issue Jan 9, 2025
jbphet added a commit that referenced this issue Jan 10, 2025
@jbphet
Copy link
Collaborator

jbphet commented Jan 13, 2025

At the previous design meeting we discussed the possibility of representing an atom or set of atoms being tested, and depicting the magnetic field as applying to them instead of to the Bloch sphere. I've created a prototype version of this and will show it at tomorrow's design meeting to see if the design team wants us to fold this in. Here is a patch with the changes:

Subject: [PATCH] experiment with visual representation of atom under test, see https://github.com/phetsims/quantum-measurement/issues/54
---
Index: js/bloch-sphere/model/BlochSphereModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/bloch-sphere/model/BlochSphereModel.ts b/js/bloch-sphere/model/BlochSphereModel.ts
--- a/js/bloch-sphere/model/BlochSphereModel.ts	(revision b27328e33a2a175bc1260e44860f4a4f45f1dea0)
+++ b/js/bloch-sphere/model/BlochSphereModel.ts	(date 1736807688691)
@@ -34,7 +34,7 @@
 
 class BlochSphereModel implements TModel {
 
-  public readonly showMagneticFieldProperty: BooleanProperty;
+  public readonly magneticFieldEnabledProperty: BooleanProperty;
 
   // Bloch Spheres shown in the screen
   public readonly preparationBlochSphere: ComplexBlochSphere;
@@ -75,8 +75,8 @@
 
   public constructor( providedOptions: QuantumMeasurementModelOptions ) {
 
-    this.showMagneticFieldProperty = new BooleanProperty( false, {
-      tandem: providedOptions.tandem.createTandem( 'showMagneticFieldProperty' ),
+    this.magneticFieldEnabledProperty = new BooleanProperty( false, {
+      tandem: providedOptions.tandem.createTandem( 'magneticFieldEnabledProperty' ),
       phetioFeatured: true
     } );
 
@@ -192,7 +192,7 @@
 
     // Set the precession rate of the Bloch sphere based on the magnetic field strength and the selected scene.
     Multilink.multilink(
-      [ this.magneticFieldStrengthProperty, this.showMagneticFieldProperty ],
+      [ this.magneticFieldStrengthProperty, this.magneticFieldEnabledProperty ],
       ( magneticFieldStrength, showMagneticField ) => {
         this.singleMeasurementBlochSphere.rotatingSpeedProperty.value = showMagneticField ?
                                                                         magneticFieldStrength :
@@ -267,7 +267,7 @@
       'The model should be prepared for measurement prior to calling this method.'
     );
 
-    if ( this.showMagneticFieldProperty.value ) {
+    if ( this.magneticFieldEnabledProperty.value ) {
 
       // Transition to the state where the model is waiting to take a measurement.
       this.measurementStateProperty.value = 'timingObservation';
@@ -316,7 +316,7 @@
   public reset(): void {
     this.resetCounts();
     this.preparationBlochSphere.reset();
-    this.showMagneticFieldProperty.reset();
+    this.magneticFieldEnabledProperty.reset();
     this.measurementStateProperty.reset();
     this.magneticFieldStrengthProperty.reset();
     this.measurementBasisProperty.reset();
Index: js/bloch-sphere/view/SystemUnderTestNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/bloch-sphere/view/SystemUnderTestNode.ts b/js/bloch-sphere/view/SystemUnderTestNode.ts
new file mode 100644
--- /dev/null	(date 1736807688693)
+++ b/js/bloch-sphere/view/SystemUnderTestNode.ts	(date 1736807688693)
@@ -0,0 +1,108 @@
+// Copyright 2024, University of Colorado Boulder
+
+/**
+ *
+ *
+ * @author John Blanco, PhET Interactive Simulations
+ */
+
+import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
+import NumberProperty from '../../../../axon/js/NumberProperty.js';
+import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
+import Dimension2 from '../../../../dot/js/Dimension2.js';
+import { combineOptions } from '../../../../phet-core/js/optionize.js';
+import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
+import ShadedSphereNode from '../../../../scenery-phet/js/ShadedSphereNode.js';
+import { Color, GridBox, Node, NodeOptions, Rectangle, Text, VBox } from '../../../../scenery/js/imports.js';
+import quantumMeasurement from '../../quantumMeasurement.js';
+import MagneticFieldNode from './MagneticFieldNode.js';
+
+const ATOM_RADIUS = 18; // in view coordinates
+const ATOM_NODE_OPTIONS = {
+  mainColor: Color.RED,
+  highlightColor: Color.RED.colorUtilsBrighter( 0.7 )
+};
+const LABEL_FONT = new PhetFont( 18 );
+
+class SystemUnderTestNode extends Node {
+
+  /**
+   * @param size - the size of the node in view coordinates
+   * @param magneticFieldEnabledProperty - whether the magnetic field is enabled
+   * @param magneticFieldStrengthProperty - the property that indicates the magnetic field strength
+   * @param isSingleMeasurementModeProperty - whether the system is in single measurement mode
+   * @param providedOptions - options for the node, mostly used for positioning if at all
+   */
+  public constructor( size: Dimension2,
+                      magneticFieldEnabledProperty: TReadOnlyProperty<boolean>,
+                      magneticFieldStrengthProperty: NumberProperty,
+                      isSingleMeasurementModeProperty: TReadOnlyProperty<boolean>,
+                      providedOptions?: NodeOptions ) {
+
+    const rect = new Rectangle( 0, 0, size.width, size.height, {
+      stroke: Color.BLACK,
+      fill: new Color( 235, 255, 235 ),
+      cornerRadius: 8
+    } );
+
+    const magneticFieldNode = new MagneticFieldNode( magneticFieldStrengthProperty, {
+      center: rect.center,
+      opacity: 0.25,
+      visibleProperty: magneticFieldEnabledProperty
+    } );
+
+    const singleSphericalAtomNode = new ShadedSphereNode( ATOM_RADIUS, ATOM_NODE_OPTIONS );
+
+    const labeledSingleAtomNode = new VBox( {
+      children: [
+        singleSphericalAtomNode,
+        new Text( 'Atom', { font: LABEL_FONT } )
+      ],
+      center: rect.center,
+      visibleProperty: isSingleMeasurementModeProperty
+    } );
+
+    // Create the set of atoms for the multiple measurement mode.  The layout here is quite specific and will need to
+    // be adjusted if the number of atoms changes.
+    const atomNodesForMultipleMode: Array<Array<Node | null>> = [];
+    const numColumns = 3;
+    const numRows = 4;
+    for ( let row = 0; row < numRows; row++ ) {
+      for ( let column = 0; column < numColumns; column++ ) {
+        if ( !atomNodesForMultipleMode[ row ] ) {
+          atomNodesForMultipleMode[ row ] = [];
+        }
+        if ( row <= 2 || column === 1 ) {
+          atomNodesForMultipleMode[ row ][ column ] = new ShadedSphereNode( ATOM_RADIUS, ATOM_NODE_OPTIONS );
+        }
+        else {
+          atomNodesForMultipleMode[ row ][ column ] = null;
+        }
+      }
+    }
+    const multipleSphericalAtomNode = new GridBox( {
+      rows: atomNodesForMultipleMode,
+      spacing: 10,
+      center: rect.center
+    } );
+
+    const labeledMultiAtomNode = new VBox( {
+      children: [
+        multipleSphericalAtomNode,
+        new Text( 'Atoms', { font: LABEL_FONT } )
+      ],
+      center: rect.center,
+      visibleProperty: DerivedProperty.valueEqualsConstant( isSingleMeasurementModeProperty, false )
+    } );
+
+    const options = combineOptions<NodeOptions>( {
+      children: [ rect, magneticFieldNode, labeledSingleAtomNode, labeledMultiAtomNode ]
+    }, providedOptions );
+
+    super( options );
+  }
+}
+
+quantumMeasurement.register( 'SystemUnderTestNode', SystemUnderTestNode );
+
+export default SystemUnderTestNode;
\ No newline at end of file
Index: js/bloch-sphere/view/MagneticFieldNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/bloch-sphere/view/MagneticFieldNode.ts b/js/bloch-sphere/view/MagneticFieldNode.ts
--- a/js/bloch-sphere/view/MagneticFieldNode.ts	(revision b27328e33a2a175bc1260e44860f4a4f45f1dea0)
+++ b/js/bloch-sphere/view/MagneticFieldNode.ts	(date 1736807688693)
@@ -6,32 +6,39 @@
  */
 
 import NumberProperty from '../../../../axon/js/NumberProperty.js';
+import Dimension2 from '../../../../dot/js/Dimension2.js';
+import { combineOptions } from '../../../../phet-core/js/optionize.js';
 import { Node, NodeOptions } from '../../../../scenery/js/imports.js';
 import quantumMeasurement from '../../quantumMeasurement.js';
 import MagneticFieldArrowNode from './MagneticFieldArrowNode.js';
 
+const SIZE = new Dimension2( 150, 170 );
+
 export default class MagneticFieldNode extends Node {
 
-  public constructor( magneticFieldStrength: NumberProperty, providedOptions: NodeOptions ) {
+  public constructor( magneticFieldStrength: NumberProperty, providedOptions?: NodeOptions ) {
 
-    super( providedOptions );
-
-    const columns = 8;
-    const rows = 7;
-    const separationX = 300 / columns;
-    const separationY = 300 / rows;
+    const columns = 5;
+    const rows = 5;
+    const separationX = SIZE.width / columns;
+    const separationY = SIZE.height / rows;
+    const arrowNodes: MagneticFieldArrowNode[] = [];
 
     for ( let i = 0; i < columns; i++ ) {
       for ( let j = 0; j < rows; j++ ) {
-        const magneticFieldArrowNode = new MagneticFieldArrowNode( magneticFieldStrength, 30 );
-        magneticFieldStrength.link( strength => {
+        const magneticFieldArrowNode = new MagneticFieldArrowNode( magneticFieldStrength, 20 );
+        magneticFieldStrength.link( () => {
           magneticFieldArrowNode.centerX = i * separationX;
           magneticFieldArrowNode.centerY = j * separationY;
         } );
 
-        this.addChild( magneticFieldArrowNode );
+        arrowNodes.push( magneticFieldArrowNode );
       }
     }
+
+    const options = combineOptions<NodeOptions>( { children: arrowNodes }, providedOptions );
+
+    super( options );
   }
 }
 
Index: js/bloch-sphere/view/MagneticFieldControl.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/bloch-sphere/view/MagneticFieldControl.ts b/js/bloch-sphere/view/MagneticFieldControl.ts
--- a/js/bloch-sphere/view/MagneticFieldControl.ts	(revision b27328e33a2a175bc1260e44860f4a4f45f1dea0)
+++ b/js/bloch-sphere/view/MagneticFieldControl.ts	(date 1736807688692)
@@ -37,7 +37,7 @@
       fill: QuantumMeasurementColors.controlPanelFillColorProperty,
       stroke: null,
       xMargin: 10,
-      yMargin: 10
+      yMargin: 20
     }, providedOptions );
 
     const magneticFieldIndicator = new Node( {
Index: js/bloch-sphere/view/BlochSphereMeasurementArea.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/bloch-sphere/view/BlochSphereMeasurementArea.ts b/js/bloch-sphere/view/BlochSphereMeasurementArea.ts
--- a/js/bloch-sphere/view/BlochSphereMeasurementArea.ts	(revision b27328e33a2a175bc1260e44860f4a4f45f1dea0)
+++ b/js/bloch-sphere/view/BlochSphereMeasurementArea.ts	(date 1736807688691)
@@ -1,8 +1,8 @@
 // Copyright 2025, University of Colorado Boulder
 
 /**
- * BlochSphereMeasurementArea is the node that contains the measurement area for the Bloch Sphere screen. It contains the
- * UI elements for controlling magnetic field and basis of measurements...
+ * BlochSphereMeasurementArea is the node that contains the measurement area for the Bloch Sphere screen. It contains
+ * the UI elements for controlling various aspects of the measurement that is to be performed.
  *
  * @author Agustín Vallejo
  * @author John Blanco (PhET Interactive Simulations)
@@ -10,13 +10,15 @@
 
 import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
 import DerivedStringProperty from '../../../../axon/js/DerivedStringProperty.js';
+import Dimension2 from '../../../../dot/js/Dimension2.js';
 import optionize, { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
 import WithRequired from '../../../../phet-core/js/types/WithRequired.js';
 import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
-import { Node, NodeOptions, RichText, RichTextOptions, Text, VBox } from '../../../../scenery/js/imports.js';
+import { HBox, Node, NodeOptions, RichText, RichTextOptions, Text, VBox } from '../../../../scenery/js/imports.js';
 import AquaRadioButtonGroup from '../../../../sun/js/AquaRadioButtonGroup.js';
 import RectangularRadioButtonGroup from '../../../../sun/js/buttons/RectangularRadioButtonGroup.js';
 import TextPushButton from '../../../../sun/js/buttons/TextPushButton.js';
+import Checkbox from '../../../../sun/js/Checkbox.js';
 import Panel from '../../../../sun/js/Panel.js';
 import QuantumMeasurementColors from '../../common/QuantumMeasurementColors.js';
 import QuantumMeasurementConstants from '../../common/QuantumMeasurementConstants.js';
@@ -28,8 +30,8 @@
 import { MeasurementBasis } from '../model/MeasurementBasis.js';
 import BlochSphereNumericalEquationNode from './BlochSphereNumericalEquationNode.js';
 import MagneticFieldControl from './MagneticFieldControl.js';
-import MagneticFieldNode from './MagneticFieldNode.js';
 import MeasurementTimerControl from './MeasurementTimerControl.js';
+import SystemUnderTestNode from './SystemUnderTestNode.js';
 
 type SelfOptions = EmptySelfOptions;
 
@@ -40,12 +42,7 @@
   public constructor( model: BlochSphereModel, providedOptions: BlochSphereMeasurementAreaOptions ) {
 
     const equationNode = new BlochSphereNumericalEquationNode( model, {
-      tandem: providedOptions.tandem.createTandem( 'equationNode' ),
-      centerY: -50
-    } );
-
-    const magneticFieldNode = new MagneticFieldNode( model.magneticFieldStrengthProperty, {
-      visibleProperty: model.showMagneticFieldProperty
+      tandem: providedOptions.tandem.createTandem( 'equationNode' )
     } );
 
     const singleMeasurementBlochSphereNode = new BlochSphereNode( model.singleMeasurementBlochSphere, {
@@ -53,7 +50,8 @@
       drawTitle: false,
       drawKets: false,
       drawAngleIndicators: true,
-      center: magneticFieldNode.center,
+      top: equationNode.bottom + 50,
+      left: equationNode.left,
       visibleProperty: model.isSingleMeasurementModeProperty
     } );
 
@@ -74,7 +72,8 @@
       } );
       multipleMeasurementBlochSpheresNode.addChild( blochSphereNode );
     } );
-    multipleMeasurementBlochSpheresNode.center = magneticFieldNode.center.plusXY( 0, 30 );
+    multipleMeasurementBlochSpheresNode.centerX = singleMeasurementBlochSphereNode.centerX;
+    multipleMeasurementBlochSpheresNode.top = 70;
 
     const spinUpLabelStringProperty = new DerivedStringProperty(
       [ model.measurementBasisProperty ],
@@ -113,6 +112,7 @@
       font: new PhetFont( 18 ),
       fill: 'black'
     };
+
     // Create and add the radio buttons that select the chart type view in the nuclideChartAccordionBox.
     const basisRadioButtonGroupTandem = providedOptions.tandem.createTandem( 'basisRadioButtonGroup' );
 
@@ -142,7 +142,7 @@
 
     const measurementTimerControl = new MeasurementTimerControl( model.timeToMeasurementProperty, model.measurementTimeProperty, {
       tandem: providedOptions.tandem.createTandem( 'measurementTimerControl' ),
-      visibleProperty: model.showMagneticFieldProperty
+      visibleProperty: model.magneticFieldEnabledProperty
     } );
 
     const measurementControlPanel = new Panel( new VBox( {
@@ -191,7 +191,7 @@
 
     const measurementControls = new VBox( {
       left: singleMeasurementBlochSphereNode.right + 20,
-      top: magneticFieldNode.top - 50,
+      top: equationNode.bottom + 10,
       spacing: 10,
       children: [
         measurementResultHistogram,
@@ -200,21 +200,49 @@
       ]
     } );
 
+    const magneticFieldCheckbox = new Checkbox(
+      model.magneticFieldEnabledProperty,
+      // TODO: This text should be localized, see https://github.com/phetsims/quantum-measurement/issues/54
+      new Text( 'Enable Magnetic Field', { font: new PhetFont( { size: 16 } ) } ),
+      {
+        spacing: 10,
+        centerX: multipleMeasurementBlochSpheresNode.centerX,
+        bottom: QuantumMeasurementConstants.LAYOUT_BOUNDS.bottom - 55,
+        tandem: providedOptions.tandem.createTandem( 'magneticFieldCheckbox' )
+      }
+    );
+
     const magneticFieldControl = new MagneticFieldControl( model.magneticFieldStrengthProperty, {
+      visibleProperty: model.magneticFieldEnabledProperty,
+      tandem: providedOptions.tandem.createTandem( 'magneticFieldControl' )
+    } );
+
+    const systemUnderTestNode = new SystemUnderTestNode(
+      new Dimension2( 150, 180 ),
+      model.magneticFieldEnabledProperty,
+      model.magneticFieldStrengthProperty,
+      model.isSingleMeasurementModeProperty
+    );
+
+    const magneticFieldAndStrengthControl = new HBox( {
+      children: [ magneticFieldControl, systemUnderTestNode ],
+      spacing: 12,
       centerX: singleMeasurementBlochSphereNode.centerX,
-      top: magneticFieldNode.bottom + 10,
-      visibleProperty: model.showMagneticFieldProperty,
-      tandem: providedOptions.tandem.createTandem( 'magneticFieldControl' )
+      bottom: magneticFieldCheckbox.top - 20
+    } );
+
+    magneticFieldAndStrengthControl.localBoundsProperty.link( () => {
+      magneticFieldAndStrengthControl.centerX = singleMeasurementBlochSphereNode.centerX;
     } );
 
     const options = optionize<BlochSphereMeasurementAreaOptions, SelfOptions, NodeOptions>()( {
       children: [
         equationNode,
-        magneticFieldNode,
         singleMeasurementBlochSphereNode,
         multipleMeasurementBlochSpheresNode,
         measurementControls,
-        magneticFieldControl
+        magneticFieldAndStrengthControl,
+        magneticFieldCheckbox
       ]
     }, providedOptions );
 
Index: js/bloch-sphere/view/BlochSphereScreenView.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/bloch-sphere/view/BlochSphereScreenView.ts b/js/bloch-sphere/view/BlochSphereScreenView.ts
--- a/js/bloch-sphere/view/BlochSphereScreenView.ts	(revision b27328e33a2a175bc1260e44860f4a4f45f1dea0)
+++ b/js/bloch-sphere/view/BlochSphereScreenView.ts	(date 1736807688692)
@@ -7,16 +7,14 @@
  */
 
 import BlochSphereModel from 'model/BlochSphereModel.js';
-import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
-import { Color, Line, Text } from '../../../../scenery/js/imports.js';
-import Checkbox from '../../../../sun/js/Checkbox.js';
+import { Color, Line } from '../../../../scenery/js/imports.js';
 import Tandem from '../../../../tandem/js/Tandem.js';
 import QuantumMeasurementScreenView from '../../common/view/QuantumMeasurementScreenView.js';
 import quantumMeasurement from '../../quantumMeasurement.js';
 import BlochSphereMeasurementArea from './BlochSphereMeasurementArea.js';
 import BlochSpherePreparationArea from './BlochSpherePreparationArea.js';
 
-export default class BlochSphereScreenView extends QuantumMeasurementScreenView {
+class BlochSphereScreenView extends QuantumMeasurementScreenView {
 
   private readonly model: BlochSphereModel;
 
@@ -42,28 +40,15 @@
     } );
     this.addChild( dividingLine );
 
-    const showMagneticFieldCheckbox = new Checkbox(
-      model.showMagneticFieldProperty,
-      new Text( 'Show Magnetic Field', { font: new PhetFont( { size: 16 } ) } ),
-      {
-        tandem: tandem.createTandem( 'showMagneticFieldCheckbox' ),
-        spacing: 10,
-        centerX: this.layoutBounds.centerX + 150,
-        top: this.layoutBounds.top + 20
-      } );
-    this.addChild( showMagneticFieldCheckbox );
-
-
     const measurementArea = new BlochSphereMeasurementArea( model, {
       tandem: tandem.createTandem( 'measurementArea' ),
       left: dividingLineX + 20,
-      top: showMagneticFieldCheckbox.bottom + 20
+      top: this.layoutBounds.top + 40
     } );
     this.addChild( measurementArea );
 
     this.pdomPlayAreaNode.pdomOrder = [
       preparationArea,
-      showMagneticFieldCheckbox,
       measurementArea
     ];
 
@@ -78,4 +63,6 @@
   }
 }
 
-quantumMeasurement.register( 'BlochSphereScreenView', BlochSphereScreenView );
\ No newline at end of file
+quantumMeasurement.register( 'BlochSphereScreenView', BlochSphereScreenView );
+
+export default BlochSphereScreenView;
\ No newline at end of file

@jbphet
Copy link
Collaborator

jbphet commented Jan 15, 2025

The code from the previous comment has been integrated after review by the design team.

The basic model and view have been implemented and I think it's time to close this issue and start creating and tracking more targeted ones.

@jbphet jbphet closed this as completed Jan 15, 2025
@phet-dev phet-dev reopened this Jan 16, 2025
@phet-dev
Copy link
Contributor

Reopening because there is a TODO marked for this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants