From c712c42525b7ce7981b8d8461c2452501c0adb77 Mon Sep 17 00:00:00 2001 From: Sam Reid Date: Wed, 10 May 2023 16:14:06 -0600 Subject: [PATCH] Factor out logic that determines which soccer player node is displayed, see https://github.com/phetsims/center-and-variability/issues/153 --- js/common/model/CAVSceneModel.ts | 3 +- js/common/view/CAVScreenView.ts | 14 +++++- js/common/view/SceneView.ts | 46 +++----------------- js/common/view/SoccerPlayerNode.ts | 21 +++++---- js/variability/view/VariabilityScreenView.ts | 26 +++++++++++ 5 files changed, 59 insertions(+), 51 deletions(-) diff --git a/js/common/model/CAVSceneModel.ts b/js/common/model/CAVSceneModel.ts index 6f2b85b0..11594802 100644 --- a/js/common/model/CAVSceneModel.ts +++ b/js/common/model/CAVSceneModel.ts @@ -63,8 +63,7 @@ export default class CAVSceneModel extends PhetioObject implements TModel { public readonly iqrValueProperty: TReadOnlyProperty; public readonly madValueProperty: Property; - - public isVisibleProperty: Property = new BooleanProperty( true ); + public readonly isVisibleProperty: Property = new BooleanProperty( true ); // Signify whenever any object's value or position changes public readonly objectChangedEmitter: TEmitter<[ SoccerBall ]> = new Emitter<[ SoccerBall ]>( { diff --git a/js/common/view/CAVScreenView.ts b/js/common/view/CAVScreenView.ts index b75e27fe..279887ac 100644 --- a/js/common/view/CAVScreenView.ts +++ b/js/common/view/CAVScreenView.ts @@ -30,6 +30,9 @@ import CAVModel from '../model/CAVModel.js'; import SceneView from './SceneView.js'; import KickButtonGroup from './KickButtonGroup.js'; import DynamicProperty from '../../../../axon/js/DynamicProperty.js'; +import SoccerPlayerNode, { SoccerPlayerImageSet } from './SoccerPlayerNode.js'; +import SoccerPlayer from '../model/SoccerPlayer.js'; +import CAVSceneModel from '../model/CAVSceneModel.js'; type SelfOptions = { questionBarOptions: QuestionBarOptions; @@ -86,7 +89,12 @@ export default class CAVScreenView extends ScreenView { this.addChild( this.frontObjectLayer ); // TODO: Scene names, see https://github.com/phetsims/center-and-variability/issues/184 - model.sceneModels.map( ( sceneModel, index ) => new SceneView( model, sceneModel, this.backObjectLayer, this.frontObjectLayer, + model.sceneModels.map( ( sceneModel, index ) => new SceneView( + model, + sceneModel, + this.backObjectLayer, + this.frontObjectLayer, + ( soccerPlayer, sceneModel ) => this.getSoccerPlayerImageSet( soccerPlayer, sceneModel ), modelViewTransform, () => this.accordionBox, { tandem: options.tandem.createTandem( 'sceneView' + index ) } ) ); @@ -195,6 +203,10 @@ export default class CAVScreenView extends ScreenView { } ) ); } + public getSoccerPlayerImageSet( soccerPlayer: SoccerPlayer, sceneModel: CAVSceneModel ): SoccerPlayerImageSet { + return SoccerPlayerNode.MULTI_GROUP[ soccerPlayer.initialPlaceInLine ]; + } + /** * The MedianPredictionNode is shared in the Median screen and MeanAndMedianScreen, so factored out here. */ diff --git a/js/common/view/SceneView.ts b/js/common/view/SceneView.ts index bcec722b..72aff3f9 100644 --- a/js/common/view/SceneView.ts +++ b/js/common/view/SceneView.ts @@ -5,7 +5,7 @@ import SoccerBallNode from './SoccerBallNode.js'; import { AnimationMode } from '../model/AnimationMode.js'; import CAVObjectType from '../model/CAVObjectType.js'; import CAVSceneModel from '../model/CAVSceneModel.js'; -import SoccerPlayerNode from './SoccerPlayerNode.js'; +import SoccerPlayerNode, { SoccerPlayerImageSet } from './SoccerPlayerNode.js'; import BooleanProperty from '../../../../axon/js/BooleanProperty.js'; import Tandem from '../../../../tandem/js/Tandem.js'; import CAVModel from '../model/CAVModel.js'; @@ -14,13 +14,10 @@ import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransfo import DragIndicatorArrowNode from './DragIndicatorArrowNode.js'; import PlayAreaMedianIndicatorNode from './PlayAreaMedianIndicatorNode.js'; import AccordionBox from '../../../../sun/js/AccordionBox.js'; -import InfoDialog from '../../variability/view/InfoDialog.js'; -import VariabilitySceneModel from '../../variability/model/VariabilitySceneModel.js'; -import VariabilityModel from '../../variability/model/VariabilityModel.js'; -import Multilink from '../../../../axon/js/Multilink.js'; import SoccerBall from '../model/SoccerBall.js'; import { Shape } from '../../../../kite/js/imports.js'; import Bounds2 from '../../../../dot/js/Bounds2.js'; +import SoccerPlayer from '../model/SoccerPlayer.js'; /** * Renders view elements for a CAVSceneModel. Note that to satisfy the correct z-ordering, elements @@ -35,6 +32,7 @@ export default class SceneView { sceneModel: CAVSceneModel, backObjectLayer: Node, frontObjectLayer: Node, + getSoccerPlayerImageSet: ( soccerPlayer: SoccerPlayer, sceneModel: CAVSceneModel ) => SoccerPlayerImageSet, modelViewTransform: ModelViewTransform2, getAccordionBox: () => AccordionBox | null, options: { tandem: Tandem } ) { @@ -222,44 +220,14 @@ export default class SceneView { sceneModel.objectChangedEmitter.addListener( this.updateMedianNode ); sceneModel.isVisibleProperty.link( this.updateMedianNode ); - // TODO: See https://github.com/phetsims/center-and-variability/issues/153 - const varModel = model as VariabilityModel; - const varSceneModel = sceneModel as VariabilitySceneModel; - - // TODO: Do we want this information in the model? Do we want one player that re-kicks, or a list of clones? https://github.com/phetsims/center-and-variability/issues/154 - const isVariabilityModel = !!varModel.isInfoShowingProperty; - let sceneIndex = 0; - if ( isVariabilityModel ) { - sceneIndex = varModel.variabilitySceneModels.indexOf( varSceneModel ); - } - - const soccerPlayerNodes = sceneModel.soccerPlayers.map( soccerPlayer => { - return new SoccerPlayerNode( + const soccerPlayerNodes = sceneModel.soccerPlayers.map( soccerPlayer => + new SoccerPlayerNode( soccerPlayer, - isVariabilityModel ? sceneIndex : null, + getSoccerPlayerImageSet( soccerPlayer, sceneModel ), modelViewTransform, - sceneModel.isVisibleProperty - ); - } ); + sceneModel.isVisibleProperty ) ); soccerPlayerNodes.forEach( soccerPlayerNode => frontObjectLayer.addChild( soccerPlayerNode ) ); - - if ( varModel && varModel.isInfoShowingProperty && varModel.selectedSceneModelProperty && varSceneModel ) { - const infoDialog = new InfoDialog( varModel, varSceneModel, { - tandem: options.tandem.createTandem( 'infoDialog' ) - } ); - - Multilink.multilink( [ varModel.isInfoShowingProperty, varSceneModel.isVisibleProperty ], - ( isInfoShowing, isVisible ) => { - if ( isInfoShowing && isVisible ) { - infoDialog.show(); - } - else { - infoDialog.hide(); - } - } ); - } - model.isShowingPlayAreaMedianProperty.link( this.updateMedianNode ); } } diff --git a/js/common/view/SoccerPlayerNode.ts b/js/common/view/SoccerPlayerNode.ts index fd272308..83e7b166 100644 --- a/js/common/view/SoccerPlayerNode.ts +++ b/js/common/view/SoccerPlayerNode.ts @@ -163,29 +163,29 @@ const variabilityPlayerGroups = [ { kicking: variabilityPlayer04Kicking_png } ]; -const playerGroups = [ ...standardKickerGroup, ...standardKickerGroup ]; +export type SoccerPlayerImageSet = { + standing: HTMLImageElement; + poisedToKick: HTMLImageElement; + kicking: HTMLImageElement; +}; const SCALE = 0.155; export default class SoccerPlayerNode extends Node { public readonly soccerPlayer: SoccerPlayer; - public constructor( soccerPlayer: SoccerPlayer, sceneIndex: number | null, modelViewTransform: ModelViewTransform2, isSceneVisibleProperty: TReadOnlyProperty, providedOptions?: SoccerPlayerNodeOptions ) { + public constructor( soccerPlayer: SoccerPlayer, playerImageSet: SoccerPlayerImageSet, modelViewTransform: ModelViewTransform2, isSceneVisibleProperty: TReadOnlyProperty, providedOptions?: SoccerPlayerNodeOptions ) { super(); this.soccerPlayer = soccerPlayer; - const imageNumber = soccerPlayer.initialPlaceInLine; - - const playerGroup = sceneIndex === null ? playerGroups[ imageNumber ] : variabilityPlayerGroups[ sceneIndex ]; - - const standingNode = new Image( playerGroup.standing ); + const standingNode = new Image( playerImageSet.standing ); this.addChild( standingNode ); - const poisedToKickNode = new Image( playerGroup.poisedToKick ); + const poisedToKickNode = new Image( playerImageSet.poisedToKick ); this.addChild( poisedToKickNode ); - const kickingNode = new Image( playerGroup.kicking ); + const kickingNode = new Image( playerImageSet.kicking ); this.addChild( kickingNode ); this.setScaleMagnitude( SCALE ); @@ -217,6 +217,9 @@ export default class SoccerPlayerNode extends Node { this.mutate( options ); } + + public static readonly MULTI_GROUP = [ ...standardKickerGroup, ...standardKickerGroup ]; + public static readonly VARIABILITY_GROUP = variabilityPlayerGroups; } centerAndVariability.register( 'SoccerPlayerNode', SoccerPlayerNode ); \ No newline at end of file diff --git a/js/variability/view/VariabilityScreenView.ts b/js/variability/view/VariabilityScreenView.ts index 7fc7abb4..6c686ad7 100644 --- a/js/variability/view/VariabilityScreenView.ts +++ b/js/variability/view/VariabilityScreenView.ts @@ -23,6 +23,11 @@ import BottomRepresentationCheckboxGroup from '../../common/view/BottomRepresent import CAVConstants from '../../common/CAVConstants.js'; import VerticalCheckboxGroup from '../../../../sun/js/VerticalCheckboxGroup.js'; import MaxKicksControlNode from '../../common/view/MaxKicksControlNode.js'; +import SoccerPlayer from '../../common/model/SoccerPlayer.js'; +import SoccerPlayerNode, { SoccerPlayerImageSet } from '../../common/view/SoccerPlayerNode.js'; +import CAVSceneModel from '../../common/model/CAVSceneModel.js'; +import InfoDialog from './InfoDialog.js'; +import Multilink from '../../../../axon/js/Multilink.js'; type SelfOptions = EmptySelfOptions; type VariabilityScreenViewOptions = SelfOptions & StrictOmit; @@ -85,6 +90,27 @@ export default class VariabilityScreenView extends CAVScreenView { } ) ] } ) ); + + model.variabilitySceneModels.forEach( ( sceneModel, index ) => { + const infoDialog = new InfoDialog( model, sceneModel, { + tandem: options.tandem.createTandem( 'scene' + index ).createTandem( 'infoDialog' ) + } ); + + Multilink.multilink( [ model.isInfoShowingProperty, sceneModel.isVisibleProperty ], + ( isInfoShowing, isVisible ) => { + if ( isInfoShowing && isVisible ) { + infoDialog.show(); + } + else { + infoDialog.hide(); + } + } ); + } ); + } + + public override getSoccerPlayerImageSet( soccerPlayer: SoccerPlayer, sceneModel: CAVSceneModel ): SoccerPlayerImageSet { + const index = this.model.sceneModels.indexOf( sceneModel ); + return SoccerPlayerNode.VARIABILITY_GROUP[ index ]; } }