-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
On the variability screen, each player sets a different "scene" and remembers its data #164
Comments
We will likely rename CAVModel => CAVSceneModel and pull out appropriate parts to a new CAVModel, which has 1+ CAVSceneModels. |
Clarifying some behavior before we get too far on this:
The only mention I saw in the design doc regarding different scenes for the different t-shirts said this, so it would be good to clarify:
Next question: if the user kicks soccer ball "A" in t-shirt-1, then switches to t-shirt-2, then switches back to t-shirt-1, will the soccer ball "A" still be flying through the air? (It would be paused when on another scene)? Does the clear button clear all the scenes or just the selected scene? |
From today's design meeting:
|
Hey Sam, Cathy, In thinking about PhET-iO of this aspect, I was thinking that each player should have the following data available under it:
Then there could be the a data structure in PhET-iO that is the "currently displayed values" that is read only. So that if you built a "save data" function to pull values into your system, you always get the visible data using the same static PhET-iO Element ID.
|
Current patch: Subject: [PATCH] Rename CAVModel => CAVSceneModel, see https://github.com/phetsims/center-and-variability/issues/164
---
Index: js/common/view/ValueReadoutsNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/ValueReadoutsNode.ts b/js/common/view/ValueReadoutsNode.ts
--- a/js/common/view/ValueReadoutsNode.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/view/ValueReadoutsNode.ts (date 1683126064145)
@@ -9,7 +9,7 @@
import optionize, { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import centerAndVariability from '../../centerAndVariability.js';
-import { TPaint, Text, VBox, VBoxOptions } from '../../../../scenery/js/imports.js';
+import { Text, TPaint, VBox, VBoxOptions } from '../../../../scenery/js/imports.js';
import CAVSceneModel from '../model/CAVSceneModel.js';
import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js';
import StringUtils from '../../../../phetcommon/js/util/StringUtils.js';
Index: js/mean-and-median/model/MeanAndMedianModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/mean-and-median/model/MeanAndMedianModel.ts b/js/mean-and-median/model/MeanAndMedianModel.ts
--- a/js/mean-and-median/model/MeanAndMedianModel.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/mean-and-median/model/MeanAndMedianModel.ts (date 1683132869576)
@@ -9,13 +9,14 @@
import centerAndVariability from '../../centerAndVariability.js';
import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
-import CAVSceneModel, { CAVModelOptions } from '../../common/model/CAVSceneModel.js';
+import CAVSceneModel from '../../common/model/CAVSceneModel.js';
+import CAVModel from '../../common/model/CAVModel.js';
+import Tandem from '../../../../tandem/js/Tandem.js';
// constants
const HIGHLIGHT_ANIMATION_TIME_STEP = 0.25; // in seconds
-export default class MeanAndMedianModel extends CAVSceneModel {
-
+export default class MeanAndMedianModel extends CAVModel {
// Indicates how far the show median animation has progressed, or null if not animating. Not PhET-iO instrumented since
// it represents a transient value.
@@ -24,8 +25,13 @@
private lastHighlightAnimationStepTime = 0;
public readonly isMedianAnimationCompleteProperty = new BooleanProperty( false );
- public constructor( options: CAVModelOptions ) {
- super( options );
+ public constructor( options: { tandem: Tandem } ) {
+
+ const scene = new CAVSceneModel( { tandem: options.tandem.createTandem( 'sceneModel' ) } );
+ super( [ scene ], {
+ ...options,
+ instrumentMeanPredictionProperty: true
+ } );
// Don't show animation on startup or when setting PhET-iO state
this.isShowingTopMedianProperty.lazyLink( isShowingTopMedian => {
@@ -33,7 +39,7 @@
if ( !phet.joist.sim.isSettingPhetioStateProperty.value ) {
this.highlightAnimationIndex = 0;
- this.lastHighlightAnimationStepTime = this.timeProperty.value;
+ this.lastHighlightAnimationStepTime = scene.timeProperty.value;
}
else {
@@ -47,7 +53,7 @@
}
} );
- this.objectValueBecameNonNullEmitter.addListener( () => this.updateAnimation() );
+ scene.objectValueBecameNonNullEmitter.addListener( () => this.updateAnimation() );
}
public override reset(): void {
@@ -63,12 +69,12 @@
private clearAnimation(): void {
this.highlightAnimationIndex = null;
- this.soccerBalls.forEach( soccerBall => soccerBall.isShowingAnimationHighlightProperty.set( false ) );
+ this.selectedSceneModelProperty.value.soccerBalls.forEach( soccerBall => soccerBall.isShowingAnimationHighlightProperty.set( false ) );
}
private updateAnimation(): void {
- const sortedObjects = this.getSortedLandedObjects();
+ const sortedObjects = this.selectedSceneModelProperty.value.getSortedLandedObjects();
for ( let i = 0; i < sortedObjects.length / 2; i++ ) {
const isHighlighted = i === this.highlightAnimationIndex;
@@ -86,11 +92,11 @@
this.isMedianAnimationCompleteProperty.value = true;
}
else if ( this.highlightAnimationIndex !== null &&
- this.timeProperty.value > this.lastHighlightAnimationStepTime + HIGHLIGHT_ANIMATION_TIME_STEP ) {
+ this.selectedSceneModelProperty.value.timeProperty.value > this.lastHighlightAnimationStepTime + HIGHLIGHT_ANIMATION_TIME_STEP ) {
// if the animation has already started, step it to the next animation index
this.highlightAnimationIndex++;
- this.lastHighlightAnimationStepTime = this.timeProperty.value;
+ this.lastHighlightAnimationStepTime = this.selectedSceneModelProperty.value.timeProperty.value;
}
}
}
Index: js/center-and-variability-main.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/center-and-variability-main.ts b/js/center-and-variability-main.ts
--- a/js/center-and-variability-main.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/center-and-variability-main.ts (date 1683124664802)
@@ -12,10 +12,8 @@
import Tandem from '../../tandem/js/Tandem.js';
import CenterAndVariabilityStrings from './CenterAndVariabilityStrings.js';
import MedianScreen from './median/MedianScreen.js';
-import MeanAndMedianScreen from './mean-and-median/MeanAndMedianScreen.js';
import SimulationPreferencesContentNode from './common/view/SimulationPreferencesContentNode.js';
import PreferencesModel from '../../joist/js/preferences/PreferencesModel.js';
-import VariabilityScreen from './variability/VariabilityScreen.js';
const centerAndVariabilityTitleStringProperty = CenterAndVariabilityStrings[ 'center-and-variability' ].titleStringProperty;
@@ -39,8 +37,8 @@
simLauncher.launch( () => {
const sim = new Sim( centerAndVariabilityTitleStringProperty, [
new MedianScreen( { tandem: Tandem.ROOT.createTandem( 'medianScreen' ) } ),
- new MeanAndMedianScreen( { tandem: Tandem.ROOT.createTandem( 'meanAndMedianScreen' ) } ),
- new VariabilityScreen( { tandem: Tandem.ROOT.createTandem( 'variabilityScreen' ) } )
+ // new MeanAndMedianScreen( { tandem: Tandem.ROOT.createTandem( 'meanAndMedianScreen' ) } ),
+ // new VariabilityScreen( { tandem: Tandem.ROOT.createTandem( 'variabilityScreen' ) } )
], simOptions );
sim.start();
} );
\ No newline at end of file
Index: js/variability/model/VariabilitySceneModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/variability/model/VariabilitySceneModel.ts b/js/variability/model/VariabilitySceneModel.ts
new file mode 100644
--- /dev/null (date 1683125899188)
+++ b/js/variability/model/VariabilitySceneModel.ts (date 1683125899188)
@@ -0,0 +1,74 @@
+// Copyright 2023, University of Colorado Boulder
+
+import CAVSceneModel from '../../common/model/CAVSceneModel.js';
+import centerAndVariability from '../../centerAndVariability.js';
+import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
+import Property from '../../../../axon/js/Property.js';
+import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
+import NullableIO from '../../../../tandem/js/types/NullableIO.js';
+import NumberIO from '../../../../tandem/js/types/NumberIO.js';
+import Tandem from '../../../../tandem/js/Tandem.js';
+
+export default class VariabilitySceneModel extends CAVSceneModel {
+
+ public readonly maxValueProperty: TReadOnlyProperty<number | null>;
+ public readonly minValueProperty: TReadOnlyProperty<number | null>;
+ public readonly rangeValueProperty: TReadOnlyProperty<number | null>;
+ public readonly q1ValueProperty: Property<number | null>;
+ public readonly q3ValueProperty: Property<number | null>;
+ public readonly iqrValueProperty: TReadOnlyProperty<number | null>;
+ public readonly madValueProperty: Property<number | null>;
+
+ public constructor( options: { tandem: Tandem } ) {
+ super();
+
+ this.maxValueProperty = new DerivedProperty( [ this.dataRangeProperty ], dataRange => {
+ return dataRange === null ? null : dataRange.max;
+ }, {
+ tandem: options.tandem.createTandem( 'maxValueProperty' ),
+ phetioValueType: NullableIO( NumberIO )
+ } );
+ this.minValueProperty = new DerivedProperty( [ this.dataRangeProperty ], dataRange => {
+ return dataRange === null ? null : dataRange.min;
+ }, {
+ tandem: options.tandem.createTandem( 'minValueProperty' ),
+ phetioValueType: NullableIO( NumberIO )
+ } );
+
+ this.rangeValueProperty = new DerivedProperty( [ this.maxValueProperty, this.minValueProperty ], ( max, min ) => {
+ return ( max === null || min === null ) ? null : max - min;
+ }, {
+ tandem: options.tandem.createTandem( 'rangeValueProperty' ),
+ phetioValueType: NullableIO( NumberIO )
+ } );
+
+ this.q1ValueProperty = new Property<number | null>( null, {
+ tandem: options.tandem.createTandem( 'q1ValueProperty' ),
+ phetioValueType: NullableIO( NumberIO ),
+ phetioReadOnly: true
+ } );
+ this.q3ValueProperty = new Property<number | null>( null, {
+ tandem: options.tandem.createTandem( 'q3ValueProperty' ),
+ phetioValueType: NullableIO( NumberIO ),
+ phetioReadOnly: true
+ } );
+ this.iqrValueProperty = new DerivedProperty( [ this.q1ValueProperty, this.q3ValueProperty ], ( q1, q3 ) => {
+ return q3! - q1!;
+ } );
+
+ this.madValueProperty = new Property<number | null>( null, {
+ tandem: options.tandem.createTandem( 'madValueProperty' ),
+ phetioValueType: NullableIO( NumberIO )
+ } );
+ }
+
+ public override reset(): void {
+ super.reset();
+
+ // TODO: Should this be called in the parent class? Or at least make sure not to forget it
+ // in any of the subclasses?
+ this.updateDataMeasures();
+ }
+}
+
+centerAndVariability.register( 'VariabilitySceneModel', VariabilitySceneModel );
\ No newline at end of file
Index: js/common/view/CAVPlotNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/CAVPlotNode.ts b/js/common/view/CAVPlotNode.ts
--- a/js/common/view/CAVPlotNode.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/view/CAVPlotNode.ts (date 1683132721319)
@@ -19,6 +19,8 @@
import CAVConstants from '../CAVConstants.js';
import WithRequired from '../../../../phet-core/js/types/WithRequired.js';
import DataPointNode from './DataPointNode.js';
+import CAVModel from '../model/CAVModel.js';
+import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
type SelfOptions = {
dataPointFill: TColor;
@@ -30,7 +32,7 @@
private readonly dotLayer = new Node();
protected readonly modelViewTransform: ModelViewTransform2;
- public constructor( model: CAVSceneModel, providedOptions?: CAVPlotOptions ) {
+ public constructor( model: CAVModel, sceneModel: CAVSceneModel, providedOptions?: CAVPlotOptions ) {
const options = optionize<CAVPlotOptions, SelfOptions, NodeOptions>()( {}, providedOptions );
@@ -47,16 +49,15 @@
// Coordinates here are somewhat unusual, since x dimension is based off of meters, and y dimension is based off of
// number of objects.
const modelViewTransform = ModelViewTransform2.createRectangleInvertedYMapping(
- new Bounds2( model.physicalRange.min, 0, model.physicalRange.max, 1 ),
+ new Bounds2( CAVConstants.PHYSICAL_RANGE.min, 0, CAVConstants.PHYSICAL_RANGE.max, 1 ),
new Bounds2( 0, numberLinePositionY - dataPointHeight, CAVConstants.CHART_VIEW_WIDTH, numberLinePositionY )
);
this.modelViewTransform = modelViewTransform;
const numberLineNode = new NumberLineNode(
- model.physicalRange,
- model.meanValueProperty,
+ sceneModel.meanValueProperty,
model.isShowingTopMeanProperty,
- model.dataRangeProperty, {
+ sceneModel.dataRangeProperty, {
color: 'black',
includeXAxis: true,
includeMeanStroke: false,
@@ -77,14 +78,24 @@
backgroundNode.addChild( this.dotLayer );
- model.soccerBalls.forEach( ( soccerBall, index ) => {
+ model.sceneModels.scenes.forEach( scene => {
+
+ const dataPointLayer = new Node( {
+ visibleProperty: scene.isVisibleProperty
+ } );
+
+ // Create the data points for that scene
+ scene.soccerBalls.forEach( ( soccerBall, index ) => {
- const dotNode = new DataPointNode( soccerBall, model.isShowingTopMedianProperty, modelViewTransform, {
- tandem: options.tandem.createTandem( 'dotNodeGroup' ).createTandem( 'dataPoint' + index ),
- fill: options.dataPointFill
- } );
+ const dotNode = new DataPointNode( soccerBall, model.isShowingTopMedianProperty, modelViewTransform, {
+ tandem: options.tandem.createTandem( 'dotNodeGroup' ).createTandem( 'dataPoint' + index ),
+ fill: options.dataPointFill
+ } );
- this.dotLayer.addChild( dotNode );
+ dataPointLayer.addChild( dotNode );
+ } );
+
+ this.dotLayer.addChild( dataPointLayer );
} );
}
Index: js/common/view/SceneView.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/SceneView.ts b/js/common/view/SceneView.ts
new file mode 100644
--- /dev/null (date 1683134965566)
+++ b/js/common/view/SceneView.ts (date 1683134965566)
@@ -0,0 +1,144 @@
+// Copyright 2023, University of Colorado Boulder
+
+import { Node } from '../../../../scenery/js/imports.js';
+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 BooleanProperty from '../../../../axon/js/BooleanProperty.js';
+import Tandem from '../../../../tandem/js/Tandem.js';
+import CAVModel from '../model/CAVModel.js';
+import CAVConstants from '../CAVConstants.js';
+import centerAndVariability from '../../centerAndVariability.js';
+import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js';
+import DragIndicatorArrowNode from './DragIndicatorArrowNode.js';
+import PlayAreaMedianIndicatorNode from './PlayAreaMedianIndicatorNode.js';
+import AccordionBox from '../../../../sun/js/AccordionBox.js';
+
+export default class SceneView {
+
+ private readonly updateMedianNode: () => void;
+ private objectHasBeenDragged: boolean;
+
+ public constructor(
+ model: CAVModel,
+ sceneModel: CAVSceneModel,
+ backObjectLayer: Node,
+ frontObjectLayer: Node,
+ modelViewTransform: ModelViewTransform2,
+ accordionBox: AccordionBox,
+ options: { tandem: Tandem } ) {
+
+ // TODO: This shouldn't be per-scene
+ this.objectHasBeenDragged = false;
+ const objectNodeGroupTandem = options.tandem.createTandem( 'soccerBallNodeGroup' );
+
+ const objectNodesInputEnabledProperty = new BooleanProperty( true, {
+ tandem: objectNodeGroupTandem.createTandem( 'inputEnabledProperty' )
+ } );
+ sceneModel.soccerBalls.map( ( soccerBall, index ) => {
+ const soccerBallNode = new SoccerBallNode( soccerBall, model.isShowingPlayAreaMedianProperty, modelViewTransform, objectNodesInputEnabledProperty, {
+ tandem: options.tandem.createTandem( 'soccerBalls' ).createTandem( 'soccerBallNode' + index )
+ } );
+
+ backObjectLayer.addChild( soccerBallNode );
+
+ // While flying, it should be in front in z-order, to be in front of the accordion box
+ soccerBall.animationModeProperty.lazyLink( ( animationMode, oldAnimationModel ) => {
+ if ( animationMode === AnimationMode.FLYING ) {
+ backObjectLayer.removeChild( soccerBallNode );
+ frontObjectLayer.addChild( soccerBallNode );
+ }
+ else if ( oldAnimationModel ) {
+ frontObjectLayer.removeChild( soccerBallNode );
+ backObjectLayer.addChild( soccerBallNode );
+ }
+ } );
+ const dragIndicatorArrowNode = new DragIndicatorArrowNode( {
+ tandem: options.tandem.createTandem( 'dragIndicatorArrowNode' ),
+ visible: false
+ } );
+
+ // TODO: https://github.com/phetsims/center-and-variability/issues/164 should only have one across scenes
+ backObjectLayer.addChild( dragIndicatorArrowNode );
+
+ soccerBall.valueProperty.lazyLink( ( value, oldValue ) => {
+ if ( value !== null ) {
+ if ( oldValue === null ) {
+
+ // add the dragIndicatorArrowNode above the last object when it is added to the play area. if an object was
+ // moved before this happens, don't show the dragIndicatorArrowNode
+ if ( sceneModel.soccerBallCountProperty.value === CAVConstants.PHYSICAL_RANGE.max &&
+ objectNodesInputEnabledProperty.value &&
+ _.every( sceneModel.soccerBalls, soccerBall => soccerBall.valueProperty.value !== null ) &&
+ !this.objectHasBeenDragged ) {
+ dragIndicatorArrowNode.centerX = modelViewTransform.modelToViewX( value );
+
+ const dragIndicatorArrowNodeMargin = 6;
+
+ // calculate where the top object is
+ const topObjectPositionY = modelViewTransform.modelToViewY( 0 ) -
+ ( sceneModel.getOtherObjectsAtTarget( soccerBall ).length + 1 ) *
+ Math.abs( modelViewTransform.modelToViewDeltaY( CAVObjectType.SOCCER_BALL.radius ) ) * 2 -
+ dragIndicatorArrowNodeMargin;
+
+ dragIndicatorArrowNode.bottom = topObjectPositionY;
+ dragIndicatorArrowNode.visible = true;
+ }
+ }
+ else {
+ this.objectHasBeenDragged = true;
+ dragIndicatorArrowNode.visible = false;
+ }
+ }
+ } );
+
+ return soccerBallNode;
+ } );
+
+ const playAreaMedianIndicatorNode = new PlayAreaMedianIndicatorNode();
+ frontObjectLayer.addChild( playAreaMedianIndicatorNode );
+
+ this.updateMedianNode = () => {
+ const medianValue = sceneModel.medianValueProperty.value;
+ const visible = medianValue !== null && model.isShowingPlayAreaMedianProperty.value;
+
+ if ( visible ) {
+
+ // if there is a ball at that location, go above the ball
+ const ballsAtLocation = sceneModel.soccerBalls.filter( soccerBall => soccerBall.valueProperty.value === medianValue );
+ const modelHeight = ballsAtLocation.length * CAVObjectType.SOCCER_BALL.radius * 2; // assumes no spacing
+
+ const viewHeight = modelViewTransform.modelToViewDeltaY( modelHeight );
+
+ playAreaMedianIndicatorNode.centerX = modelViewTransform.modelToViewX( medianValue );
+ playAreaMedianIndicatorNode.bottom = modelViewTransform.modelToViewY( 0 ) + viewHeight;
+
+ // The arrow shouldn't overlap the accordion box
+ const accordionBoxHeight = accordionBox.expandedProperty.value ? accordionBox.getExpandedBoxHeight() : accordionBox.getCollapsedBoxHeight();
+ if ( playAreaMedianIndicatorNode.top < accordionBox.top + accordionBoxHeight ) {
+ playAreaMedianIndicatorNode.top = accordionBox.top + accordionBoxHeight + 4;
+ }
+ }
+ playAreaMedianIndicatorNode.visible = visible;
+ };
+ sceneModel.medianValueProperty.link( this.updateMedianNode );
+ sceneModel.objectChangedEmitter.addListener( this.updateMedianNode );
+
+ const soccerPlayerNodes = sceneModel.soccerPlayers.map( soccerPlayer => new SoccerPlayerNode( soccerPlayer, modelViewTransform ) );
+
+ soccerPlayerNodes.forEach( soccerPlayerNode => frontObjectLayer.addChild( soccerPlayerNode ) );
+
+ model.isShowingPlayAreaMedianProperty.link( this.updateMedianNode );
+
+ // TODO: https://github.com/phetsims/center-and-variability/issues/164
+ // this.accordionBox.expandedProperty.link( this.updateMedianNode );
+ }
+
+ public reset(): void {
+ this.objectHasBeenDragged = false;
+ }
+}
+
+centerAndVariability.register( 'SceneView', SceneView );
\ No newline at end of file
Index: js/common/view/NumberLineNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/NumberLineNode.ts b/js/common/view/NumberLineNode.ts
--- a/js/common/view/NumberLineNode.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/view/NumberLineNode.ts (date 1683123944706)
@@ -39,7 +39,6 @@
public readonly tickMarkSet: TickMarkSet;
public constructor(
- range: Range,
meanValueProperty: TReadOnlyProperty<number | null>,
isShowingMeanIndicatorProperty: TReadOnlyProperty<boolean>,
rangeProperty: TReadOnlyProperty<Range | null>,
@@ -57,7 +56,7 @@
const chartTransform = new ChartTransform( {
viewWidth: CAVConstants.CHART_VIEW_WIDTH,
- modelXRange: range,
+ modelXRange: CAVConstants.PHYSICAL_RANGE,
viewHeight: tickMarkExtent / 2,
modelYRange: new Range( 0, 1 )
} );
@@ -109,7 +108,7 @@
// TODO: Can we make a 1d MVT since that's all that's needed here, or should this be using the same MVT as the outer MVT? Like the one that positions the number line node
// and puts objects in the right spots.
const modelViewTransform = ModelViewTransform2.createRectangleInvertedYMapping(
- new Bounds2( range.min, 0, range.max, range.getLength() ),
+ new Bounds2( CAVConstants.PHYSICAL_RANGE.min, 0, CAVConstants.PHYSICAL_RANGE.max, CAVConstants.PHYSICAL_RANGE.getLength() ),
new Bounds2( 0, -CAVConstants.CHART_VIEW_WIDTH, CAVConstants.CHART_VIEW_WIDTH, CAVConstants.CHART_VIEW_WIDTH )
);
Index: js/common/view/BottomRepresentationCheckboxGroup.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/BottomRepresentationCheckboxGroup.ts b/js/common/view/BottomRepresentationCheckboxGroup.ts
--- a/js/common/view/BottomRepresentationCheckboxGroup.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/view/BottomRepresentationCheckboxGroup.ts (date 1683126064136)
@@ -13,7 +13,6 @@
import centerAndVariability from '../../centerAndVariability.js';
import VerticalCheckboxGroup, { VerticalCheckboxGroupItem } from '../../../../sun/js/VerticalCheckboxGroup.js';
import { AlignGroup, GridBox, Node, TColor, Text } from '../../../../scenery/js/imports.js';
-import CAVSceneModel from '../model/CAVSceneModel.js';
import CAVConstants from '../CAVConstants.js';
import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js';
import CAVColors from '../CAVColors.js';
@@ -21,6 +20,7 @@
import PredictionThumbNode from './PredictionThumbNode.js';
import LinkableProperty from '../../../../axon/js/LinkableProperty.js';
import VariabilityModel from '../../variability/model/VariabilityModel.js';
+import CAVModel from '../model/CAVModel.js';
// constants
const TEXT_OPTIONS = {
@@ -56,7 +56,7 @@
};
}
- public static getMedianCheckboxItem( alignGroup: AlignGroup, model: CAVSceneModel ): VerticalCheckboxGroupItem {
+ public static getMedianCheckboxItem( alignGroup: AlignGroup, model: CAVModel ): VerticalCheckboxGroupItem {
return {
createNode: ( tandem: Tandem ) => {
return BottomRepresentationCheckboxGroup.createGridBox(
@@ -75,7 +75,7 @@
};
}
- public static getMeanCheckboxItem( alignGroup: AlignGroup, model: CAVSceneModel ): VerticalCheckboxGroupItem {
+ public static getMeanCheckboxItem( alignGroup: AlignGroup, model: CAVModel ): VerticalCheckboxGroupItem {
return {
createNode: ( tandem: Tandem ) => BottomRepresentationCheckboxGroup.createGridBox( new Text( CenterAndVariabilityStrings.meanStringProperty, TEXT_OPTIONS ),
NumberLineNode.createMeanIndicatorNode( true, true ), alignGroup ),
@@ -98,7 +98,7 @@
};
}
- public static getPredictMedianCheckboxItem( alignGroup: AlignGroup, model: CAVSceneModel ): VerticalCheckboxGroupItem {
+ public static getPredictMedianCheckboxItem( alignGroup: AlignGroup, model: CAVModel ): VerticalCheckboxGroupItem {
return BottomRepresentationCheckboxGroup.createPredictionItem(
model.isShowingMedianPredictionProperty,
CenterAndVariabilityStrings.predictMedianStringProperty,
@@ -109,7 +109,7 @@
);
}
- public static getPredictMeanCheckboxItem( alignGroup: AlignGroup, model: CAVSceneModel ): VerticalCheckboxGroupItem {
+ public static getPredictMeanCheckboxItem( alignGroup: AlignGroup, model: CAVModel ): VerticalCheckboxGroupItem {
return BottomRepresentationCheckboxGroup.createPredictionItem(
model.isShowingMeanPredictionProperty,
CenterAndVariabilityStrings.predictMeanStringProperty,
Index: js/common/model/CAVModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/CAVModel.ts b/js/common/model/CAVModel.ts
new file mode 100644
--- /dev/null (date 1683132772082)
+++ b/js/common/model/CAVModel.ts (date 1683132772082)
@@ -0,0 +1,91 @@
+// Copyright 2023, University of Colorado Boulder
+
+import centerAndVariability from '../../centerAndVariability.js';
+import NumberProperty from '../../../../axon/js/NumberProperty.js';
+import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
+import Tandem from '../../../../tandem/js/Tandem.js';
+import CAVConstants from '../CAVConstants.js';
+import Property from '../../../../axon/js/Property.js';
+import CAVSceneModel from './CAVSceneModel.js';
+
+type SelfOptions = {
+ tandem: Tandem;
+ instrumentMeanPredictionProperty: boolean;
+};
+export type CAVModelOptions = SelfOptions;
+
+export default class CAVModel {
+
+ // TODO: Some of these should move to subclasses
+ public readonly isShowingTopMeanProperty: BooleanProperty;
+ public readonly isShowingTopMedianProperty: BooleanProperty;
+ public readonly isShowingPlayAreaMedianProperty: BooleanProperty;
+ public readonly isShowingPlayAreaMeanProperty: BooleanProperty;
+ public readonly isShowingMeanPredictionProperty: BooleanProperty;
+ public readonly isShowingMedianPredictionProperty: BooleanProperty;
+
+ // Null until the user has made a prediction. TODO: is this comment correct?
+ public readonly medianPredictionProperty: NumberProperty;
+ public readonly meanPredictionProperty: NumberProperty;
+
+ public readonly selectedSceneModelProperty!: Property<CAVSceneModel>;
+
+ public constructor( public readonly sceneModels: CAVSceneModel[], options: CAVModelOptions ) {
+
+ this.isShowingTopMeanProperty = new BooleanProperty( false, {
+ tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'isShowingTopMeanProperty' ) : Tandem.OPT_OUT
+ } );
+ this.isShowingTopMedianProperty = new BooleanProperty( false, {
+ tandem: options.tandem.createTandem( 'isShowingTopMedianProperty' )
+ } );
+ this.isShowingPlayAreaMeanProperty = new BooleanProperty( false, {
+ tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'isShowingPlayAreaMeanProperty' ) : Tandem.OPT_OUT
+ } );
+ this.isShowingPlayAreaMedianProperty = new BooleanProperty( false, {
+ tandem: options.tandem.createTandem( 'isShowingPlayAreaMedianProperty' )
+ } );
+ this.isShowingMeanPredictionProperty = new BooleanProperty( false, {
+ tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'isShowingMeanPredictionProperty' ) : Tandem.OPT_OUT
+ } );
+ this.isShowingMedianPredictionProperty = new BooleanProperty( false, {
+ tandem: options.tandem.createTandem( 'isShowingMedianPredictionProperty' )
+ } );
+
+ this.medianPredictionProperty = new NumberProperty( 1, {
+
+ // Assumes all physical ranges are the same
+ range: CAVConstants.PHYSICAL_RANGE,
+ tandem: options.tandem.createTandem( 'medianPredictionProperty' )
+ } );
+ this.meanPredictionProperty = new NumberProperty( 1.5, {
+ range: CAVConstants.PHYSICAL_RANGE,
+ tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'meanPredictionProperty' ) : Tandem.OPT_OUT
+ } );
+
+ this.selectedSceneModelProperty = new Property( sceneModels[ 0 ], {
+ tandem: options.tandem.createTandem( 'selectedSceneModelProperty' )
+ } );
+
+ // this.isShowingPlayAreaMedianProperty.link( updateDataMeasures );
+ }
+
+ public step( dt: number ): void {
+ // Override in subclasses
+ }
+
+ public reset(): void {
+ this.medianPredictionProperty.reset();
+ this.meanPredictionProperty.reset();
+ this.isShowingTopMeanProperty.reset();
+ this.isShowingTopMedianProperty.reset();
+ this.isShowingPlayAreaMeanProperty.reset();
+ this.isShowingPlayAreaMedianProperty.reset();
+ this.isShowingMeanPredictionProperty.reset();
+ this.isShowingMedianPredictionProperty.reset();
+
+ this.sceneModels.forEach( sceneModel => sceneModel.reset() );
+ this.selectedSceneModelProperty.reset();
+ }
+}
+
+centerAndVariability.register( 'CAVModel', CAVModel );
\ No newline at end of file
Index: js/common/model/CAVSceneModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/CAVSceneModel.ts b/js/common/model/CAVSceneModel.ts
--- a/js/common/model/CAVSceneModel.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/model/CAVSceneModel.ts (date 1683134252540)
@@ -16,7 +16,6 @@
import Property from '../../../../axon/js/Property.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import Utils from '../../../../dot/js/Utils.js';
-import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import Emitter from '../../../../axon/js/Emitter.js';
import NullableIO from '../../../../tandem/js/types/NullableIO.js';
@@ -32,12 +31,7 @@
import Easing from '../../../../twixt/js/Easing.js';
import Pose from './Pose.js';
import { AnimationMode } from './AnimationMode.js';
-
-type SelfOptions = {
- tandem: Tandem;
- instrumentMeanPredictionProperty: boolean;
-};
-export type CAVModelOptions = SelfOptions;
+import TProperty from '../../../../axon/js/TProperty.js';
// constants
const TIME_BETWEEN_RAPID_KICKS = 0.5; // in seconds
@@ -46,16 +40,7 @@
public readonly soccerBalls: SoccerBall[];
public readonly soccerBallCountProperty: NumberProperty;
- // TODO: Some of these should move to subclasses
- public readonly isShowingTopMeanProperty: BooleanProperty;
- public readonly isShowingTopMedianProperty: BooleanProperty;
- public readonly isShowingPlayAreaMedianProperty: BooleanProperty;
- public readonly isShowingPlayAreaMeanProperty: BooleanProperty;
- public readonly isShowingMeanPredictionProperty: BooleanProperty;
- public readonly isShowingMedianPredictionProperty: BooleanProperty;
-
public readonly maxSoccerBalls = CAVConstants.NUMBER_OF_OBJECTS;
- public readonly physicalRange = new Range( 1, 15 );
public readonly medianValueProperty: Property<number | null>;
public readonly meanValueProperty: Property<number | null>;
@@ -68,13 +53,9 @@
parameters: [ { valueType: SoccerBall } ]
} );
- // Null until the user has made a prediction.
- public readonly medianPredictionProperty: NumberProperty;
- public readonly meanPredictionProperty: NumberProperty;
+ public readonly timeProperty: NumberProperty;
- protected readonly timeProperty: NumberProperty;
-
- protected readonly objectValueBecameNonNullEmitter: TEmitter<[ SoccerBall ]>;
+ public readonly objectValueBecameNonNullEmitter: TEmitter<[ SoccerBall ]>;
public readonly resetEmitter: TEmitter = new Emitter();
public readonly numberOfDataPointsProperty: NumberProperty;
@@ -89,7 +70,7 @@
// Starting at 0, iterate through the index of the kickers. This updates the SoccerPlayer.isActiveProperty to show the current kicker
private readonly activeKickerIndexProperty: NumberProperty;
- public constructor( options: CAVModelOptions ) {
+ public constructor( options: { tandem: Tandem } ) {
const updateDataMeasures = () => this.updateDataMeasures();
@@ -109,7 +90,7 @@
// When the soccer ball drag position changes, constrain it to the physical range and move it to the top, if necessary
soccerBall.dragPositionProperty.lazyLink( ( dragPosition: Vector2 ) => {
- soccerBall.valueProperty.value = Utils.roundSymmetric( this.physicalRange.constrainValue( dragPosition.x ) );
+ soccerBall.valueProperty.value = Utils.roundSymmetric( CAVConstants.PHYSICAL_RANGE.constrainValue( dragPosition.x ) );
this.moveToTop( soccerBall );
} );
@@ -142,25 +123,6 @@
} );
} );
- this.isShowingTopMeanProperty = new BooleanProperty( false, {
- tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'isShowingTopMeanProperty' ) : Tandem.OPT_OUT
- } );
- this.isShowingTopMedianProperty = new BooleanProperty( false, {
- tandem: options.tandem.createTandem( 'isShowingTopMedianProperty' )
- } );
- this.isShowingPlayAreaMeanProperty = new BooleanProperty( false, {
- tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'isShowingPlayAreaMeanProperty' ) : Tandem.OPT_OUT
- } );
- this.isShowingPlayAreaMedianProperty = new BooleanProperty( false, {
- tandem: options.tandem.createTandem( 'isShowingPlayAreaMedianProperty' )
- } );
- this.isShowingMeanPredictionProperty = new BooleanProperty( false, {
- tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'isShowingMeanPredictionProperty' ) : Tandem.OPT_OUT
- } );
- this.isShowingMedianPredictionProperty = new BooleanProperty( false, {
- tandem: options.tandem.createTandem( 'isShowingMedianPredictionProperty' )
- } );
-
this.medianValueProperty = new Property<number | null>( null, {
tandem: options.tandem.createTandem( 'medianValueProperty' ),
phetioValueType: NullableIO( NumberIO ),
@@ -175,21 +137,10 @@
this.numberOfDataPointsProperty = new NumberProperty( 0 );
- this.medianPredictionProperty = new NumberProperty( 1, {
- range: this.physicalRange,
- tandem: options.tandem.createTandem( 'medianPredictionProperty' )
- } );
- this.meanPredictionProperty = new NumberProperty( 1.5, {
- range: this.physicalRange,
- tandem: options.instrumentMeanPredictionProperty ? options.tandem.createTandem( 'meanPredictionProperty' ) : Tandem.OPT_OUT
- } );
-
this.timeProperty = new NumberProperty( 0, {
tandem: options.tandem.createTandem( 'timeProperty' )
} );
- this.isShowingPlayAreaMedianProperty.link( updateDataMeasures );
-
this.objectValueBecameNonNullEmitter = new Emitter<[ SoccerBall ]>( {
parameters: [ { valueType: SoccerBall } ]
} );
@@ -228,7 +179,7 @@
tandem: options.tandem.createTandem( 'distributionProperty' ),
phetioValueType: ArrayIO( NumberIO ),
phetioDocumentation: 'The distribution of probabilities of where the balls will land is represented as an un-normalized array of non-negative, floating-point numbers, one value for each location in the physical range',
- isValidValue: ( array: readonly number[] ) => array.length === this.physicalRange.getLength() + 1 && // inclusive of endpoints
+ isValidValue: ( array: readonly number[] ) => array.length === CAVConstants.PHYSICAL_RANGE.getLength() + 1 && // inclusive of endpoints
_.every( array, element => element >= 0 )
} );
@@ -325,14 +276,6 @@
* Resets the model.
*/
public reset(): void {
- this.medianPredictionProperty.reset();
- this.meanPredictionProperty.reset();
- this.isShowingTopMeanProperty.reset();
- this.isShowingTopMedianProperty.reset();
- this.isShowingPlayAreaMeanProperty.reset();
- this.isShowingPlayAreaMedianProperty.reset();
- this.isShowingMeanPredictionProperty.reset();
- this.isShowingMedianPredictionProperty.reset();
this.distributionProperty.value = CAVSceneModel.chooseDistribution();
this.clearData();
@@ -493,7 +436,7 @@
const weights = this.distributionProperty.value;
- assert && assert( weights.length === this.physicalRange.getLength() + 1, 'weight array should match the model range' );
+ assert && assert( weights.length === CAVConstants.PHYSICAL_RANGE.getLength() + 1, 'weight array should match the model range' );
const x1 = dotRandom.sampleProbabilities( weights ) + 1;
// Range equation is R=v0^2 sin(2 theta0) / g, see https://openstax.org/books/university-physics-volume-1/pages/4-3-projectile-motion
Index: js/variability/view/DistributionRadioButtonGroup.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/variability/view/DistributionRadioButtonGroup.ts b/js/variability/view/DistributionRadioButtonGroup.ts
--- a/js/variability/view/DistributionRadioButtonGroup.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/variability/view/DistributionRadioButtonGroup.ts (date 1683126064127)
@@ -5,7 +5,7 @@
import centerAndVariability from '../../centerAndVariability.js';
import optionize, { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import Property from '../../../../axon/js/Property.js';
-import { Path, Text, Node } from '../../../../scenery/js/imports.js';
+import { Node, Path, Text } from '../../../../scenery/js/imports.js';
import tshirtSolidShape from '../../../../sherpa/js/fontawesome-5/tshirtSolidShape.js';
import Tandem from '../../../../tandem/js/Tandem.js';
Index: js/common/CAVConstants.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/CAVConstants.ts b/js/common/CAVConstants.ts
--- a/js/common/CAVConstants.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/CAVConstants.ts (date 1683123497983)
@@ -14,6 +14,7 @@
import CAVQueryParameters from './CAVQueryParameters.js';
import PlotType from './model/PlotType.js';
import ScreenView from '../../../joist/js/ScreenView.js';
+import Range from '../../../dot/js/Range.js';
// Right skewed means most of the data is on the left, see https://github.com/phetsims/center-and-variability/issues/112
const RIGHT_SKEWED_DATA = [
@@ -50,7 +51,9 @@
CHECKBOX_TEXT_OPTIONS: {
font: MAIN_FONT,
maxWidth: 90
- }
+ },
+
+ PHYSICAL_RANGE: new Range( 1, 15 )
};
centerAndVariability.register( 'CAVConstants', CAVConstants );
Index: js/common/CAVScreen.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/CAVScreen.ts b/js/common/CAVScreen.ts
--- a/js/common/CAVScreen.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/CAVScreen.ts (date 1683123569645)
@@ -11,15 +11,15 @@
import optionize, { EmptySelfOptions } from '../../../phet-core/js/optionize.js';
import CAVColors from '../common/CAVColors.js';
import centerAndVariability from '../centerAndVariability.js';
-import CAVSceneModel from './model/CAVSceneModel.js';
import CAVScreenView from './view/CAVScreenView.js';
import PickRequired from '../../../phet-core/js/types/PickRequired.js';
import SliderControlsAndBasicActionsKeyboardHelpContent from '../../../scenery-phet/js/keyboard/help/SliderControlsAndBasicActionsKeyboardHelpContent.js';
+import CAVModel from './model/CAVModel.js';
type SelfOptions = EmptySelfOptions;
export type CAVScreenOptions = SelfOptions & ScreenOptions & PickRequired<ScreenOptions, 'tandem'>;
-export default class CAVScreen<M extends CAVSceneModel, V extends CAVScreenView> extends Screen<M, V> {
+export default class CAVScreen<M extends CAVModel, V extends CAVScreenView> extends Screen<M, V> {
public constructor( createModel: () => M, createView: ( m: M ) => V, providedOptions?: CAVScreenOptions ) {
Index: js/variability/view/VariabilityPlotNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/variability/view/VariabilityPlotNode.ts b/js/variability/view/VariabilityPlotNode.ts
--- a/js/variability/view/VariabilityPlotNode.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/variability/view/VariabilityPlotNode.ts (date 1683126064141)
@@ -9,7 +9,7 @@
*/
import centerAndVariability from '../../centerAndVariability.js';
-import { NodeOptions, Node } from '../../../../scenery/js/imports.js';
+import { Node, NodeOptions } from '../../../../scenery/js/imports.js';
import PickRequired from '../../../../phet-core/js/types/PickRequired.js';
import VariabilityModel from '../model/VariabilityModel.js';
import RangeNode from './RangeNode.js';
Index: tsconfig.json
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tsconfig.json b/tsconfig.json
--- a/tsconfig.json (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/tsconfig.json (date 1683124664806)
@@ -2,7 +2,7 @@
{
"extends": "../chipper/tsconfig-core.json",
"include": [
- "js/**/*",
+ "js/center-and-variability-main.ts",
"images/**/*",
"mipmaps/**/*",
"sounds/**/*"
Index: js/common/view/CAVScreenView.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/CAVScreenView.ts b/js/common/view/CAVScreenView.ts
--- a/js/common/view/CAVScreenView.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/view/CAVScreenView.ts (date 1683135097865)
@@ -10,33 +10,28 @@
import ScreenView, { ScreenViewOptions } from '../../../../joist/js/ScreenView.js';
import optionize from '../../../../phet-core/js/optionize.js';
import centerAndVariability from '../../centerAndVariability.js';
-import CAVSceneModel from '../model/CAVSceneModel.js';
import CAVConstants from '../CAVConstants.js';
import ResetAllButton from '../../../../scenery-phet/js/buttons/ResetAllButton.js';
import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js';
import { AlignBox, ManualConstraint, Node } from '../../../../scenery/js/imports.js';
-import CAVObjectType from '../model/CAVObjectType.js';
import ArrowNode from '../../../../scenery-phet/js/ArrowNode.js';
import EraserButton from '../../../../scenery-phet/js/buttons/EraserButton.js';
-import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
-import DragIndicatorArrowNode from './DragIndicatorArrowNode.js';
import QuestionBar, { QuestionBarOptions } from '../../../../scenery-phet/js/QuestionBar.js';
import NumberLineNode from './NumberLineNode.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import BackgroundNode from './BackgroundNode.js';
-import SoccerPlayerNode from './SoccerPlayerNode.js';
import merge from '../../../../phet-core/js/merge.js';
-import KickButtonGroup from './KickButtonGroup.js';
-import PlayAreaMedianIndicatorNode from './PlayAreaMedianIndicatorNode.js';
import CAVAccordionBox from './CAVAccordionBox.js';
import VerticalCheckboxGroup, { VerticalCheckboxGroupItem } from '../../../../sun/js/VerticalCheckboxGroup.js';
-import { AnimationMode } from '../model/AnimationMode.js';
-import SoccerBallNode from './SoccerBallNode.js';
import PredictionSlider from './PredictionSlider.js';
import CAVColors from '../CAVColors.js';
import Property from '../../../../axon/js/Property.js';
import Range from '../../../../dot/js/Range.js';
import Tandem from '../../../../tandem/js/Tandem.js';
+import CAVModel from '../model/CAVModel.js';
+import SceneView from './SceneView.js';
+import KickButtonGroup from './KickButtonGroup.js';
+import DynamicProperty from '../../../../axon/js/DynamicProperty.js';
type SelfOptions = {
questionBarOptions: QuestionBarOptions;
@@ -51,13 +46,13 @@
protected readonly resetAllButton: ResetAllButton;
protected readonly modelViewTransform: ModelViewTransform2;
- protected readonly model: CAVSceneModel;
+ protected readonly model: CAVModel;
protected readonly frontObjectLayer = new Node();
// TODO: We haven't enforced the "exactly half a ball should be occluded if anything is occluded" design,
// May need https://github.com/phetsims/center-and-variability/issues/166 to be addressed first
protected readonly backObjectLayer = new Node();
- protected readonly playAreaMedianIndicatorNode: ArrowNode;
+
protected readonly eraseButton: EraserButton;
// Subclasses use this to add to for correct z-ordering and correct tab navigation order
@@ -67,14 +62,13 @@
protected readonly questionBar: QuestionBar;
protected readonly playAreaNumberLineNode: NumberLineNode;
- private readonly updateMedianNode: () => void;
- public constructor( model: CAVSceneModel, providedOptions: CAVScreenViewOptions ) {
+ public constructor( model: CAVModel, providedOptions: CAVScreenViewOptions ) {
const options = optionize<CAVScreenViewOptions, SelfOptions, ScreenViewOptions>()( {}, providedOptions );
// The ground is at y=0
const modelViewTransform = ModelViewTransform2.createRectangleInvertedYMapping(
- new Bounds2( model.physicalRange.min, 0, model.physicalRange.max, model.physicalRange.getLength() ),
+ new Bounds2( CAVConstants.PHYSICAL_RANGE.min, 0, CAVConstants.PHYSICAL_RANGE.max, CAVConstants.PHYSICAL_RANGE.getLength() ),
new Bounds2( CAVConstants.NUMBER_LINE_MARGIN_X, GROUND_POSITION_Y - CAVConstants.CHART_VIEW_WIDTH, CAVConstants.NUMBER_LINE_MARGIN_X + CAVConstants.CHART_VIEW_WIDTH, GROUND_POSITION_Y )
);
@@ -83,108 +77,17 @@
this.modelViewTransform = modelViewTransform;
this.model = model;
- const objectNodeGroupTandem = options.tandem.createTandem( 'soccerBallNodeGroup' );
-
- const objectNodesInputEnabledProperty = new BooleanProperty( true, {
- tandem: objectNodeGroupTandem.createTandem( 'inputEnabledProperty' )
- } );
-
- model.soccerBalls.map( ( soccerBall, index ) => {
- const soccerBallNode = new SoccerBallNode( soccerBall, model.isShowingPlayAreaMedianProperty, modelViewTransform, objectNodesInputEnabledProperty, {
- tandem: options.tandem.createTandem( 'soccerBalls' ).createTandem( 'soccerBallNode' + index )
- } );
-
- this.backObjectLayer.addChild( soccerBallNode );
-
- // While flying, it should be in front in z-order, to be in front of the accordion box
- soccerBall.animationModeProperty.lazyLink( ( animationMode, oldAnimationModel ) => {
- if ( animationMode === AnimationMode.FLYING ) {
- this.backObjectLayer.removeChild( soccerBallNode );
- this.frontObjectLayer.addChild( soccerBallNode );
- }
- else if ( oldAnimationModel ) {
- this.frontObjectLayer.removeChild( soccerBallNode );
- this.backObjectLayer.addChild( soccerBallNode );
- }
- } );
-
- soccerBall.valueProperty.lazyLink( ( value, oldValue ) => {
- if ( value !== null ) {
- if ( oldValue === null ) {
-
- // add the dragIndicatorArrowNode above the last object when it is added to the play area. if an object was
- // moved before this happens, don't show the dragIndicatorArrowNode
- if ( model.soccerBallCountProperty.value === this.model.physicalRange.max &&
- objectNodesInputEnabledProperty.value &&
- _.every( model.soccerBalls, soccerBall => soccerBall.valueProperty.value !== null ) &&
- !objectHasBeenDragged ) {
- dragIndicatorArrowNode.centerX = this.modelViewTransform.modelToViewX( value );
-
- const dragIndicatorArrowNodeMargin = 6;
-
- // calculate where the top object is
- const topObjectPositionY = this.modelViewTransform.modelToViewY( 0 ) -
- ( model.getOtherObjectsAtTarget( soccerBall ).length + 1 ) *
- Math.abs( this.modelViewTransform.modelToViewDeltaY( CAVObjectType.SOCCER_BALL.radius ) ) * 2 -
- dragIndicatorArrowNodeMargin;
-
- dragIndicatorArrowNode.bottom = topObjectPositionY;
- dragIndicatorArrowNode.visible = true;
- }
- }
- else {
- objectHasBeenDragged = true;
- dragIndicatorArrowNode.visible = false;
- }
- }
- } );
-
- return soccerBallNode;
- } );
+ // Soccer balls go behind the accordion box after they land
+ this.contentLayer.addChild( this.backObjectLayer );
this.addChild( this.contentLayer );
this.addChild( this.frontObjectLayer );
- let objectHasBeenDragged = false;
- const dragIndicatorArrowNode = new DragIndicatorArrowNode( {
- tandem: options.tandem.createTandem( 'dragIndicatorArrowNode' ),
- visible: false
- } );
- this.backObjectLayer.addChild( dragIndicatorArrowNode );
-
- this.playAreaMedianIndicatorNode = new PlayAreaMedianIndicatorNode();
- this.addChild( this.playAreaMedianIndicatorNode );
-
- this.updateMedianNode = () => {
- const medianValue = model.medianValueProperty.value;
- const visible = medianValue !== null && model.isShowingPlayAreaMedianProperty.value;
-
- if ( visible ) {
-
- // if there is a ball at that location, go above the ball
- const ballsAtLocation = model.soccerBalls.filter( soccerBall => soccerBall.valueProperty.value === medianValue );
- const modelHeight = ballsAtLocation.length * CAVObjectType.SOCCER_BALL.radius * 2; // assumes no spacing
-
- const viewHeight = this.modelViewTransform.modelToViewDeltaY( modelHeight );
-
- this.playAreaMedianIndicatorNode.centerX = this.modelViewTransform.modelToViewX( medianValue );
- this.playAreaMedianIndicatorNode.bottom = this.modelViewTransform.modelToViewY( 0 ) + viewHeight;
-
- // The arrow shouldn't overlap the accordion box
- if ( this.accordionBox ) {
- const accordionBoxHeight = this.accordionBox.expandedProperty.value ? this.accordionBox.getExpandedBoxHeight() : this.accordionBox.getCollapsedBoxHeight();
- if ( this.playAreaMedianIndicatorNode.top < this.accordionBox.top + accordionBoxHeight ) {
- this.playAreaMedianIndicatorNode.top = this.accordionBox.top + accordionBoxHeight + 4;
- }
- }
- }
- this.playAreaMedianIndicatorNode.visible = visible;
- };
- model.medianValueProperty.link( this.updateMedianNode );
- model.objectChangedEmitter.addListener( this.updateMedianNode );
- model.isShowingPlayAreaMedianProperty.link( this.updateMedianNode );
-
+ model.sceneModels.map( sceneModel => new SceneView( model, sceneModel, this.backObjectLayer, this.frontObjectLayer,
+ modelViewTransform, this.accordionBox, {
+ tandem: options.tandem.createTandem( 'sceneView' )
+ } ) );
this.resetAllButton = new ResetAllButton( {
listener: () => {
this.interruptSubtreeInput(); // cancel interactions that may be in progress
@@ -192,8 +95,9 @@
model.reset();
// hide the dragIndicatorArrowNode and reset the flag for if it has been dragged already
- objectHasBeenDragged = false;
dragIndicatorArrowNode.visible = false;
+
+
},
right: this.layoutBounds.maxX - CAVConstants.SCREEN_VIEW_X_MARGIN,
bottom: this.layoutBounds.maxY - CAVConstants.SCREEN_VIEW_Y_MARGIN,
@@ -207,7 +111,7 @@
// Interrupt dragging of existing objects
this.interruptSubtreeInput();
- model.clearData();
+ model.selectedSceneModelProperty.value.clearData();
// hide the dragIndicatorArrowNode but don't reset objectHasBeenDragged
dragIndicatorArrowNode.visible = false;
@@ -222,10 +126,13 @@
this.contentLayer.addChild( new BackgroundNode( GROUND_POSITION_Y, this.visibleBoundsProperty ) );
this.playAreaNumberLineNode = new NumberLineNode(
- model.physicalRange,
- model.meanValueProperty,
+ new DynamicProperty( model.selectedSceneModelProperty, {
+ derive: 'meanValueProperty'
+ } ),
model.isShowingPlayAreaMeanProperty,
- model.dataRangeProperty, {
+ new DynamicProperty( model.selectedSceneModelProperty, {
+ derive: 'dataRangeProperty'
+ } ), {
includeXAxis: false,
includeMeanStroke: true,
tandem: options.tandem.createTandem( 'playAreaNumberLineNode' ),
@@ -234,10 +141,6 @@
} );
this.contentLayer.addChild( this.playAreaNumberLineNode );
- const soccerPlayerNodes = model.soccerPlayers.map( soccerPlayer => new SoccerPlayerNode( soccerPlayer, this.modelViewTransform ) );
-
- soccerPlayerNodes.forEach( soccerPlayerNode => this.contentLayer.addChild( soccerPlayerNode ) );
-
this.questionBar = new QuestionBar( this.layoutBounds, this.visibleBoundsProperty, merge( {
tandem: options.tandem.createTandem( 'questionBar' )
}, options.questionBarOptions ) );
@@ -277,7 +180,6 @@
protected setAccordionBox( accordionBox: CAVAccordionBox ): void {
this.accordionBox = accordionBox;
this.contentLayer.addChild( this.accordionBox );
- this.accordionBox.expandedProperty.link( this.updateMedianNode );
this.updateAccordionBoxPosition();
}
@@ -319,13 +221,13 @@
/**
* The MedianPredictionNode is shared in the Median screen and MeanAndMedianScreen, so factored out here.
*/
- public static createMedianPredictionNode( model: CAVSceneModel, modelViewTransform: ModelViewTransform2, tandem: Tandem ): PredictionSlider {
- return new PredictionSlider( model.medianPredictionProperty, modelViewTransform, model.physicalRange, {
+ public static createMedianPredictionNode( model: CAVModel, modelViewTransform: ModelViewTransform2, tandem: Tandem ): PredictionSlider {
+ return new PredictionSlider( model.medianPredictionProperty, modelViewTransform, CAVConstants.PHYSICAL_RANGE, {
predictionThumbNodeOptions: {
color: CAVColors.medianColorProperty
},
valueProperty: model.medianPredictionProperty,
- enabledRangeProperty: new Property<Range>( model.physicalRange ),
+ enabledRangeProperty: new Property<Range>( CAVConstants.PHYSICAL_RANGE ),
roundToInterval: 0.5,
visibleProperty: model.isShowingMedianPredictionProperty,
tandem: tandem
Index: js/median/model/MedianModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/median/model/MedianModel.ts b/js/median/model/MedianModel.ts
--- a/js/median/model/MedianModel.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/median/model/MedianModel.ts (date 1683132772086)
@@ -10,16 +10,24 @@
import centerAndVariability from '../../centerAndVariability.js';
import CardModel from '../../median/model/CardModel.js';
import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
-import CAVSceneModel, { CAVModelOptions } from '../../common/model/CAVSceneModel.js';
+import CAVSceneModel from '../../common/model/CAVSceneModel.js';
+import CAVModel from '../../common/model/CAVModel.js';
+import Tandem from '../../../../tandem/js/Tandem.js';
+import Property from '../../../../axon/js/Property.js';
-export default class MedianModel extends CAVSceneModel {
+export default class MedianModel extends CAVModel {
public readonly cards: CardModel[];
public readonly isSortingDataProperty: BooleanProperty;
- public constructor( options: CAVModelOptions ) {
- super( options );
- this.cards = this.soccerBalls.map( ( soccerBall, index ) => new CardModel( soccerBall, {
+ public constructor( options: { tandem: Tandem } ) {
+ const scene = new CAVSceneModel( { tandem: options.tandem.createTandem( 'sceneModel' ) } );
+ super( [ scene ], {
+ ...options,
+ instrumentMeanPredictionProperty: false
+ } );
+
+ this.cards = this.sceneModels[ 0 ].soccerBalls.map( ( soccerBall, index ) => new CardModel( soccerBall, {
tandem: options.tandem.createTandem( 'cards' ).createTandem( 'card' + index )
} ) );
Index: js/common/view/KickButtonGroup.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/KickButtonGroup.ts b/js/common/view/KickButtonGroup.ts
--- a/js/common/view/KickButtonGroup.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/common/view/KickButtonGroup.ts (date 1683134549811)
@@ -20,6 +20,9 @@
import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import PatternStringProperty from '../../../../axon/js/PatternStringProperty.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
+import CAVModel from '../model/CAVModel.js';
+import DynamicProperty from '../../../../axon/js/DynamicProperty.js';
+import CAVScreenView from './CAVScreenView.js';
import CAVSceneModel from '../model/CAVSceneModel.js';
type SelfOptions = EmptySelfOptions;
@@ -30,7 +33,7 @@
export default class KickButtonGroup extends VBox {
- public constructor( model: CAVSceneModel, providedOptions?: KickButtonGroupOptions ) {
+ public constructor( model: CAVModel, providedOptions?: KickButtonGroupOptions ) {
const options = optionize<KickButtonGroupOptions, SelfOptions, VBoxOptions>()( {
spacing: 2
@@ -55,14 +58,26 @@
tandem: tandem.createTandem( 'visibleProperty' )
} );
+
+ const hasKickableSoccerBallsProperty = new DynamicProperty<boolean, unknown, CAVSceneModel>( model.selectedSceneModelProperty, {
+
+ // @ts-expect-error why???
+ derive: 'hasKickableSoccerBallsProperty'
+ } );
+
+ // const hasKickableSoccerBallsProperty =
+ // DerivedProperty.deriveAny( [ model.selectedSceneModelProperty, ...model.sceneModels.map( scene => scene.hasKickableSoccerBallsProperty ) ], () => {
+ // return model.selectedSceneModelProperty.value.hasKickableSoccerBallsProperty.value;
+ // } );
+
return new RectangularPushButton( {
- visibleProperty: DerivedProperty.and( [ model.hasKickableSoccerBallsProperty, buttonVisibleProperty ] ),
+ visibleProperty: DerivedProperty.and( [ hasKickableSoccerBallsProperty, buttonVisibleProperty ] ),
content: content.label,
baseColor: CAVColors.kickButtonFillColorProperty,
xMargin: 12,
yMargin: 12,
tandem: tandem,
- listener: () => model.scheduleKicks( numberToKick ),
+ listener: () => model.selectedSceneModelProperty.value.scheduleKicks( numberToKick ),
// The Kick 1 button can be held down for repeat kicks, but the Kick 5 cannot.
fireOnHold: !multikick,
@@ -84,7 +99,11 @@
const multiKickProperty = new NumberProperty( 5 );
const kick5PatternStringProperty = new PatternStringProperty( CenterAndVariabilityStrings.kickValuePatternStringProperty, { value: multiKickProperty } );
- model.numberOfUnkickedBallsProperty.link( numberOfRemainingKickableObjects => {
+
+ const numberOfUnkickedBallsProperty = new DynamicProperty<number, unknown, CAVSceneModel>( model.selectedSceneModelProperty, {
+ derive: 'numberOfUnkickedBallsProperty'
+ } );
+ numberOfUnkickedBallsProperty.link( numberOfRemainingKickableObjects => {
const value = Math.max( Math.min( numberOfRemainingKickableObjects, 5 ), 1 );
multiKickProperty.value = value;
} );
Index: js/variability/model/VariabilityModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/variability/model/VariabilityModel.ts b/js/variability/model/VariabilityModel.ts
--- a/js/variability/model/VariabilityModel.ts (revision 48bf60f648b3caa21e68cefb5a1138c9cf8f50db)
+++ b/js/variability/model/VariabilityModel.ts (date 1683132314125)
@@ -8,25 +8,21 @@
*/
import centerAndVariability from '../../centerAndVariability.js';
-import DistributionType from './DistributionType.js';
import Property from '../../../../axon/js/Property.js';
import EnumerationProperty from '../../../../axon/js/EnumerationProperty.js';
import { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import VariabilityMeasure from './VariabilityMeasure.js';
import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
-import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
-import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
-import NullableIO from '../../../../tandem/js/types/NullableIO.js';
-import NumberIO from '../../../../tandem/js/types/NumberIO.js';
import CAVSceneModel, { CAVModelOptions } from '../../common/model/CAVSceneModel.js';
+import CAVModel from '../../common/model/CAVModel.js';
+import VariabilitySceneModel from './VariabilitySceneModel.js';
type SelfOptions = EmptySelfOptions;
type VariabilityModelOptions = SelfOptions & CAVModelOptions;
-export default class VariabilityModel extends CAVSceneModel {
+export default class VariabilityModel extends CAVModel {
private readonly initialized: boolean = false;
- public readonly selectedDistributionProperty: Property<DistributionType>;
public readonly selectedVariabilityProperty: Property<VariabilityMeasure>;
public readonly isShowingRangeProperty: Property<boolean>;
public readonly isShowingIQRProperty: Property<boolean>;
@@ -34,22 +30,33 @@
public readonly isInfoShowingProperty: Property<boolean>;
public readonly isShowingPlayAreaVariabilityProperty: BooleanProperty;
- public readonly maxValueProperty: TReadOnlyProperty<number | null>;
- public readonly minValueProperty: TReadOnlyProperty<number | null>;
- public readonly rangeValueProperty: TReadOnlyProperty<number | null>;
- public readonly q1ValueProperty: Property<number | null>;
- public readonly q3ValueProperty: Property<number | null>;
- public readonly iqrValueProperty: TReadOnlyProperty<number | null>;
- public readonly madValueProperty: Property<number | null>;
+ public readonly sceneModels: CAVSceneModel[];
public constructor( options: VariabilityModelOptions ) {
- super( options );
+
+ const sceneModels = [
+
+ // TODO: Each of these should have its own distribution
+ new VariabilitySceneModel( { tandem: options.tandem.createTandem( 'sceneModel1' ) } ),
+ new VariabilitySceneModel( { tandem: options.tandem.createTandem( 'sceneModel2' ) } ),
+ new VariabilitySceneModel( { tandem: options.tandem.createTandem( 'sceneModel3' ) } ),
+ new VariabilitySceneModel( { tandem: options.tandem.createTandem( 'sceneModel4' ) } )
+ ];
+ super( sceneModels[ 0 ], options );
this.initialized = true;
- this.selectedDistributionProperty = new EnumerationProperty( DistributionType.KICKER_1, {
- tandem: options.tandem.createTandem( 'selectedDistributionProperty' )
- } );
+ // this.selectedDistributionProperty.link( distribution => {
+ //
+ // // TODO: the parent class sets this incorrectly on reset.
+ // // TODO: PhET-iO wants to be able to set these values. Maybe in the preferences, we would also add a "custom"
+ // // option that would allow the user to specify the distribution parameters. Or for PhET-iO, and query parameters
+ // this.distributionProperty.value =
+ // distribution === DistributionType.KICKER_1 ? [ 0, 0, 0, 1, 3, 10, 18, 20, 18, 10, 3, 1, 0, 0, 0 ] :
+ // distribution === DistributionType.KICKER_2 ? [ 5, 5, 10, 10, 25, 30, 40, 50, 40, 30, 25, 10, 10, 5, 5 ] :
+ // distribution === DistributionType.KICKER_3 ? [ 6, 9, 11, 14, 11, 8, 6, 5, 5, 5, 5, 5, 5, 5, 5 ] :
+ // /*KICKER_4*/ [ 5, 5, 5, 5, 5, 5, 5, 5, 6, 8, 11, 14, 11, 9, 6 ];
+ // } );
this.selectedVariabilityProperty = new EnumerationProperty( VariabilityMeasure.RANGE, {
tandem: options.tandem.createTandem( 'selectedVariabilityProperty' )
@@ -71,74 +78,22 @@
tandem: options.tandem.createTandem( 'isInfoShowingProperty' )
} );
- this.maxValueProperty = new DerivedProperty( [ this.dataRangeProperty ], dataRange => {
- return dataRange === null ? null : dataRange.max;
- }, {
- tandem: options.tandem.createTandem( 'maxValueProperty' ),
- phetioValueType: NullableIO( NumberIO )
- } );
- this.minValueProperty = new DerivedProperty( [ this.dataRangeProperty ], dataRange => {
- return dataRange === null ? null : dataRange.min;
- }, {
- tandem: options.tandem.createTandem( 'minValueProperty' ),
- phetioValueType: NullableIO( NumberIO )
- } );
-
- this.rangeValueProperty = new DerivedProperty( [ this.maxValueProperty, this.minValueProperty ], ( max, min ) => {
- return ( max === null || min === null ) ? null : max - min;
- }, {
- tandem: options.tandem.createTandem( 'rangeValueProperty' ),
- phetioValueType: NullableIO( NumberIO )
- } );
-
-
- this.q1ValueProperty = new Property<number | null>( null, {
- tandem: options.tandem.createTandem( 'q1ValueProperty' ),
- phetioValueType: NullableIO( NumberIO ),
- phetioReadOnly: true
- } );
- this.q3ValueProperty = new Property<number | null>( null, {
- tandem: options.tandem.createTandem( 'q3ValueProperty' ),
- phetioValueType: NullableIO( NumberIO ),
- phetioReadOnly: true
- } );
- this.iqrValueProperty = new DerivedProperty( [ this.q1ValueProperty, this.q3ValueProperty ], ( q1, q3 ) => {
- return q3! - q1!;
- } );
-
- this.madValueProperty = new Property<number | null>( null, {
- tandem: options.tandem.createTandem( 'madValueProperty' ),
- phetioValueType: NullableIO( NumberIO )
- } );
this.isShowingPlayAreaVariabilityProperty = new BooleanProperty( false, {
tandem: options.tandem.createTandem( 'isShowingPlayAreaVariabilityProperty' )
} );
- this.selectedDistributionProperty.link( distribution => {
-
- // TODO: the parent class sets this incorrectly on reset.
- // TODO: PhET-iO wants to be able to set these values. Maybe in the preferences, we would also add a "custom"
- // option that would allow the user to specify the distribution parameters. Or for PhET-iO, and query parameters
- this.distributionProperty.value =
- distribution === DistributionType.KICKER_1 ? [ 0, 0, 0, 1, 3, 10, 18, 20, 18, 10, 3, 1, 0, 0, 0 ] :
- distribution === DistributionType.KICKER_2 ? [ 5, 5, 10, 10, 25, 30, 40, 50, 40, 30, 25, 10, 10, 5, 5 ] :
- distribution === DistributionType.KICKER_3 ? [ 6, 9, 11, 14, 11, 8, 6, 5, 5, 5, 5, 5, 5, 5, 5 ] :
- /*KICKER_4*/ [ 5, 5, 5, 5, 5, 5, 5, 5, 6, 8, 11, 14, 11, 9, 6 ];
- } );
-
this.updateDataMeasures();
}
public override reset(): void {
super.reset();
- this.selectedDistributionProperty.reset();
+
this.selectedVariabilityProperty.reset();
this.isShowingRangeProperty.reset();
this.isShowingIQRProperty.reset();
this.isShowingMADProperty.reset();
this.isInfoShowingProperty.reset();
- this.madValueProperty.reset();
this.updateDataMeasures();
}
|
The main work for this issue is complete. But let's review and also check @kathy-phet comments in #164 (comment) to see if there is more to do. |
I opened an issue specifically for the PhET-iO design for scenes and @kathy-phet's comment above. #193 Let's complete this issue with a code review. |
// 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 );
}
...
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();
}
} );
} and I agree with the TODO, that a lot of that info should be in the model. Type casting the model, as well as checking the type of the model seems brittle to me, and very tricky. Perhaps this work can be moved out to #153, but it does feel related to the scene separation that was happening in this issue, and worth mentioning. Otherwise the scenes seem to be working properly overall, and nothing else stood out as problematic or concerning in the changes. Back to @samreid. |
Just pushed the TODOs up. Sorry for that delay. |
Addressed TODO. Closing as completed. |
From today's design meeting: On the variability screen, each player sets a different "scene" and remembers its data
The text was updated successfully, but these errors were encountered: