diff --git a/js/buoyancy/model/BottleOrBoat.ts b/js/buoyancy/model/BottleOrBoat.ts new file mode 100644 index 00000000..b5b4bc5e --- /dev/null +++ b/js/buoyancy/model/BottleOrBoat.ts @@ -0,0 +1,11 @@ +// Copyright 2024, University of Colorado Boulder + +/** + * BottleOrBoat is a string literal union enumeration that describes whether the sim is showing the bottle or boat scene + * in the Buoyancy - Applications screen. + * + * @author Sam Reid (PhET Interactive Simulations) + */ +export const BottleOrBoatValues = [ 'bottle', 'boat' ] as const; + +export type BottleOrBoat = typeof BottleOrBoatValues[number]; \ No newline at end of file diff --git a/js/buoyancy/model/BuoyancyApplicationsModel.ts b/js/buoyancy/model/BuoyancyApplicationsModel.ts index 31aa2e9e..47209ff0 100644 --- a/js/buoyancy/model/BuoyancyApplicationsModel.ts +++ b/js/buoyancy/model/BuoyancyApplicationsModel.ts @@ -7,12 +7,8 @@ */ import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; -import EnumerationProperty from '../../../../axon/js/EnumerationProperty.js'; -import Property from '../../../../axon/js/Property.js'; import Matrix3 from '../../../../dot/js/Matrix3.js'; import Vector2 from '../../../../dot/js/Vector2.js'; -import Enumeration from '../../../../phet-core/js/Enumeration.js'; -import EnumerationValue from '../../../../phet-core/js/EnumerationValue.js'; import Cube from '../../common/model/Cube.js'; import DensityBuoyancyModel, { DensityBuoyancyModelOptions } from '../../common/model/DensityBuoyancyModel.js'; import Material from '../../common/model/Material.js'; @@ -25,23 +21,14 @@ import Range from '../../../../dot/js/Range.js'; import NumberProperty from '../../../../axon/js/NumberProperty.js'; import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; import BooleanIO from '../../../../tandem/js/types/BooleanIO.js'; - -// constants -// REVIEW: Recommended to use string literal enumeration. We can do so here since it is not encumbered by density API. -export class Scene extends EnumerationValue { - public static readonly BOTTLE = new Scene(); - public static readonly BOAT = new Scene(); - - public static readonly enumeration = new Enumeration( Scene, { - phetioDocumentation: 'Bottle or boat scene' - } ); -} +import { BottleOrBoat, BottleOrBoatValues } from './BottleOrBoat.js'; +import StringUnionProperty from '../../../../axon/js/StringUnionProperty.js'; export type BuoyancyApplicationsModelOptions = DensityBuoyancyModelOptions; export default class BuoyancyApplicationsModel extends DensityBuoyancyModel { - public readonly sceneProperty: Property; + public readonly sceneProperty: StringUnionProperty; public readonly bottle: Bottle; public readonly block: Cube; @@ -58,7 +45,8 @@ export default class BuoyancyApplicationsModel extends DensityBuoyancyModel { usePoolScale: true }, options ) ); - this.sceneProperty = new EnumerationProperty( Scene.BOTTLE, { + this.sceneProperty = new StringUnionProperty( 'bottle', { + validValues: BottleOrBoatValues, tandem: options.tandem.createTandem( 'sceneProperty' ) } ); @@ -113,15 +101,15 @@ export default class BuoyancyApplicationsModel extends DensityBuoyancyModel { // This instance lives for the lifetime of the simulation, so we don't need to remove this listener this.sceneProperty.link( ( scene, previousScene ) => { - this.bottle.internalVisibleProperty.value = scene === Scene.BOTTLE; - this.boat.internalVisibleProperty.value = scene === Scene.BOAT; - this.block.internalVisibleProperty.value = scene === Scene.BOAT; - this.scale2!.internalVisibleProperty.value = scene === Scene.BOTTLE; + this.bottle.internalVisibleProperty.value = scene === 'bottle'; + this.boat.internalVisibleProperty.value = scene === 'boat'; + this.block.internalVisibleProperty.value = scene === 'boat'; + this.scale2!.internalVisibleProperty.value = scene === 'bottle'; // When switching from boat to bottle scene, subtract the scale volume from the pool and vice versa (-1 and 1) // But don't do it when the bottle scene is first loaded (0) - const plusMinusScaleVolume = scene === Scene.BOTTLE ? - previousScene === Scene.BOAT ? -1 : 0 : 1; + const plusMinusScaleVolume = scene === 'bottle' ? + previousScene === 'boat' ? -1 : 0 : 1; this.pool.liquidVolumeProperty.value += plusMinusScaleVolume * this.scale2!.volumeProperty.value; this.pool.liquidVolumeProperty.setInitialValue( this.pool.liquidVolumeProperty.value ); diff --git a/js/buoyancy/view/BuoyancyApplicationsScreenView.ts b/js/buoyancy/view/BuoyancyApplicationsScreenView.ts index 587f58e2..fc7e58d7 100644 --- a/js/buoyancy/view/BuoyancyApplicationsScreenView.ts +++ b/js/buoyancy/view/BuoyancyApplicationsScreenView.ts @@ -30,7 +30,7 @@ import BuoyancyDisplayOptionsPanel from '../../common/view/BuoyancyDisplayOption import MaterialMassVolumeControlNode from '../../common/view/MaterialMassVolumeControlNode.js'; import densityBuoyancyCommon from '../../densityBuoyancyCommon.js'; import DensityBuoyancyCommonStrings from '../../DensityBuoyancyCommonStrings.js'; -import BuoyancyApplicationsModel, { Scene } from '../model/BuoyancyApplicationsModel.js'; +import BuoyancyApplicationsModel from '../model/BuoyancyApplicationsModel.js'; import DensityAccordionBox from './DensityAccordionBox.js'; import arrayRemove from '../../../../phet-core/js/arrayRemove.js'; import BottleView from './BottleView.js'; @@ -77,7 +77,7 @@ export default class BuoyancyApplicationsScreenView extends DensityBuoyancyScree listener: () => { model.resetBoatScene(); }, - visibleProperty: new DerivedProperty( [ model.sceneProperty ], scene => scene === Scene.BOAT ), + visibleProperty: new DerivedProperty( [ model.sceneProperty ], scene => scene === 'boat' ), tandem: tandem.createTandem( 'resetSceneButton' ) } ); this.addChild( resetSceneButton ); @@ -291,8 +291,8 @@ export default class BuoyancyApplicationsScreenView extends DensityBuoyancyScree // This instance lives for the lifetime of the simulation, so we don't need to remove this listener model.sceneProperty.link( scene => { - rightBottleContent.visible = scene === Scene.BOTTLE; - rightBoatContent.visible = scene === Scene.BOAT; + rightBottleContent.visible = scene === 'bottle'; + rightBoatContent.visible = scene === 'boat'; } ); const displayedMysteryMaterials = [ @@ -305,10 +305,10 @@ export default class BuoyancyApplicationsScreenView extends DensityBuoyancyScree model.sceneProperty.link( scene => { - const materials = scene === Scene.BOTTLE ? [ + const materials = scene === 'bottle' ? [ model.bottle.interiorMaterialProperty, model.bottle.materialProperty - ] : scene === Scene.BOAT ? [ + ] : scene === 'boat' ? [ model.block.materialProperty, model.boat.materialProperty ] : []; @@ -316,7 +316,7 @@ export default class BuoyancyApplicationsScreenView extends DensityBuoyancyScree densityAccordionBox.setReadoutItems( materials.map( material => { return { readoutItem: material }; } ) ); - const submergedObjects = scene === Scene.BOTTLE ? + const submergedObjects = scene === 'bottle' ? [ { readoutItem: model.bottle, readoutNameProperty: DensityBuoyancyCommonStrings.bottleStringProperty @@ -347,12 +347,12 @@ export default class BuoyancyApplicationsScreenView extends DensityBuoyancyScree const bottleBoatRadioButtonGroup = new RectangularRadioButtonGroup( model.sceneProperty, [ { - value: Scene.BOTTLE, + value: 'bottle', createNode: () => BuoyancyApplicationsScreenView.getBottleIcon(), tandemName: 'bottleRadioButton' }, { - value: Scene.BOAT, + value: 'boat', createNode: () => BuoyancyApplicationsScreenView.getBoatIcon(), tandemName: 'boatRadioButton' }