Skip to content

Commit

Permalink
Big improvements to angle indicators, see #54
Browse files Browse the repository at this point in the history
  • Loading branch information
AgustinVallejo committed Dec 25, 2024
1 parent 71d9757 commit 187b753
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
3 changes: 2 additions & 1 deletion js/bloch-sphere/model/BlochSphereModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export default class BlochSphereModel implements TModel {

public readonly blochSphere: ComplexBlochSphere;

// Coefficients of the state equation |psi> = upCoefficient |up> + downCoefficient * exp( i * |down>
// Coefficients of the state equation
// |psi> = upCoefficient |up> + downCoefficient * exp( i * phase * PI ) |down>
public readonly upCoefficientProperty: NumberProperty;
public readonly downCoefficientProperty: NumberProperty;
public readonly phaseFactorProperty: NumberProperty;
Expand Down
43 changes: 37 additions & 6 deletions js/common/view/BlochSphereNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Bounds2 from '../../../../dot/js/Bounds2.js';
import Utils from '../../../../dot/js/Utils.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import { Shape } from '../../../../kite/js/imports.js';
import optionize from '../../../../phet-core/js/optionize.js';
import optionize, { combineOptions } from '../../../../phet-core/js/optionize.js';
import WithRequired from '../../../../phet-core/js/types/WithRequired.js';
import ArrowNode from '../../../../scenery-phet/js/ArrowNode.js';
import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
Expand All @@ -24,6 +24,7 @@ import quantumMeasurement from '../../quantumMeasurement.js';
import QuantumMeasurementStrings from '../../QuantumMeasurementStrings.js';
import AbstractBlochSphere from '../model/AbstractBlochSphere.js';
import QuantumMeasurementConstants from '../QuantumMeasurementConstants.js';
import DashedArrowNode, { DashedArrowNodeOptions } from './DashedArrowNode.js';

type SelfOptions = {
drawKets?: boolean;
Expand Down Expand Up @@ -157,36 +158,64 @@ export default class BlochSphereNode extends Node {
visibleProperty: stateVectorVisibleProperty
} );

const ANGLE_INDICATOR_PATH_OPTIONS = {
const angleIndicatorPathOptions = {
stroke: 'gray',
lineWidth: 1,
lineDash: [ 2, 2 ],
visible: options.drawAngleIndicators
};
const polarAngleIndicator = new Path( null, ANGLE_INDICATOR_PATH_OPTIONS );
const azimutalAngleIndicator = new Path( null, ANGLE_INDICATOR_PATH_OPTIONS );
const polarAngleIndicator = new Path( null, angleIndicatorPathOptions );
const azimutalAngleIndicator = new Path( null, angleIndicatorPathOptions );
const xyProjectionVector = new DashedArrowNode( 0, 0, 0, -sphereRadius, combineOptions<DashedArrowNodeOptions>( {
headWidth: 4,
headHeight: 4,
tailWidth: 1,
fill: 'gray'
}, angleIndicatorPathOptions ) );
const zProjectionLine = new Path( null, angleIndicatorPathOptions );

Multilink.multilink(
[
blochSphere.azimutalAngleProperty,
blochSphere.polarAngleProperty,
xAxisOffsetAngleProperty
], ( azimutalAngle, polarAngle, xAxisOffsetAngle ) => {

const tip = pointOnTheSphere( azimutalAngle, polarAngle, xAxisOffsetAngleProperty.value );
stateVector.setTip( tip.x, tip.y );

// Full opacity when pointing towards camera or at the poles
stateVector.opacity = Math.sin( polarAngle ) < 1e-5 || Math.cos( azimutalAngle + xAxisOffsetAngle ) > 0 ? 1 : 0.4;

// If polar angle allows it, show the projection vector on the xy plane and the z projection line
if ( Math.sin( polarAngle ) < 1e-5 ) {
xyProjectionVector.visible = false;
zProjectionLine.visible = false;
}
else {
const xyProjectionTip = pointOnTheEquator( azimutalAngle, xAxisOffsetAngle ).times( Math.sin( polarAngle ) );

xyProjectionVector.visible = true;
xyProjectionVector.setTip( xyProjectionTip.x, xyProjectionTip.y );

zProjectionLine.visible = true;
zProjectionLine.shape = new Shape().moveTo( tip.x, tip.y ).lineTo( tip.x, xyProjectionTip.y );
}

// Polar angle indicator will rotate with azimuth
const rotationFactor = Math.sin( azimutalAngle + xAxisOffsetAngle );

polarAngleIndicator.shape = new Shape().ellipticalArc(
// Center of the ellipse
0,
0,
// Ellipse dimensions
tip.x / 2, // Ellipse width goes to half the state vector
equatorSemiMajorAxis / 2 * rotationFactor, // Ellipse width goes to half the state vector
equatorSemiMajorAxis / 2,
0,
// Begins at -PI/2; Ends at the polar angle with an adjustement due to the sphere perspective
-Math.PI / 2,
polarAngle - Math.PI / 2 + equatorInclinationAngle * Math.sin( polarAngle ) * Math.cos( azimutalAngle + xAxisOffsetAngle ),
rotationFactor !== 0 ? Math.atan2( tip.y, tip.x / rotationFactor ) : -Math.PI / 2,
false
);

Expand Down Expand Up @@ -218,6 +247,8 @@ export default class BlochSphereNode extends Node {
zAxisLabel,
polarAngleIndicator,
azimutalAngleIndicator,
xyProjectionVector,
zProjectionLine,
stateVector
];

Expand Down
6 changes: 5 additions & 1 deletion js/common/view/DashedArrowNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import quantumMeasurement from '../../quantumMeasurement.js';

type SelfOptions = EmptySelfOptions;

type DashedArrowNodeOptions = SelfOptions & ArrowNodeOptions;
export type DashedArrowNodeOptions = SelfOptions & ArrowNodeOptions;

export default class DashedArrowNode extends Node {

Expand Down Expand Up @@ -56,6 +56,10 @@ export default class DashedArrowNode extends Node {
this.arrowHead.setTailAndTip( tail.x, tail.y, tipX, tipY );
this.arrowLine.setLine( tailX, tailY, tipX, tipY );
}

public setTip( tipX: number, tipY: number ): void {
this.setTailAndTip( this.arrowLine.x1, this.arrowLine.y1, tipX, tipY );
}
}

quantumMeasurement.register( 'DashedArrowNode', DashedArrowNode );

0 comments on commit 187b753

Please sign in to comment.