Skip to content

Commit

Permalink
factor out OpticalObjectLabelNode, which handles object numbering, #380
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelzoom committed Mar 15, 2022
1 parent f70c5d8 commit 85c8613
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 76 deletions.
58 changes: 19 additions & 39 deletions js/common/view/labels/ArrowObjectSceneLabelsNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,24 @@
import DerivedProperty from '../../../../../axon/js/DerivedProperty.js';
import Vector2 from '../../../../../dot/js/Vector2.js';
import geometricOptics from '../../../geometricOptics.js';
import geometricOpticsStrings from '../../../geometricOpticsStrings.js';
import LabelNode, { LabelNodeOptions } from './LabelNode.js';
import VisibleProperties from '../VisibleProperties.js';
import ModelViewTransform2 from '../../../../../phetcommon/js/view/ModelViewTransform2.js';
import Bounds2 from '../../../../../dot/js/Bounds2.js';
import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js';
import ArrowObjectScene from '../../model/ArrowObjectScene.js';
import IProperty from '../../../../../axon/js/IProperty.js';
import { OpticalImageType } from '../../model/OpticalImageType.js';
import GOSceneLabelsNode, { GOSceneLabelsNodeOptions } from './GOSceneLabelsNode.js';
import ArrowImage from '../../model/ArrowImage.js';
import StringUtils from '../../../../../phetcommon/js/util/StringUtils.js';
import BooleanProperty from '../../../../../axon/js/BooleanProperty.js';
import ArrowObject from '../../model/ArrowObject.js';
import Optic from '../../model/Optic.js';
import Property from '../../../../../axon/js/Property.js';
import OpticalObjectLabelNode, { OpticalObjectLabelNodeOptions } from './OpticalObjectLabelNode.js';
import LabelNode, { LabelNodeOptions } from './LabelNode.js';
import optionize from '../../../../../phet-core/js/optionize.js';
import { OpticalImageType } from '../../model/OpticalImageType.js';
import Property from '../../../../../axon/js/Property.js';
import StringUtils from '../../../../../phetcommon/js/util/StringUtils.js';
import geometricOpticsStrings from '../../../geometricOpticsStrings.js';

