diff --git a/js/common/MoleculesAndLightA11yStrings.js b/js/common/MoleculesAndLightA11yStrings.js index 12609dbc..34b28ab3 100644 --- a/js/common/MoleculesAndLightA11yStrings.js +++ b/js/common/MoleculesAndLightA11yStrings.js @@ -62,6 +62,9 @@ define( require => { emissionPhaseDescriptionPatternString: { value: '{{photonTarget}} molecule stops {{excitedRepresentation}} and emits absorbed {{lightSource}} photon {{direction}}.' }, + moleculesOutOfViewPatternString: { + value: '{{firstMolecule}} and {{secondMolecule}} out of view. Return molecule to make more observations.' + }, absorbedString: { value: 'absorbed' }, diff --git a/js/moleculesandlight/view/ObservationWindow.js b/js/moleculesandlight/view/ObservationWindow.js index 6800a495..48a89895 100644 --- a/js/moleculesandlight/view/ObservationWindow.js +++ b/js/moleculesandlight/view/ObservationWindow.js @@ -25,7 +25,6 @@ define( require => { const PhotonNode = require( 'MOLECULES_AND_LIGHT/photon-absorption/view/PhotonNode' ); const PhotonTarget = require( 'MOLECULES_AND_LIGHT/photon-absorption/model/PhotonTarget' ); const platform = require( 'PHET_CORE/platform' ); - const Property = require( 'AXON/Property' ); const Rectangle = require( 'SCENERY/nodes/Rectangle' ); const RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' ); const StringUtils = require( 'PHETCOMMON/util/StringUtils' ); @@ -57,6 +56,7 @@ define( require => { const startsGlowingString = MoleculesAndLightA11yStrings.startsGlowingString.value; const breakApartPhaseDescriptionPatternString = MoleculesAndLightA11yStrings.breakApartPhaseDescriptionPatternString.value; const emissionPhaseDescriptionPatternString = MoleculesAndLightA11yStrings.emissionPhaseDescriptionPatternString.value; + const moleculesOutOfViewPatternString = MoleculesAndLightA11yStrings.moleculesOutOfViewPatternString.value; // constants const PHOTON_EMITTER_WIDTH = 125; @@ -234,14 +234,17 @@ define( require => { } ); this.addChild( descriptionList ); - Property.multilink( - [ photonAbsorptionModel.photonWavelengthProperty, photonAbsorptionModel.photonTargetProperty ], ( photonWavelength, photonTarget ) => { - phaseItem.accessibleName = this.getInitialPhaseDescription( photonAbsorptionModel.emissionFrequencyProperty.get(), photonWavelength, photonTarget ); + photonAbsorptionModel.photonWavelengthProperty.link( photonWavelength => { + + // if there isn't a target molecule, keep the current description which should indicate that a molecule should be + // added back with the "Return Molecule" button + if ( photonAbsorptionModel.targetMolecule ) { + phaseItem.accessibleName = this.getInitialPhaseDescription( photonAbsorptionModel.emissionFrequencyProperty.get(), photonWavelength, photonAbsorptionModel.photonTargetProperty.get() ); } - ); + } ); photonAbsorptionModel.emissionFrequencyProperty.link( ( emissionFrequency, oldFrequency ) => { - if ( emissionFrequency === 0 || oldFrequency === 0 ) { + if ( ( emissionFrequency === 0 || oldFrequency === 0 ) && photonAbsorptionModel.targetMolecule ) { phaseItem.accessibleName = this.getInitialPhaseDescription( emissionFrequency, photonAbsorptionModel.photonWavelengthProperty.get(), photonAbsorptionModel.photonTargetProperty.get() ); } } ); @@ -257,28 +260,31 @@ define( require => { this.moleculeBrokeApart = false; // when the photon target changes, add listeners to the new target molecule that will update the phase description - photonAbsorptionModel.photonTargetProperty.link( photonTarget => { - const newMolecule = photonAbsorptionModel.targetMolecule; + photonAbsorptionModel.activeMolecules.addItemAddedListener( molecule => { - // TODO: IMplement these - newMolecule.currentVibrationRadiansProperty.lazyLink( vibrationRadians => { - this.moleculeVibrating = newMolecule.vibratingProperty.get(); + // new target molecule added, + if ( molecule === photonAbsorptionModel.targetMolecule ) { + phaseItem.accessibleName = this.getInitialPhaseDescription( photonAbsorptionModel.emissionFrequencyProperty.get(), photonAbsorptionModel.photonWavelengthProperty.get(), photonAbsorptionModel.photonTargetProperty.get() ); + } + + molecule.currentVibrationRadiansProperty.lazyLink( vibrationRadians => { + this.moleculeVibrating = molecule.vibratingProperty.get(); if ( this.moleculeVibrating ) { phaseItem.accessibleName = this.getVibrationPhaseDescription( vibrationRadians ); } } ); - newMolecule.rotatingProperty.lazyLink( rotating => { + molecule.rotatingProperty.lazyLink( rotating => { this.moleculeRotating = rotating; - this.moleculeRotatingClockwise = newMolecule.rotationDirectionClockwiseProperty.get(); + this.moleculeRotatingClockwise = molecule.rotationDirectionClockwiseProperty.get(); if ( rotating ) { phaseItem.accessibleName = this.getRotationPhaseDescription(); } } ); - newMolecule.highElectronicEnergyStateProperty.lazyLink( highEnergy => { + molecule.highElectronicEnergyStateProperty.lazyLink( highEnergy => { this.moleculeHighElectronicEnergyState = highEnergy; if ( highEnergy ) { @@ -286,12 +292,34 @@ define( require => { } } ); - newMolecule.brokeApartEmitter.addListener( ( moleculeA, moleculeB ) => { + molecule.brokeApartEmitter.addListener( ( moleculeA, moleculeB ) => { this.moleculeBrokeApart = true; phaseItem.accessibleName = this.getBreakApartPhaseDescription( moleculeA, moleculeB ); + + // new molecules are removed from the observation window, describe that they are no longer in place + const activeMolecules = this.photonAbsorptionModel.activeMolecules; + + // when the constituent molecules are added to the list of active molecules, add a listener that + // will describe their removal once they are removed - only needs to be added once on the + // first addition of a constituent molecule + const addMoleculeRemovalListener = () => { + const describeMoleculesRemoved = ( molecule, observableArray ) => { + if ( !activeMolecules.contains( moleculeA ) && !activeMolecules.contains( moleculeB ) ) { + phaseItem.accessibleName = this.getMoleculesRemovedDescription( moleculeA, moleculeB ); + activeMolecules.removeItemRemovedListener( describeMoleculesRemoved ); + } + }; + + activeMolecules.addItemRemovedListener( describeMoleculesRemoved ); + + // itemRemoved listener has been added, can remove the listener on molecule addition + activeMolecules.removeItemAddedListener( addMoleculeRemovalListener ); + }; + + activeMolecules.addItemAddedListener( addMoleculeRemovalListener ); } ); - newMolecule.photonEmittedEmitter.addListener( photon => { + molecule.photonEmittedEmitter.addListener( photon => { phaseItem.accessibleName = this.getEmissionPhaseDescription( photon ); console.log( phaseItem.accessibleName ); } ); @@ -530,6 +558,16 @@ define( require => { } ); }, + getMoleculesRemovedDescription: function( firstMolecule, secondMolecule ) { + const firstMolecularFormula = MoleculeNameMap.getMolecularFormula( firstMolecule ); + const secondMolecularFormula = MoleculeNameMap.getMolecularFormula( secondMolecule ); + + return StringUtils.fillIn( moleculesOutOfViewPatternString, { + firstMolecule: firstMolecularFormula, + secondMolecule: secondMolecularFormula + } ); + }, + getGeometryDescription: function() {}, getGeometryDefinitionDescription: function( emissionFrequency, photonWavelength, photonTarget ) {} diff --git a/js/photon-absorption/model/PhotonAbsorptionModel.js b/js/photon-absorption/model/PhotonAbsorptionModel.js index 5e528770..919d837a 100644 --- a/js/photon-absorption/model/PhotonAbsorptionModel.js +++ b/js/photon-absorption/model/PhotonAbsorptionModel.js @@ -397,8 +397,8 @@ define( require => { photonTarget === PhotonTarget.SINGLE_CH4_MOLECULE ? new CH4( { tandem: tandem.createTandem( 'CH4' ) } ) : assert && assert( false, 'unhandled photon target.' ); - this.activeMolecules.add( newMolecule ); this.targetMolecule = newMolecule; + this.activeMolecules.add( newMolecule ); // Set the photonGroupTandem so that photons created by the molecule can be registered for PhET-iO newMolecule.photonGroupTandem = this.photonGroupTandem;