Skip to content

Commit

Permalink
Initial draft of play area interval tool, see #194
Browse files Browse the repository at this point in the history
  • Loading branch information
samreid committed May 11, 2023
1 parent 79dc974 commit d3e186e
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 16 deletions.
4 changes: 2 additions & 2 deletions center-and-variability-strings_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@
"mad": {
"value": "MAD"
},
"variability": {
"value": "Variability"
"intervalTool": {
"value": "Interval Tool"
},
"predictMean": {
"value": "Predict Mean"
Expand Down
2 changes: 1 addition & 1 deletion js/CenterAndVariabilityStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type StringsType = {
'q3StringProperty': LinkableProperty<string>;
'meanAbsoluteDeviationMADStringProperty': LinkableProperty<string>;
'madStringProperty': LinkableProperty<string>;
'variabilityStringProperty': LinkableProperty<string>;
'intervalToolStringProperty': LinkableProperty<string>;
'predictMeanStringProperty': LinkableProperty<string>;
'predictMedianStringProperty': LinkableProperty<string>;
'valueUnknownStringProperty': LinkableProperty<string>;
Expand Down
6 changes: 4 additions & 2 deletions js/common/CAVColors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* @author Sam Reid (PhET Interactive Simulations)
*/

import { ProfileColorProperty } from '../../../scenery/js/imports.js';
import { Color, ProfileColorProperty } from '../../../scenery/js/imports.js';
import centerAndVariability from '../centerAndVariability.js';

const CAVColors = {
Expand Down Expand Up @@ -59,7 +59,9 @@ const CAVColors = {

intervalToolIconShadedSphereMainColorProperty: new ProfileColorProperty( centerAndVariability, 'intervalToolIconShadedSphereMainColor', { default: '#e0d987' } ),
intervalToolIconShadedSphereHighlightColorProperty: new ProfileColorProperty( centerAndVariability, 'intervalToolIconShadedSphereHighlightColor', { default: '#e0d987' } ),
intervalToolIconShadedSphereShadowColorProperty: new ProfileColorProperty( centerAndVariability, 'intervalToolIconShadedSphereShadowColor', { default: '#545034' } )
intervalToolIconShadedSphereShadowColorProperty: new ProfileColorProperty( centerAndVariability, 'intervalToolIconShadedSphereShadowColor', { default: '#545034' } ),
intervalToolFillProperty: new ProfileColorProperty( centerAndVariability, 'intervalToolFillProperty', { default: Color.toColor( '#fefccf' ).withAlpha( 0.6 ) } ),
intervalToolStrokeProperty: new ProfileColorProperty( centerAndVariability, 'intervalToolStrokeProperty', { default: '#eae4c3' } )
};

centerAndVariability.register( 'CAVColors', CAVColors );
Expand Down
6 changes: 3 additions & 3 deletions js/common/view/BottomRepresentationCheckboxGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ export default class BottomRepresentationCheckboxGroup {
} );
}

public static getVariabilityCheckboxItem( alignGroup: AlignGroup, model: VariabilityModel ): VerticalCheckboxGroupItem {
public static getIntervalToolCheckboxItem( alignGroup: AlignGroup, model: VariabilityModel ): VerticalCheckboxGroupItem {
return {
createNode: ( tandem: Tandem ) => {
return BottomRepresentationCheckboxGroup.createGridBox(
new Text( CenterAndVariabilityStrings.variabilityStringProperty, TEXT_OPTIONS ),
new Text( CenterAndVariabilityStrings.intervalToolStringProperty, TEXT_OPTIONS ),
new IntervalToolIconNode(),
alignGroup
);
},
property: model.isShowingPlayAreaVariabilityProperty,
property: model.isShowingIntervalToolProperty,
tandemName: 'variabilityCheckbox'
};
}
Expand Down
7 changes: 5 additions & 2 deletions js/common/view/CAVScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ export default class CAVScreenView extends ScreenView {
protected readonly modelViewTransform: ModelViewTransform2;
protected readonly model: CAVModel;

protected readonly intervalToolLayer = new Node();

// TODO: We haven't enforced the "exactly half a ball should be occluded if anything is occluded" design, see https://github.com/phetsims/center-and-variability/issues/175
protected readonly frontObjectLayer = new Node();
protected readonly backObjectLayer = new Node();
protected readonly frontObjectLayer = new Node();

protected readonly eraseButton: EraserButton;

// Subclasses use this to add to for correct z-ordering and correct tab navigation order
// Subclasses add to the contentLayer for correct z-ordering and correct tab navigation order
protected readonly contentLayer = new Node();

protected accordionBox: CAVAccordionBox | null = null;
Expand Down Expand Up @@ -87,6 +89,7 @@ export default class CAVScreenView extends ScreenView {
this.addChild( this.contentLayer );

this.contentLayer.addChild( new BackgroundNode( GROUND_POSITION_Y, this.visibleBoundsProperty ) );
this.contentLayer.addChild( this.intervalToolLayer );

// Soccer balls go behind the accordion box after they land
this.contentLayer.addChild( this.backObjectLayer );
Expand Down
2 changes: 1 addition & 1 deletion js/common/view/PredictionThumbNode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2023, University of Colorado Boulder
/**
* The thumb node for the PreditionSlider. Is made up of an Arrow and ShadedSphere.
* The thumb node for the PredictionSlider. Is made up of an Arrow and ShadedSphere.
*
* @author Marla Schulz (PhET Interactive Simulations)
*
Expand Down
16 changes: 13 additions & 3 deletions js/variability/model/VariabilityModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export default class VariabilityModel extends CAVModel {
public readonly isShowingIQRProperty: Property<boolean>;
public readonly isShowingMADProperty: Property<boolean>;
public readonly isInfoShowingProperty: Property<boolean>;
public readonly isShowingPlayAreaVariabilityProperty: BooleanProperty;
public readonly isShowingIntervalToolProperty: BooleanProperty;
public readonly intervalTool1ValueProperty: NumberProperty;
public readonly intervalTool2ValueProperty: NumberProperty;

public readonly resetEmitter = new Emitter();
public readonly variabilitySceneModels: VariabilitySceneModel[];
Expand Down Expand Up @@ -76,9 +78,17 @@ export default class VariabilityModel extends CAVModel {
tandem: options.tandem.createTandem( 'isInfoShowingProperty' )
} );

this.isShowingIntervalToolProperty = new BooleanProperty( false, {
tandem: options.tandem.createTandem( 'isShowingIntervalToolProperty' )
} );

this.isShowingPlayAreaVariabilityProperty = new BooleanProperty( false, {
tandem: options.tandem.createTandem( 'isShowingPlayAreaVariabilityProperty' )
this.intervalTool1ValueProperty = new NumberProperty( 2, {
range: CAVConstants.PHYSICAL_RANGE,
tandem: options.tandem.createTandem( 'intervalTool1ValueProperty' )
} );
this.intervalTool2ValueProperty = new NumberProperty( 3, {
range: CAVConstants.PHYSICAL_RANGE,
tandem: options.tandem.createTandem( 'intervalTool2ValueProperty' )
} );
}

Expand Down
49 changes: 49 additions & 0 deletions js/variability/view/IntervalToolPlayAreaNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2023, University of Colorado Boulder

import centerAndVariability from '../../centerAndVariability.js';
import { Rectangle, Node, Line } from '../../../../scenery/js/imports.js';
import CAVColors from '../../common/CAVColors.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import Multilink from '../../../../axon/js/Multilink.js';
import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js';
import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';

export default class IntervalToolPlayAreaNode extends Node {
public constructor( intervalToolValue1Property: NumberProperty, intervalToolValue2Property: NumberProperty, modelViewTransform: ModelViewTransform2,
topAlignmentBoundsProperty: TReadOnlyProperty<Bounds2> ) {

const rectangleNode = new Rectangle( 0, 0, 0, 400, {
fill: CAVColors.intervalToolFillProperty,
stroke: CAVColors.intervalToolStrokeProperty,
cursor: 'pointer'
} );
const leftEdge = new Line( 0, 0, 0, 400, {
stroke: CAVColors.intervalToolStrokeProperty
} );
const rightEdge = new Line( 0, 0, 0, 400, {
stroke: CAVColors.intervalToolStrokeProperty
} );
super( {
children: [
rectangleNode,
leftEdge,
rightEdge
]
} );

Multilink.multilink( [ intervalToolValue1Property, intervalToolValue2Property, topAlignmentBoundsProperty ], ( value1, value2, topAlignmentBounds ) => {
const viewX1 = modelViewTransform.modelToViewX( value1 );
const viewX2 = modelViewTransform.modelToViewX( value2 );
const rectBottom = modelViewTransform.modelToViewY( 0 );
const rectTop = topAlignmentBounds.top + 1;
const rectHeight = rectBottom - rectTop;
rectangleNode.setRect( Math.min( viewX1, viewX2 ), rectBottom - rectHeight, Math.abs( viewX2 - viewX1 ), rectHeight );

leftEdge.setLine( viewX1, rectBottom, viewX1, rectTop );
rightEdge.setLine( viewX2, rectBottom, viewX2, rectTop );
} );
}
}

centerAndVariability.register( 'IntervalToolPlayAreaNode', IntervalToolPlayAreaNode );
37 changes: 35 additions & 2 deletions js/variability/view/VariabilityScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import SoccerPlayerNode, { SoccerPlayerImageSet } from '../../common/view/Soccer
import CAVSceneModel from '../../common/model/CAVSceneModel.js';
import InfoDialog from './InfoDialog.js';
import Multilink from '../../../../axon/js/Multilink.js';
import PredictionSlider from '../../common/view/PredictionSlider.js';
import Property from '../../../../axon/js/Property.js';
import Range from '../../../../dot/js/Range.js';
import IntervalToolPlayAreaNode from './IntervalToolPlayAreaNode.js';

type SelfOptions = EmptySelfOptions;
type VariabilityScreenViewOptions = SelfOptions & StrictOmit<CAVScreenViewOptions, 'questionBarOptions'>;
Expand All @@ -50,6 +54,28 @@ export default class VariabilityScreenView extends CAVScreenView {

super( model, options );

this.contentLayer.addChild( new PredictionSlider( model.intervalTool1ValueProperty, this.modelViewTransform, CAVConstants.PHYSICAL_RANGE, {
predictionThumbNodeOptions: {
color: CAVColors.intervalToolIconShadedSphereMainColorProperty
},
valueProperty: model.intervalTool1ValueProperty,
enabledRangeProperty: new Property<Range>( CAVConstants.PHYSICAL_RANGE ),
roundToInterval: null, // continuous
visibleProperty: model.isShowingIntervalToolProperty,
tandem: options.tandem.createTandem( 'variabilityIntervalPredictionTool1ValueNode' )
} ) );

this.contentLayer.addChild( new PredictionSlider( model.intervalTool2ValueProperty, this.modelViewTransform, CAVConstants.PHYSICAL_RANGE, {
predictionThumbNodeOptions: {
color: CAVColors.intervalToolIconShadedSphereMainColorProperty
},
valueProperty: model.intervalTool2ValueProperty,
enabledRangeProperty: new Property<Range>( CAVConstants.PHYSICAL_RANGE ),
roundToInterval: null, // continuous
visibleProperty: model.isShowingIntervalToolProperty,
tandem: options.tandem.createTandem( 'variabilityIntervalPredictionTool2ValueNode' )
} ) );

const variabilityAccordionBox = new VariabilityAccordionBox( model, this.layoutBounds, options.tandem.createTandem( 'accordionBox' ), this.questionBar.bottom + CAVConstants.SCREEN_VIEW_Y_MARGIN );
this.setAccordionBox( variabilityAccordionBox );
variabilityAccordionBox.alignWithPlayAreaNumberLineNode( this.playAreaNumberLineNode.globalBounds.x );
Expand All @@ -59,6 +85,13 @@ export default class VariabilityScreenView extends CAVScreenView {
variabilityRadioButtonGroupWrapper.centerY = accordionBoxWrapper.centerY;
} );

const intervalToolPlayAreaNode = new IntervalToolPlayAreaNode( model.intervalTool1ValueProperty, model.intervalTool2ValueProperty, this.modelViewTransform,
variabilityAccordionBox.boundsProperty );
model.isShowingIntervalToolProperty.link( isShowingPlayAreaVariability => {
intervalToolPlayAreaNode.visible = isShowingPlayAreaVariability;
} );
this.intervalToolLayer.addChild( intervalToolPlayAreaNode );

const sceneRadioButtonGroup = new SceneRadioButtonGroup( model.variabilitySceneModels, model.selectedSceneModelProperty, {
left: 10,
tandem: options.tandem.createTandem( 'sceneRadioButtonGroup' )
Expand All @@ -79,9 +112,9 @@ export default class VariabilityScreenView extends CAVScreenView {
spacing: 15,
children: [
new VerticalCheckboxGroup( [
BottomRepresentationCheckboxGroup.getVariabilityCheckboxItem( iconGroup, model ),
BottomRepresentationCheckboxGroup.getMedianCheckboxItem( iconGroup, model ),
BottomRepresentationCheckboxGroup.getMeanCheckboxItem( iconGroup, model )
BottomRepresentationCheckboxGroup.getMeanCheckboxItem( iconGroup, model ),
BottomRepresentationCheckboxGroup.getIntervalToolCheckboxItem( iconGroup, model )
], {
tandem: this.tandem.createTandem( 'bottomCheckboxGroup' )
} ),
Expand Down

0 comments on commit d3e186e

Please sign in to comment.