diff --git a/forces-and-motion-basics_en.html b/forces-and-motion-basics_en.html
index a3ed0372..65a115e3 100644
--- a/forces-and-motion-basics_en.html
+++ b/forces-and-motion-basics_en.html
@@ -46,6 +46,11 @@
"phet-io",
"adapted-from-phet"
],
+ "simFeatures": {
+ "supportedRegionsAndCultures": [
+ "usa"
+ ]
+ },
"simulation": true,
"phet-io": {
"validation": false,
diff --git a/js/forces-and-motion-basics-main.js b/js/forces-and-motion-basics-main.js
index b38ceb25..137312d9 100644
--- a/js/forces-and-motion-basics-main.js
+++ b/js/forces-and-motion-basics-main.js
@@ -14,10 +14,12 @@ import { Image } from '../../scenery/js/imports.js';
import Tandem from '../../tandem/js/Tandem.js';
import accelerationIcon_png from '../images/accelerationIcon_png.js';
import frictionIcon_png from '../images/frictionIcon_png.js';
-import motionIcon_png from '../images/motionIcon_png.js';
import tugIcon_png from '../images/tugIcon_png.js';
import ForcesAndMotionBasicsStrings from './ForcesAndMotionBasicsStrings.js';
import MotionScreen from './motion/MotionScreen.js';
+import PreferencesModelSingleton from './motion/PreferencesModelSingleton.js';
+import MassPlayerImages from './motion/view/MassPlayerImages.js';
+import MotionScreenIcon from './motion/view/MotionScreenIcon.js';
import NetForceModel from './netforce/model/NetForceModel.js';
import NetForceScreenView from './netforce/view/NetForceScreenView.js';
@@ -27,6 +29,7 @@ const forcesAndMotionBasicsTitleStringProperty = ForcesAndMotionBasicsStrings[ '
const tandem = Tandem.ROOT;
const simOptions = {
+ preferencesModel: PreferencesModelSingleton,
credits: {
leadDesign: 'Ariel Paul, Noah Podolefsky',
graphicArts: 'Mariah Hermsmeyer, Sharon Siman-Tov',
@@ -63,9 +66,7 @@ simLauncher.launch( () => {
const motionScreen = new MotionScreen( 'motion', motionScreenTandem, {
name: ForcesAndMotionBasicsStrings.motionStringProperty,
- homeScreenIcon: new ScreenIcon( new Image( motionIcon_png, {
- tandem: motionScreenTandem.createTandem( 'icon' )
- } ), screenIconOptions )
+ homeScreenIcon: new MotionScreenIcon( MassPlayerImages.MASS_PLAYER_PORTRAYALS, screenIconOptions, motionScreenTandem )
} );
const frictionScreen = new MotionScreen( 'friction', frictionScreenTandem, {
diff --git a/js/motion/PreferencesModelSingleton.js b/js/motion/PreferencesModelSingleton.js
new file mode 100644
index 00000000..e42cfaac
--- /dev/null
+++ b/js/motion/PreferencesModelSingleton.js
@@ -0,0 +1,20 @@
+// Copyright 2024, University of Colorado Boulder
+
+/**
+ * Preferences model as a singleton, so it can be accessed by the HumanTypeEnum.
+ *
+ * @author Luisa Vargas
+ */
+
+import PreferencesModel from '../../../joist/js/preferences/PreferencesModel.js';
+import forcesAndMotionBasics from '../forcesAndMotionBasics.js';
+import MassPlayerImages from './view/MassPlayerImages.js';
+
+const PreferencesModelSingleton = new PreferencesModel( {
+ localizationOptions: {
+ portrayals: MassPlayerImages.MASS_PLAYER_PORTRAYALS
+ }
+} );
+
+forcesAndMotionBasics.register( 'PreferencesModelSingleton', PreferencesModelSingleton );
+export default PreferencesModelSingleton;
\ No newline at end of file
diff --git a/js/motion/model/HumanTypeEnum.js b/js/motion/model/HumanTypeEnum.js
new file mode 100644
index 00000000..87c873d8
--- /dev/null
+++ b/js/motion/model/HumanTypeEnum.js
@@ -0,0 +1,67 @@
+// Copyright 2024, University of Colorado Boulder
+
+/**
+ * HumanTypeEnum identifies the human object type and sets the holding, standing, or sitting image for the human.
+ *
+ * @author Luisa Vargas
+ */
+
+import MappedProperty from '../../../../axon/js/MappedProperty.js';
+import EnumerationValue from '../../../../phet-core/js/EnumerationValue.js';
+import Enumeration from '../../../../phet-core/js/Enumeration.js';
+import forcesAndMotionBasics from '../../forcesAndMotionBasics.js';
+import PreferencesModelSingleton from '../PreferencesModelSingleton.js';
+
+class HumanTypeEnum extends EnumerationValue {
+
+ static GIRL = new HumanTypeEnum( 'girl' );
+ static MAN = new HumanTypeEnum( 'man' );
+
+ static enumeration = new Enumeration( HumanTypeEnum );
+
+ /**
+ * @param {string} humanType
+ */
+ constructor( humanType ) {
+ super();
+
+ this.holdingImageProperty = new MappedProperty( PreferencesModelSingleton.localizationModel.regionAndCulturePortrayalProperty, {
+ map: portrayal => {
+ if ( humanType === 'girl' ) {
+ return portrayal.girlHolding;
+ }
+ else {
+ assert && assert( humanType === 'man', 'Human type must be girl, or man, but it is ', humanType );
+ return portrayal.manHolding;
+ }
+ }
+ } );
+
+ this.standingImageProperty = new MappedProperty( PreferencesModelSingleton.localizationModel.regionAndCulturePortrayalProperty, {
+ map: portrayal => {
+ if ( humanType === 'girl' ) {
+ return portrayal.girlStanding;
+ }
+ else {
+ assert && assert( humanType === 'man', 'Human type must be girl, or man, but it is ', humanType );
+ return portrayal.manStanding;
+ }
+ }
+ } );
+
+ this.sittingImageProperty = new MappedProperty( PreferencesModelSingleton.localizationModel.regionAndCulturePortrayalProperty, {
+ map: portrayal => {
+ if ( humanType === 'girl' ) {
+ return portrayal.girlSitting;
+ }
+ else {
+ assert && assert( humanType === 'man', 'Human type must be girl, or man, but it is ', humanType );
+ return portrayal.manSitting;
+ }
+ }
+ } );
+ }
+}
+
+forcesAndMotionBasics.register( 'HumanTypeEnum', HumanTypeEnum );
+export default HumanTypeEnum;
\ No newline at end of file
diff --git a/js/motion/model/Item.js b/js/motion/model/Item.js
index 9bda2521..750631ab 100644
--- a/js/motion/model/Item.js
+++ b/js/motion/model/Item.js
@@ -19,6 +19,7 @@ import IOType from '../../../../tandem/js/types/IOType.js';
import ObjectLiteralIO from '../../../../tandem/js/types/ObjectLiteralIO.js';
import ReferenceIO from '../../../../tandem/js/types/ReferenceIO.js';
import forcesAndMotionBasics from '../../forcesAndMotionBasics.js';
+import HumanTypeEnum from './HumanTypeEnum.js';
class Item extends PhetioObject {
@@ -26,7 +27,7 @@ class Item extends PhetioObject {
* Constructor for Item
*
* @param {MotionModel || NetForceModel} context - model context in which this item exists
- * @param {string} name - string describing this type of item
+ * @param {string | HumanTypeEnum } name - string describing this type of item, or HumanTypeEnum of this human item
* @param {Tandem} tandem
* @param {image} image - image from the 'image!' plugin, representing the item
* @param {number} mass - model mass of the item
@@ -47,20 +48,36 @@ class Item extends PhetioObject {
phetioState: false
} );
- this.name = name;
+ this.name = typeof name === 'string' ? name : name.name.toLowerCase();
+
+ // Set the standing, sitting, and holding image properties if item is human
+ let standingImageProperty;
+ let sittingImageProperty;
+ let holdingImageProperty;
+ if ( name === HumanTypeEnum.GIRL ) {
+ standingImageProperty = HumanTypeEnum.GIRL.standingImageProperty;
+ sittingImageProperty = HumanTypeEnum.GIRL.sittingImageProperty;
+ holdingImageProperty = HumanTypeEnum.GIRL.holdingImageProperty;
+ }
+ else if ( name === HumanTypeEnum.MAN ) {
+ standingImageProperty = HumanTypeEnum.MAN.standingImageProperty;
+ sittingImageProperty = HumanTypeEnum.MAN.sittingImageProperty;
+ holdingImageProperty = HumanTypeEnum.MAN.holdingImageProperty;
+ }
//Non-observable properties
this.initialX = x;
this.initialY = y;
- this.image = image;
this.mass = mass;
this.pusherInset = pusherInset;
- this.sittingImageNode = sittingImage;
- this.holdingImage = holdingImage;
this.context = context;
this.mystery = mystery;
this.homeScale = homeScale || 1.0;
+ this.imageProperty = typeof name === 'string' ? new Property( image ) : standingImageProperty;
+ this.sittingImageProperty = typeof name === 'string' ? new Property( sittingImage ) : sittingImageProperty;
+ this.holdingImageProperty = typeof name === 'string' ? new Property( holdingImage ) : holdingImageProperty;
+
// @public - the position of the item
this.positionProperty = new Vector2Property( new Vector2( x, y ), {
tandem: tandem.createTandem( 'positionProperty' )
@@ -112,17 +129,12 @@ class Item extends PhetioObject {
this.context.directionProperty.link( direction => {
//only change directions if on the board, and always choose one of left/right, and only for people
- if ( this.onBoardProperty.get() && direction !== 'none' && sittingImage ) {
+ if ( this.onBoardProperty.get() && direction !== 'none' && ( name === HumanTypeEnum.GIRL || name === HumanTypeEnum.MAN ) ) {
this.directionProperty.set( direction );
}
} );
}
- //For unknown reasons, the trash can is not centered when drawn, so we make up for it with a workaround here
- get centeringOffset() {
- return this.image === 'trashCan.png' ? 5 : 0;
- }
-
// @public - Return true if the arms should be up (for a human)
armsUp() {
return this.context.draggingItems().length > 0 || this.context.isItemStackedAbove( this );
diff --git a/js/motion/model/MotionModel.js b/js/motion/model/MotionModel.js
index 81683c86..03ebcf69 100644
--- a/js/motion/model/MotionModel.js
+++ b/js/motion/model/MotionModel.js
@@ -21,15 +21,10 @@ import crate_png from '../../../images/crate_png.js';
import fridge_png from '../../../images/fridge_png.js';
import mysteryObject01_png from '../../../images/mysteryObject01_png.js';
import waterBucket_png from '../../../images/waterBucket_png.js';
-import girlHolding_png from '../../../mipmaps/girlHolding_png.js';
-import girlSitting_png from '../../../mipmaps/girlSitting_png.js';
-import girlStanding_png from '../../../mipmaps/girlStanding_png.js';
-import manHolding_png from '../../../mipmaps/manHolding_png.js';
-import manSitting_png from '../../../mipmaps/manSitting_png.js';
-import manStanding_png from '../../../mipmaps/manStanding_png.js';
import trashCan_png from '../../../mipmaps/trashCan_png.js';
import forcesAndMotionBasics from '../../forcesAndMotionBasics.js';
import MotionConstants from '../MotionConstants.js';
+import HumanTypeEnum from './HumanTypeEnum.js';
import Item from './Item.js';
class MotionModel {
@@ -235,8 +230,8 @@ class MotionModel {
const fridge = new Item( this, 'fridge', tandem.createTandem( 'fridge' ), fridge_png, 200, leftmostItemXLeft, 437, 0.8, 1.1, 4 );
const crate1 = new Item( this, 'crate1', tandem.createTandem( 'crate1' ), crate_png, 50, leftmostItemXLeft + crate1Spacing, 507, 0.5 );
const crate2 = new Item( this, 'crate2', tandem.createTandem( 'crate2' ), crate_png, 50, leftmostItemXLeft + crate1Spacing + crate2Spacing, 507, 0.5 );
- const girl = new Item( this, 'girl', tandem.createTandem( 'girl' ), girlStanding_png, 40, leftmostItemXRight, 465, 0.6, 1.0, 4.2, girlSitting_png, girlHolding_png[ 1 ].img );
- const man = new Item( this, 'man', tandem.createTandem( 'man' ), manStanding_png, 80, leftmostItemXRight + manSpacing, 428, 0.6, 0.92, 5, manSitting_png, manHolding_png );
+ const girl = new Item( this, HumanTypeEnum.GIRL, tandem.createTandem( 'girl' ), undefined, 40, leftmostItemXRight, 465, 0.6, 1.0, 4.2 );
+ const man = new Item( this, HumanTypeEnum.MAN, tandem.createTandem( 'man' ), undefined, 80, leftmostItemXRight + manSpacing, 428, 0.6, 0.92, 5 );
const trashCan = new Item( this, 'trash', tandem.createTandem( 'trash' ), trashCan_png, 100, leftmostItemXRight + manSpacing + trashSpacing, 496, 0.7, 1.0, 5 );
const mysteryBox = new Item( this, 'mystery', tandem.createTandem( 'mystery' ), mysteryObject01_png, 50, leftmostItemXRight + manSpacing + trashSpacing + mysterySpacing, 513, 0.3, 1.0, undefined, undefined, undefined, true );
const bucket = new Item( this, 'bucket', tandem.createTandem( 'bucket' ), waterBucket_png, 100, leftmostItemXRight + manSpacing + trashSpacing + mysterySpacing + bucketSpacing, 547 + -35, 0.68, 1.0, 8 );
@@ -312,7 +307,7 @@ class MotionModel {
for ( let i = 0; i < this.stackObservableArray.length; i++ ) {
const size = this.view.getSize( this.stackObservableArray.get( i ) );
sumHeight += size.height;
- this.stackObservableArray.get( i ).animateTo( this.view.layoutBounds.width / 2 - size.width / 2 + this.stackObservableArray.get( i ).centeringOffset, ( this.skateboard ? 334 : 360 ) - sumHeight, 'stack' );//TODO: factor out this code for layout, which is duplicated in MotionTab.topOfStack https://github.com/phetsims/tasks/issues/1129
+ this.stackObservableArray.get( i ).animateTo( this.view.layoutBounds.width / 2 - size.width / 2, ( this.skateboard ? 334 : 360 ) - sumHeight, 'stack' );//TODO: factor out this code for layout, which is duplicated in MotionTab.topOfStack https://github.com/phetsims/tasks/issues/1129
}
}
diff --git a/js/motion/view/ItemNode.js b/js/motion/view/ItemNode.js
index 00cead94..8fe72d70 100644
--- a/js/motion/view/ItemNode.js
+++ b/js/motion/view/ItemNode.js
@@ -14,6 +14,7 @@ import { Image, Node, Rectangle, SimpleDragHandler, Text } from '../../../../sce
import Tandem from '../../../../tandem/js/Tandem.js';
import forcesAndMotionBasics from '../../forcesAndMotionBasics.js';
import ForcesAndMotionBasicsStrings from '../../ForcesAndMotionBasicsStrings.js';
+import PreferencesModelSingleton from '../PreferencesModelSingleton.js';
const pattern0MassUnitsKilogramsString = ForcesAndMotionBasicsStrings.pattern[ '0massUnitsKilograms' ];
@@ -27,13 +28,14 @@ class ItemNode extends Node {
* @param {MotionModel} model the entire model for the containing screen
* @param {MotionScreenView} motionView the entire view for the containing screen
* @param {Item} item the corresponding to this ItemNode
- * @param {Image} normalImage the phet.scenery.Image to show for this node
- * @param {Image} sittingImage optional image for when the person is sitting down
- * @param {Image} holdingImage optional image for when the person is holding an object
+ * @param {Property} normalImageProperty property for the phet.scenery.Image to show for this node
+ * @param {Property} sittingImageProperty property fot optional sitting image for when the person is sitting down
+ * @param {Property} holdingImageProperty property for optional holding image for when the person is holding an object
* @param {Property} showMassesProperty property for whether the mass value should be shown
* @param {Rectangle} itemToolbox - The toolbox that contains this item
+ * @param {Tandem} tandem
*/
- constructor( model, motionView, item, normalImage, sittingImage, holdingImage, showMassesProperty, itemToolbox, tandem ) {
+ constructor( model, motionView, item, normalImageProperty, sittingImageProperty, holdingImageProperty, showMassesProperty, itemToolbox, tandem ) {
super( {
cursor: 'pointer',
@@ -50,24 +52,24 @@ class ItemNode extends Node {
this.translate( item.positionProperty.get() );
//Create the node for the main graphic
- const normalImageNode = new Image( normalImage, { tandem: tandem.createTandem( 'normalImageNode' ) } );
+ const normalImageNode = new Image( normalImageProperty.value, { tandem: tandem.createTandem( 'normalImageNode' ) } );
this.normalImageNode = normalImageNode;
// keep track of the sitting image to track its width for the pusher
// @public (read-only)
- this.sittingImageNode = new Image( sittingImage, { tandem: tandem.createTandem( 'sittingImageNode' ) } );
+ this.sittingImageNode = new Image( sittingImageProperty.value, { tandem: tandem.createTandem( 'sittingImageNode' ) } );
//When the model changes, update the image position as well as which image is shown
const updateImage = () => {
// var centerX = normalImageNode.centerX;
- if ( ( typeof holdingImage !== 'undefined' ) && ( item.armsUp() && item.onBoardProperty.get() ) ) {
- normalImageNode.image = holdingImage;
+ if ( ( typeof holdingImageProperty.value !== 'undefined' ) && ( item.armsUp() && item.onBoardProperty.get() ) ) {
+ normalImageNode.image = holdingImageProperty.value;
}
- else if ( item.onBoardProperty.get() && typeof sittingImage !== 'undefined' ) {
- normalImageNode.image = sittingImage;
+ else if ( item.onBoardProperty.get() && typeof sittingImageProperty.value !== 'undefined' ) {
+ normalImageNode.image = sittingImageProperty.value;
}
else {
- normalImageNode.image = normalImage;
+ normalImageNode.image = normalImageProperty.value;
}
if ( this.labelNode ) {
this.updateLabelPosition();
@@ -90,7 +92,7 @@ class ItemNode extends Node {
const moveToStack = () => {
item.onBoardProperty.set( true );
const imageWidth = item.getCurrentScale() * normalImageNode.width;
- item.animateTo( motionView.layoutBounds.width / 2 - imageWidth / 2 + item.centeringOffset, motionView.topOfStack - this.height, 'stack' );
+ item.animateTo( motionView.layoutBounds.width / 2 - imageWidth / 2, motionView.topOfStack - this.height, 'stack' );
model.stackObservableArray.add( item );
if ( model.stackObservableArray.length > 3 ) {
model.spliceStackBottom();
@@ -250,6 +252,8 @@ class ItemNode extends Node {
this.addChild( labelText );
showMassesProperty.link( showMasses => { labelText.visible = showMasses; } );
+
+ PreferencesModelSingleton.localizationModel.regionAndCulturePortrayalProperty.link( updateImage );
}
diff --git a/js/motion/view/MassPlayerImages.js b/js/motion/view/MassPlayerImages.js
new file mode 100644
index 00000000..b6c9931c
--- /dev/null
+++ b/js/motion/view/MassPlayerImages.js
@@ -0,0 +1,19 @@
+// Copyright 2024, University of Colorado Boulder
+
+/**
+ * MassPlayerImages contains an array of character sets, each representing a different region/culture.
+ *
+ * @author Luisa Vargas
+ */
+
+import forcesAndMotionBasics from '../../forcesAndMotionBasics.js';
+import MassPlayerPortrayalUSA from './MassPlayerPortrayalUSA.js';
+
+const MassPlayerImages = {
+ MASS_PLAYER_PORTRAYALS: [
+ MassPlayerPortrayalUSA
+ ]
+};
+
+forcesAndMotionBasics.register( 'MassPlayerImages', MassPlayerImages );
+export default MassPlayerImages;
\ No newline at end of file
diff --git a/js/motion/view/MassPlayerPortrayal.js b/js/motion/view/MassPlayerPortrayal.js
new file mode 100644
index 00000000..d686d9d9
--- /dev/null
+++ b/js/motion/view/MassPlayerPortrayal.js
@@ -0,0 +1,43 @@
+// Copyright 2024, University of Colorado Boulder
+
+/**
+ * The MassPlayerPortrayal defines what is needed for each portrayal in Forces and Motion: Basics.
+ *
+ * @author Luisa Vargas
+ *
+ */
+
+import RegionAndCulturePortrayal from '../../../../joist/js/preferences/RegionAndCulturePortrayal.js';
+import forcesAndMotionBasics from '../../forcesAndMotionBasics.js';
+
+export default class MassPlayerPortrayal extends RegionAndCulturePortrayal {
+
+ /**
+ * @param label { LocalizedStringProperty }
+ * @param girlHolding { HTMLImageElement }
+ * @param girlSitting { HTMLImageElement }
+ * @param girlStanding { HTMLImageElement }
+ * @param manHolding { HTMLImageElement }
+ * @param manSitting { HTMLImageElement }
+ * @param manStanding { HTMLImageElement }
+ * @param screenIcon { HTMLImageElement }
+ * @param queryParameterValue { string }
+ */
+ constructor( label,
+ girlHolding, girlSitting, girlStanding,
+ manHolding, manSitting, manStanding,
+ screenIcon, queryParameterValue ) {
+
+ super( label, queryParameterValue, {} );
+
+ this.girlHolding = girlHolding;
+ this.girlSitting = girlSitting;
+ this.girlStanding = girlStanding;
+ this.manHolding = manHolding;
+ this.manStanding = manStanding;
+ this.manSitting = manSitting;
+ this.screenIcon = screenIcon;
+ }
+}
+
+forcesAndMotionBasics.register( 'MassPlayerPortrayal', MassPlayerPortrayal );
\ No newline at end of file
diff --git a/js/motion/view/MassPlayerPortrayalUSA.js b/js/motion/view/MassPlayerPortrayalUSA.js
new file mode 100644
index 00000000..b30a15c5
--- /dev/null
+++ b/js/motion/view/MassPlayerPortrayalUSA.js
@@ -0,0 +1,33 @@
+// Copyright 2024, University of Colorado Boulder
+
+/**
+ * This file instantiates the USA region and culture portrayals.
+ *
+ * @author Luisa Vargas
+ *
+ */
+
+import JoistStrings from '../../../../joist/js/JoistStrings.js';
+import { USA_REGION_AND_CULTURE_ID } from '../../../../joist/js/preferences/RegionAndCulturePortrayal.js';
+import motionIcon_png from '../../../images/motionIcon_png.js';
+import girlHolding_png from '../../../mipmaps/girlHolding_png.js';
+import girlSitting_png from '../../../mipmaps/girlSitting_png.js';
+import girlStanding_png from '../../../mipmaps/girlStanding_png.js';
+import manHolding_png from '../../../mipmaps/manHolding_png.js';
+import manSitting_png from '../../../mipmaps/manSitting_png.js';
+import manStanding_png from '../../../mipmaps/manStanding_png.js';
+import MassPlayerPortrayal from './MassPlayerPortrayal.js';
+
+const MassPlayerPortrayalUSA = new MassPlayerPortrayal(
+ JoistStrings.preferences.tabs.localization.regionAndCulture.portrayalSets.unitedStatesOfAmericaStringProperty,
+ girlHolding_png[ 1 ].img,
+ girlSitting_png,
+ girlStanding_png,
+ manHolding_png,
+ manSitting_png,
+ manStanding_png,
+ motionIcon_png,
+ USA_REGION_AND_CULTURE_ID
+);
+
+export default MassPlayerPortrayalUSA;
\ No newline at end of file
diff --git a/js/motion/view/MotionScreenIcon.js b/js/motion/view/MotionScreenIcon.js
new file mode 100644
index 00000000..a22151ef
--- /dev/null
+++ b/js/motion/view/MotionScreenIcon.js
@@ -0,0 +1,42 @@
+// Copyright 2024, University of Colorado Boulder
+
+/**
+ * MotionScreenIcon places all the screenIcons of different region and culture representations into a single Node.
+ * Each icon's visibility is controlled by the regionAndCulturePortrayalProperty.
+ *
+ * @author Luisa Vargas
+ *
+ */
+
+import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
+import ScreenIcon from '../../../../joist/js/ScreenIcon.js';
+import { Node, Image } from '../../../../scenery/js/imports.js';
+import forcesAndMotionBasics from '../../forcesAndMotionBasics.js';
+import PreferencesModelSingleton from '../PreferencesModelSingleton.js';
+
+export default class MotionScreenIcon extends ScreenIcon {
+
+ /**
+ * @param { Array } portrayals
+ * @param options
+ * @param { Tandem } tandem
+ */
+ constructor( portrayals, options, tandem ) {
+
+ const motionScreenImages = portrayals.map( massPlayerPortrayal => {
+ return new Image( massPlayerPortrayal.screenIcon, {
+ visibleProperty: new DerivedProperty( [ PreferencesModelSingleton.localizationModel.regionAndCulturePortrayalProperty ], portrayal => {
+ return portrayal === massPlayerPortrayal;
+ } ),
+ tandem: tandem.createTandem( 'icon' )
+ } );
+ } );
+
+ const screenIconNode = new Node( {
+ children: motionScreenImages
+ } );
+ super( screenIconNode, options );
+ }
+}
+
+forcesAndMotionBasics.register( 'MotionScreenIcon', MotionScreenIcon );
\ No newline at end of file
diff --git a/js/motion/view/MotionScreenView.js b/js/motion/view/MotionScreenView.js
index bf0da5c1..e53466c7 100644
--- a/js/motion/view/MotionScreenView.js
+++ b/js/motion/view/MotionScreenView.js
@@ -311,10 +311,12 @@ class MotionScreenView extends ScreenView {
const toolboxNode = itemSide === 'left' ? leftItemToolboxNode : rightItemToolboxNode;
const itemLayer = itemSide === 'left' ? leftItemLayer : rightItemLayer;
const Constructor = item.bucket ? WaterBucketNode : ItemNode;
+ const sittingImageProperty = item.sittingImageProperty.value ? item.sittingImageProperty : item.imageProperty;
+ const holdingImageProperty = item.holdingImageProperty.value ? item.holdingImageProperty : item.imageProperty;
const itemNode = new Constructor( model, this, item,
- item.image,
- item.sittingImageNode || item.image,
- item.holdingImage || item.image,
+ item.imageProperty,
+ sittingImageProperty,
+ holdingImageProperty,
model.showMassesProperty,
toolboxNode,
tandem.createTandem( item.name ) );
diff --git a/js/motion/view/WaterBucketNode.js b/js/motion/view/WaterBucketNode.js
index 6f73e1c2..02b0d48b 100644
--- a/js/motion/view/WaterBucketNode.js
+++ b/js/motion/view/WaterBucketNode.js
@@ -25,15 +25,15 @@ class WaterBucketNode extends ItemNode {
* @param {MotionModel} model the model for the entire 'motion', 'friction' or 'acceleration' screen
* @param {MotionScreenView} motionView the view for the entire 'motion', 'friction' or 'acceleration' screen
* @param {Item} item the model for the item itself
- * @param {Image} image image to be shown when in the toolbox or being dragged
- * @param {Image} imageSitting image to be shown if it is a sitting person
- * @param {Image} imageHolding image to be shown if it is a sitting person holding their arms in the air
+ * @param {Image} imageProperty imageProperty holds image to be shown when in the toolbox or being dragged
+ * @param {Image} imageSittingProperty imageProperty holds image to be shown if it is a sitting person
+ * @param {Image} imageHoldingProperty imageProperty holds image to be shown if it is a sitting person holding their arms in the air
* @param {Property} showMassesProperty boolean property of whether the masses should be shown
* @param {Rectangle} toolboxNode parent toolbox for the WaterBucketNode
* @param {Tandem} tandem
*/
- constructor( model, motionView, item, image, imageSitting, imageHolding, showMassesProperty, toolboxNode, tandem ) {
- super( model, motionView, item, image, imageSitting, imageHolding, showMassesProperty, toolboxNode, tandem );
+ constructor( model, motionView, item, imageProperty, imageSittingProperty, imageHoldingProperty, showMassesProperty, toolboxNode, tandem ) {
+ super( model, motionView, item, imageProperty, imageSittingProperty, imageHoldingProperty, showMassesProperty, toolboxNode, tandem );
this.item = item;
const waterPathNode = new Path( Shape.lineSegment( new Vector2( 0, 0 ), new Vector2( 0, 18 ) ), {
stroke: 'black',
@@ -50,7 +50,7 @@ class WaterBucketNode extends ItemNode {
//Metrics based on original image size of 98 pixels wide.
const padX = 4.5;
const padY = 9;
- const s = image.width / 98.0;
+ const s = imageProperty.value.width / 98.0;
const leftLineX = x => linear( 0, 1, ( 1 + padX ) * s, ( 10 + padX ) * s, x );
const leftLineY = x => linear( 0, 1, ( 9 - padY ) * s, ( 102 - padY ) * s, x );
diff --git a/package.json b/package.json
index 0d353869..cbe3fe66 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,11 @@
"phet-io",
"adapted-from-phet"
],
+ "simFeatures": {
+ "supportedRegionsAndCultures": [
+ "usa"
+ ]
+ },
"simulation": true,
"phet-io": {
"validation": false,