type SelfOptions = {
isBasicsVersion: boolean;
Expand Down Expand Up @@ -71,14 +72,14 @@ class ArrowObjectSceneLabelsNode extends GOSceneLabelsNode {
// Image labels ------------------------------------------------------------------------------------

const image2Label = new ArrowImageLabelNode( scene.arrowImage2, scene.optic, zoomTransformProperty,
lightPropagationEnabledProperty, visibleProperties.secondPointVisibleProperty,
visibleProperties.secondPointVisibleProperty, lightPropagationEnabledProperty,
visibleProperties.virtualImageVisibleProperty );
this.addChild( image2Label );

const image1Label = new ArrowImageLabelNode( scene.arrowImage1, scene.optic, zoomTransformProperty,
lightPropagationEnabledProperty, new BooleanProperty( true ), visibleProperties.virtualImageVisibleProperty, {
new BooleanProperty( true ), lightPropagationEnabledProperty, visibleProperties.virtualImageVisibleProperty, {

// Use numbering in the full version of the sim, or in the basics version if Image 2 is visible.
// Use numbering in the full version of the sim, or in the basics version if Image 2 is made visible.
isNumberedProperty: new DerivedProperty( [ image2Label.visibleProperty ],
( image2LabelVisible: boolean ) => ( !providedOptions.isBasicsVersion || image2LabelVisible )
)
Expand All @@ -92,14 +93,8 @@ class ArrowObjectSceneLabelsNode extends GOSceneLabelsNode {
}
}

type ArrowObjectLabelNodeSelfOptions = {
isNumberedProperty?: IReadOnlyProperty<boolean>;
};

type ArrowObjectLabelNodeOptions = ArrowObjectLabelNodeSelfOptions & LabelNodeOptions;

// Label for an arrow object.
class ArrowObjectLabelNode extends LabelNode {
class ArrowObjectLabelNode extends OpticalObjectLabelNode {

/**
* @param arrowObject
Expand All @@ -110,11 +105,7 @@ class ArrowObjectLabelNode extends LabelNode {
constructor( arrowObject: ArrowObject,
optic: Optic,
zoomTransformProperty: IReadOnlyProperty<ModelViewTransform2>,
providedOptions?: ArrowObjectLabelNodeOptions ) {

const options = optionize<ArrowObjectLabelNodeOptions, ArrowObjectLabelNodeSelfOptions, LabelNodeOptions>( {
isNumberedProperty: new BooleanProperty( true )
}, providedOptions );
providedOptions?: OpticalObjectLabelNodeOptions ) {

// If the arrow points up, position the label below the optical axis.
// Otherwise, position the label below the arrow's tip.
Expand All @@ -124,22 +115,7 @@ class ArrowObjectLabelNode extends LabelNode {
( arrowPosition.y > opticPosition.y ) ? new Vector2( arrowPosition.x, opticPosition.y ) : arrowPosition
);

super( '', labelPositionProperty, zoomTransformProperty, options );

options.isNumberedProperty.link( ( isNumbered: boolean ) => {
if ( isNumbered ) {

// Object N
this.setText( StringUtils.fillIn( geometricOpticsStrings.label.objectN, {
objectNumber: arrowObject.opticalObjectNumber
} ) );
}
else {

// Object
this.setText( geometricOpticsStrings.label.object );
}
} );
super( arrowObject.opticalObjectNumber, labelPositionProperty, zoomTransformProperty, providedOptions );
}
}

Expand Down Expand Up @@ -171,9 +147,13 @@ class ArrowImageLabelNode extends LabelNode {

const options = optionize<ArrowImageLabelNodeOptions, ArrowImageLabelNodeSelfOptions, LabelNodeOptions>( {
isNumberedProperty: new BooleanProperty( true ),
visibleProperty: new DerivedProperty(
[ lightPropagationEnabledProperty, arrowObjectVisibleProperty, arrowImage.visibleProperty,
arrowImage.opticalImageTypeProperty, virtualImageVisibleProperty ],
visibleProperty: new DerivedProperty( [
lightPropagationEnabledProperty,
arrowObjectVisibleProperty,
arrowImage.visibleProperty,
arrowImage.opticalImageTypeProperty,
virtualImageVisibleProperty
],
( lightPropagationEnabled: boolean, arrowObjectVisible: boolean, arrowImageVisible: boolean,
opticalImageType: OpticalImageType, virtualImageVisible: boolean ) =>
( lightPropagationEnabled && arrowObjectVisible && arrowImageVisible && ( opticalImageType === 'real' || virtualImageVisible ) )
Expand Down
24 changes: 17 additions & 7 deletions js/common/view/labels/FramedObjectSceneLabelsNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import DerivedProperty from '../../../../../axon/js/DerivedProperty.js';
import geometricOptics from '../../../geometricOptics.js';
import geometricOpticsStrings from '../../../geometricOpticsStrings.js';
import LabelNode from './LabelNode.js';
import VisibleProperties from '../VisibleProperties.js';
import ModelViewTransform2 from '../../../../../phetcommon/js/view/ModelViewTransform2.js';
Expand All @@ -18,6 +17,9 @@ import FramedObjectScene from '../../model/FramedObjectScene.js';
import { OpticalImageType } from '../../model/OpticalImageType.js';
import IProperty from '../../../../../axon/js/IProperty.js';
import GOSceneLabelsNode, { GOSceneLabelsNodeOptions } from './GOSceneLabelsNode.js';
import OpticalObjectLabelNode from './OpticalObjectLabelNode.js';
import BooleanProperty from '../../../../../axon/js/BooleanProperty.js';
import geometricOpticsStrings from '../../../geometricOpticsStrings.js';

class FramedObjectSceneLabelsNode extends GOSceneLabelsNode {

Expand All @@ -38,17 +40,21 @@ class FramedObjectSceneLabelsNode extends GOSceneLabelsNode {

super( scene.optic, visibleProperties, zoomTransformProperty, modelVisibleBoundsProperty, providedOptions );

// Object label ------------------------------------------------------------------------------------
const isNumberedProperty = new BooleanProperty( false, {
validValues: [ false ]
} );

// Object
const objectLabelString = geometricOpticsStrings.label.object;
// Object label ------------------------------------------------------------------------------------

const objectLabelPositionProperty = new DerivedProperty(
[ scene.framedObject.boundsProperty ],
( bounds: Bounds2 ) => bounds.centerTop
);

const objectLabel = new LabelNode( objectLabelString, objectLabelPositionProperty, zoomTransformProperty );
const objectLabel = new OpticalObjectLabelNode( scene.framedObject.opticalObjectNumber,
objectLabelPositionProperty, zoomTransformProperty, {
isNumberedProperty: isNumberedProperty
} );
this.addChild( objectLabel );

// Image label ------------------------------------------------------------------------------------
Expand All @@ -59,8 +65,12 @@ class FramedObjectSceneLabelsNode extends GOSceneLabelsNode {
);

const imageLabel = new LabelNode( '', imageLabelPositionProperty, zoomTransformProperty, {
visibleProperty: new DerivedProperty( [ lightPropagationEnabledProperty, scene.framedImage1.visibleProperty,
scene.framedImage1.opticalImageTypeProperty, visibleProperties.virtualImageVisibleProperty ],
visibleProperty: new DerivedProperty( [
lightPropagationEnabledProperty,
scene.framedImage1.visibleProperty,
scene.framedImage1.opticalImageTypeProperty,
visibleProperties.virtualImageVisibleProperty
],
( lightPropagationEnabled: boolean, imageVisible: boolean, opticalImageType: OpticalImageType, virtualImageVisible: boolean ) =>
( lightPropagationEnabled && imageVisible && ( opticalImageType === 'real' || virtualImageVisible ) )
)
Expand Down
35 changes: 5 additions & 30 deletions js/common/view/labels/LightObjectSceneLabelsNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,15 @@ import DerivedProperty from '../../../../../axon/js/DerivedProperty.js';
import Vector2 from '../../../../../dot/js/Vector2.js';
import geometricOptics from '../../../geometricOptics.js';
import geometricOpticsStrings from '../../../geometricOpticsStrings.js';
import LabelNode, { LabelNodeOptions } from './LabelNode.js';
import LabelNode from './LabelNode.js';
import VisibleProperties from '../VisibleProperties.js';
import ModelViewTransform2 from '../../../../../phetcommon/js/view/ModelViewTransform2.js';
import Bounds2 from '../../../../../dot/js/Bounds2.js';
import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js';
import LightObjectScene from '../../model/LightObjectScene.js';
import GOSceneLabelsNode, { GOSceneLabelsNodeOptions } from './GOSceneLabelsNode.js';
import StringUtils from '../../../../../phetcommon/js/util/StringUtils.js';
import LightObject from '../../model/LightObject.js';
import BooleanProperty from '../../../../../axon/js/BooleanProperty.js';
import optionize from '../../../../../phet-core/js/optionize.js';
import OpticalObjectLabelNode, { OpticalObjectLabelNodeOptions } from './OpticalObjectLabelNode.js';

type SelfOptions = {
isBasicsVersion: boolean;
Expand Down Expand Up @@ -73,14 +71,10 @@ class LightObjectSceneLabelsNode extends GOSceneLabelsNode {
}
}

type LightObjectLabelNodeSelfOptions = {
isNumberedProperty?: IReadOnlyProperty<boolean>;
};

type LightObjectLabelNodeOptions = LightObjectLabelNodeSelfOptions & LabelNodeOptions;
type LightObjectLabelNodeOptions = OpticalObjectLabelNodeOptions;

// Label for a light object.
class LightObjectLabelNode extends LabelNode {
class LightObjectLabelNode extends OpticalObjectLabelNode {

/**
* @param lightObject
Expand All @@ -91,31 +85,12 @@ class LightObjectLabelNode extends LabelNode {
zoomTransformProperty: IReadOnlyProperty<ModelViewTransform2>,
providedOptions?: LightObjectLabelNodeOptions ) {

const options = optionize<LightObjectLabelNodeOptions, LightObjectLabelNodeSelfOptions, LabelNodeOptions>( {
isNumberedProperty: new BooleanProperty( true )
}, providedOptions );

// Position the label below the light, slightly to the left of center (determined empirically)
const labelPositionProperty = new DerivedProperty( [ lightObject.boundsProperty ],
( bounds: Bounds2 ) => new Vector2( bounds.centerX - 15, bounds.top )
);

super( '', labelPositionProperty, zoomTransformProperty, options );

options.isNumberedProperty.link( ( isNumbered: boolean ) => {
if ( isNumbered ) {

// Object N
this.setText( StringUtils.fillIn( geometricOpticsStrings.label.objectN, {
objectNumber: lightObject.opticalObjectNumber
} ) );
}
else {

// Object
this.setText( geometricOpticsStrings.label.object );
}
} );
super( lightObject.opticalObjectNumber, labelPositionProperty, zoomTransformProperty, providedOptions );
}
}

Expand Down
65 changes: 65 additions & 0 deletions js/common/view/labels/OpticalObjectLabelNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2021-2022, University of Colorado Boulder

/**
* OpticalObjectLabelNode is the base class of labeling optical objects.
* It can label them as simply 'Object', or it can number them like 'Object 1'.
* Numbering is dynamic to support PhET-iO.
*
* @author Chris Malley (PixelZoom, Inc.)
*/
import LabelNode, { LabelNodeOptions } from './LabelNode.js';
import geometricOpticsStrings from '../../../geometricOpticsStrings.js';
import geometricOptics from '../../../geometricOptics.js';
import StringUtils from '../../../../../phetcommon/js/util/StringUtils.js';
import BooleanProperty from '../../../../../axon/js/BooleanProperty.js';
import optionize from '../../../../../phet-core/js/optionize.js';
import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js';
import Vector2 from '../../../../../dot/js/Vector2.js';
import ModelViewTransform2 from '../../../../../phetcommon/js/view/ModelViewTransform2.js';

type SelfOptions = {

// Whether the object should be numbered, like 'Object 1'
isNumberedProperty?: IReadOnlyProperty<boolean>;
};

export type OpticalObjectLabelNodeOptions = SelfOptions & LabelNodeOptions;

class OpticalObjectLabelNode extends LabelNode {

/**
* @param objectNumber
* @param labelPositionProperty
* @param zoomTransformProperty
* @param providedOptions
*/
constructor( objectNumber: number,
labelPositionProperty: IReadOnlyProperty<Vector2>,
zoomTransformProperty: IReadOnlyProperty<ModelViewTransform2>,
providedOptions?: OpticalObjectLabelNodeOptions ) {

const options = optionize<OpticalObjectLabelNodeOptions, SelfOptions, LabelNodeOptions>( {
isNumberedProperty: new BooleanProperty( true )
}, providedOptions );

super( '', labelPositionProperty, zoomTransformProperty, options );

options.isNumberedProperty.link( ( isNumbered: boolean ) => {
if ( isNumbered ) {

// Object N
this.setText( StringUtils.fillIn( geometricOpticsStrings.label.objectN, {
objectNumber: objectNumber
} ) );
}
else {

// Object
this.setText( geometricOpticsStrings.label.object );
}
} );
}
}

geometricOptics.register( 'OpticalObjectLabelNode', OpticalObjectLabelNode );
export default OpticalObjectLabelNode;

0 comments on commit 85c8613

Please sign in to comment.