Skip to content

Commit

Permalink
Converting DerivedProperty/Multilink to properly parameterized TypeSc…
Browse files Browse the repository at this point in the history
…ript, and usage of IReadOnlyProperty. See phetsims/axon#371
  • Loading branch information
jonathanolson committed Dec 8, 2021
1 parent 55a812a commit e28ac3f
Show file tree
Hide file tree
Showing 16 changed files with 44 additions and 35 deletions.
11 changes: 6 additions & 5 deletions js/common/model/RAPModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import RAPRatio from './RAPRatio.js';
import RatioTerm from './RatioTerm.js';
import Tandem from '../../../../tandem/js/Tandem.js';
import RAPRatioTuple from './RAPRatioTuple.js';
import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';

// constant to help achieve feedback in 40% of the visual screen height (2 default tick marks). Calculated by taking the
// fitness distance when the right hand is 2 tick marks from the target ratio. This number is based on a target ratio of
Expand All @@ -38,9 +39,9 @@ class RAPModel {

ratio: RAPRatio;
targetRatioProperty: NumberProperty;
unclampedFitnessProperty: DerivedProperty<number>;
ratioFitnessProperty: DerivedProperty<number>;
inProportionProperty: DerivedProperty<boolean>;
unclampedFitnessProperty: IReadOnlyProperty<number>;
ratioFitnessProperty: IReadOnlyProperty<number>;
inProportionProperty: IReadOnlyProperty<boolean>;

public constructor( tandem: Tandem ) {

Expand All @@ -65,7 +66,7 @@ class RAPModel {
this.ratio.tupleProperty,
this.targetRatioProperty,
this.ratio.movingInDirectionProperty
], ( ratioTuple: RAPRatioTuple, ratio: number ) => {
], ( ratioTuple: RAPRatioTuple, ratio: number, movingInDirection: boolean ) => {

const antecedent = ratioTuple.antecedent;
const consequent = ratioTuple.consequent;
Expand Down Expand Up @@ -117,7 +118,7 @@ unclampedFitness: ${unclampedFitness}
} );

// @public - whether or not the model is in its "in proportion" state.
this.inProportionProperty = new DerivedProperty( [
this.inProportionProperty = new DerivedProperty<boolean, [ number, boolean ]>( [
this.unclampedFitnessProperty,
this.ratio.movingInDirectionProperty
], this.inProportion.bind( this ), {
Expand Down
3 changes: 2 additions & 1 deletion js/common/model/RAPRatio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ratioAndProportion from '../../ratioAndProportion.js';
import rapConstants from '../rapConstants.js';
import RAPRatioTuple from './RAPRatioTuple.js';
import Tandem from '../../../../tandem/js/Tandem.js';
import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';

// The threshold for velocity of a moving ratio value to indicate that it is "moving."
const VELOCITY_THRESHOLD = 0.01;
Expand All @@ -41,7 +42,7 @@ class RAPRatio {
lockedProperty: BooleanProperty;
antecedentVelocityTracker: VelocityTracker;
consequentVelocityTracker: VelocityTracker;
movingInDirectionProperty: DerivedProperty<boolean>;
movingInDirectionProperty: IReadOnlyProperty<boolean>;
ratioLockListenerEnabled: boolean;


Expand Down
2 changes: 1 addition & 1 deletion js/common/view/BothHandsPDOMNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ class BothHandsPDOMNode extends Node {
options.tickMarkRangeProperty,
options.ratioTupleProperty,
options.unclampedFitnessProperty
], () => {
], ( ...args: any[] ) => {

dynamicDescription.innerContent = this.bothHandsDescriber.getBothHandsDynamicDescription();

Expand Down
2 changes: 1 addition & 1 deletion js/common/view/RAPScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class RAPScreenView extends ScreenView {
// A collection of properties that keep track of which cues should be displayed for both the antecedent and consequent hands.
const cueArrowsState = new CueArrowsState();

const tickMarksAndLabelsColorProperty = new DerivedProperty<Color | string>( [ model.ratioFitnessProperty ],
const tickMarksAndLabelsColorProperty = new DerivedProperty( [ model.ratioFitnessProperty ],
( fitness: number ) => Color.interpolateRGBA(
RAPColors.tickMarksAndLabelsOutOfFitnessProperty.value,
RAPColors.tickMarksAndLabelsInFitnessProperty.value, fitness
Expand Down
7 changes: 4 additions & 3 deletions js/common/view/RAPTickMarkLabelsNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @author Michael Kauzmann (PhET Interactive Simulations)
*/

import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';
import Property from '../../../../axon/js/Property.js';
import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
import { Color, Node, NodeOptions, Text } from '../../../../scenery/js/imports.js';
Expand All @@ -20,7 +21,7 @@ class RAPTickMarkLabelsNode extends Node {
private heightOfText: number | null;
private tickMarkViewProperty: Property<TickMarkView>;
private tickMarkRangeProperty: Property<number>;
private colorProperty: Property<Color | string>;
private colorProperty: IReadOnlyProperty<Color | string>;

/**
* @param {Property.<TickMarkView>} tickMarkViewProperty
Expand All @@ -30,7 +31,7 @@ class RAPTickMarkLabelsNode extends Node {
* @param {Object} [options]
*/
constructor( tickMarkViewProperty: Property<TickMarkView>, tickMarkRangeProperty: Property<number>, height: number,
colorProperty: Property<Color | string>, options?: Omit<NodeOptions, 'children'> ) {
colorProperty: IReadOnlyProperty<Color | string>, options?: Omit<NodeOptions, 'children'> ) {

super();

Expand All @@ -47,7 +48,7 @@ class RAPTickMarkLabelsNode extends Node {

this.mutate( options );

Property.multilink( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) );
Property.multilink<any[]>( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) );
}

/**
Expand Down
7 changes: 4 additions & 3 deletions js/common/view/RatioHalf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import HandPositionsDescriber from './describers/HandPositionsDescriber.js';
import RAPRatioTuple from '../model/RAPRatioTuple.js';
import CueArrowsState from './CueArrowsState.js';
import RatioDescriber from './describers/RatioDescriber.js';
import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';

// constants
const MIN_FRAMING_RECTANGLE_HEIGHT = 32;
Expand Down Expand Up @@ -86,12 +87,12 @@ type RatioHalfDefinedOptions = {
playTickMarkBumpSoundProperty: Property<boolean>;
setJumpingOverProportionShouldTriggerSound: ( shouldTriggerSound: boolean ) => void;
getIdealValue: () => number;
inProportionProperty: Property<boolean>;
inProportionProperty: IReadOnlyProperty<boolean>;
isRight?: boolean;

// TODO: Why is lint wrong here? https://github.com/phetsims/ratio-and-proportion/issues/404
handColorProperty?: Property<ColorDef>; // eslint-disable-line no-undef
a11yDependencies?: Property<any>[];
a11yDependencies?: IReadOnlyProperty<any>[];
bothHandsCueDisplay?: CueDisplay;
}

Expand Down Expand Up @@ -229,7 +230,7 @@ class RatioHalf extends Rectangle {
const viewSounds = new ViewSounds( options.tickMarkRangeProperty, options.tickMarkViewProperty, options.playTickMarkBumpSoundProperty );

// This follows the spec outlined in https://github.com/phetsims/ratio-and-proportion/issues/81
const cueDisplayStateProperty: DerivedProperty<CueDisplay> = new DerivedProperty( [
const cueDisplayStateProperty: IReadOnlyProperty<CueDisplay> = new DerivedProperty( [
options.cueArrowsState.interactedWithKeyboardProperty,
options.cueArrowsState.interactedWithMouseProperty,
options.cueArrowsState.keyboardFocusedProperty,
Expand Down
2 changes: 1 addition & 1 deletion js/common/view/RatioHalfTickMarksNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class RatioHalfTickMarksNode extends GridNode {
this.tickMarkViewProperty = tickMarkViewProperty;
this.tickMarkRangeProperty = tickMarkRangeProperty;

Property.multilink( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) );
Property.multilink<any[]>( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) );
}

/**
Expand Down
7 changes: 4 additions & 3 deletions js/common/view/RatioHandNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import getKeyboardInputSnappingMapper from './getKeyboardInputSnappingMapper.js'
import RAPColors from './RAPColors.js';
import TickMarkView from './TickMarkView.js';
import ratioAndProportionStrings from '../../ratioAndProportionStrings.js';
import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';

type CreateIconOptions = {

Expand Down Expand Up @@ -53,9 +54,9 @@ class RatioHandNode extends Node {
tickMarkViewProperty: Property<TickMarkView>,
keyboardStep: number,
colorProperty: Property<Color | string>,
cueDisplayProperty: Property<CueDisplay>,
cueDisplayProperty: IReadOnlyProperty<CueDisplay>,
getIdealValue: () => number,
inProportionProperty: Property<boolean>,
inProportionProperty: IReadOnlyProperty<boolean>,
options?: any ) {

const shiftKeyboardStep = rapConstants.toFixed( keyboardStep * rapConstants.SHIFT_KEY_MULTIPLIER ); // eslint-disable-line bad-sim-text
Expand Down Expand Up @@ -91,7 +92,7 @@ class RatioHandNode extends Node {
// @ts-ignore
this.initializeVoicing( options );

Property.multilink( [ valueProperty ].concat( options.a11yDependencies ), () => {
Property.multilink<any[]>( [ valueProperty ].concat( options.a11yDependencies ), () => {

// @ts-ignore
this.voicingObjectResponse = options.voicingCreateObjectResponse();
Expand Down
6 changes: 3 additions & 3 deletions js/common/view/describers/RatioDescriber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import sceneryPhetStrings from '../../../../../scenery-phet/js/sceneryPhetString
import ratioAndProportion from '../../../ratioAndProportion.js';
import ratioAndProportionStrings from '../../../ratioAndProportionStrings.js';
import rapConstants from '../../rapConstants.js';
import Property from '../../../../../axon/js/Property.js';
import RAPModel from '../../model/RAPModel.js';
import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js';

// constants
const RATIO_FITNESS_STRINGS_CAPITALIZED = [
Expand Down Expand Up @@ -58,8 +58,8 @@ assert && assert( RATIO_FITNESS_STRINGS_LOWERCASE.length === RATIO_FITNESS_STRIN

class RatioDescriber {

private ratioFitnessProperty: Property<number>;
private unclampedFitnessProperty: Property<number>;
private ratioFitnessProperty: IReadOnlyProperty<number>;
private unclampedFitnessProperty: IReadOnlyProperty<number>;
private model: RAPModel;

/**
Expand Down
5 changes: 3 additions & 2 deletions js/common/view/sound/InProportionSoundGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import inProportionSound from '../../../../sounds/in-proportion/in-proportion_mp
import ratioAndProportion from '../../../ratioAndProportion.js';
import RAPModel from '../../model/RAPModel.js';
import Property from '../../../../../axon/js/Property.js';
import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js';

const SUCCESS_OUTPUT_LEVEL = 0.8;
const SILENT_LEVEL = 0;
Expand All @@ -33,7 +34,7 @@ class InProportionSoundGenerator extends SoundClip {

private model: RAPModel;
private targetRatioProperty: Property<number>;
private fitnessProperty: Property<number>;
private fitnessProperty: IReadOnlyProperty<number>;
private playedSuccessYetProperty: Property<boolean>;
private timePlayedSoFarProperty: Property<number>;
private previousRatioWasLargerThanTarget: boolean;
Expand Down Expand Up @@ -69,7 +70,7 @@ class InProportionSoundGenerator extends SoundClip {
// of this sound is always played, even when the outside enabledControlProperty is set to false.
this.timePlayedSoFarProperty = new NumberProperty( MANDATORY_PLAY_TIME );

const playedMandatoryPortionYetProperty: DerivedProperty<boolean> = new DerivedProperty( [ this.timePlayedSoFarProperty, this.playedSuccessYetProperty ],
const playedMandatoryPortionYetProperty: IReadOnlyProperty<boolean> = new DerivedProperty( [ this.timePlayedSoFarProperty, this.playedSuccessYetProperty ],
( timePlayed: number, playedSuccessYet: boolean ) => playedSuccessYet && timePlayed <= MANDATORY_PLAY_TIME );

// In addition to any supplemental enabledControlProperty that the client wants to pass in, make sure to set up
Expand Down
3 changes: 2 additions & 1 deletion js/common/view/sound/MovingInProportionSoundGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import choirLoopSound from '../../../../sounds/moving-in-proportion/moving-in-pr
import movingInProportionOrganLoopSound from '../../../../sounds/moving-in-proportion/moving-in-proportion-organ-loop_mp3.js';
import ratioAndProportion from '../../../ratioAndProportion.js';
import RAPModel from '../../model/RAPModel.js';
import RAPRatioTuple from '../../model/RAPRatioTuple.js';

class MovingInProportionSoundGenerator extends SoundGenerator {

Expand Down Expand Up @@ -52,7 +53,7 @@ class MovingInProportionSoundGenerator extends SoundGenerator {
model.inProportionProperty,
model.ratio.tupleProperty,
model.ratioFitnessProperty
], ( movingInDirection: boolean, inProportion: boolean ) => {
], ( movingInDirection: boolean, inProportion: boolean, tuple: RAPRatioTuple, ratioFitness: number ) => {
if ( movingInDirection && // only when moving
!model.valuesTooSmallForInProportion() && // no moving in proportion success if too small
inProportion && // must be fit enough to play the moving in proportion success
Expand Down
8 changes: 4 additions & 4 deletions js/common/view/sound/StaccatoFrequencySoundGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import g001Sound from '../../../../sounds/staccato/staccato-g-001_mp3.js';
import g002Sound from '../../../../sounds/staccato/staccato-g-002_mp3.js';
import gSound from '../../../../sounds/staccato/staccato-g_mp3.js';
import ratioAndProportion from '../../../ratioAndProportion.js';
import Property from '../../../../../axon/js/Property.js';
import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js';

// organize the sounds by variation and note
const staccatoSounds = [
Expand All @@ -55,8 +55,8 @@ const staccatoSounds = [
class StaccatoFrequencySoundGenerator extends SoundGenerator {


private inProportionProperty: Property<boolean>;
private fitnessProperty: Property<number>;
private inProportionProperty: IReadOnlyProperty<boolean>;
private fitnessProperty: IReadOnlyProperty<number>;
private staccatoSoundClips: SoundClip[][];
private timeLinearFunction: LinearFunction;
private timeSinceLastPlay: number;
Expand All @@ -67,7 +67,7 @@ class StaccatoFrequencySoundGenerator extends SoundGenerator {
* @param {Property.<boolean>} inProportionProperty - true when the model ratio is in proportion
* @param {Object} [options]
*/
constructor( fitnessProperty: Property<number>, fitnessRange: Range, inProportionProperty: Property<boolean>, options: object ) {
constructor( fitnessProperty: IReadOnlyProperty<number>, fitnessRange: Range, inProportionProperty: IReadOnlyProperty<boolean>, options: object ) {
options = merge( {
initialOutputLevel: 0.25
}, options );
Expand Down
7 changes: 4 additions & 3 deletions js/create/view/CreateScreenSummaryNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ import HandPositionsDescriber from '../../common/view/describers/HandPositionsDe
import MyChallengeAccordionBox from './MyChallengeAccordionBox.js';
import BackgroundColorHandler from '../../common/view/BackgroundColorHandler.js';
import TickMarkView from '../../common/view/TickMarkView.js';
import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';

class CreateScreenSummaryNode extends Node {

constructor( ratioFitnessProperty: Property<number>,
constructor( ratioFitnessProperty: IReadOnlyProperty<number>,
ratioTupleProperty: Property<RAPRatioTuple>,
tickMarkViewProperty: Property<TickMarkView>,
ratioDescriber: RatioDescriber,
inProportionProperty: Property<boolean>,
inProportionProperty: IReadOnlyProperty<boolean>,
handPositionsDescriber: HandPositionsDescriber,
tickMarkRangeProperty: Property<number>,
myChallengeAccordionBox: MyChallengeAccordionBox ) {
Expand Down Expand Up @@ -79,7 +80,7 @@ class CreateScreenSummaryNode extends Node {
inProportionProperty,
tickMarkRangeProperty
], ( tickMarkView: TickMarkView, targetAntecedent: number, targetConsequent: number,
currentTuple: RAPRatioTuple, fitness: number, inProportion: boolean ) => {
currentTuple: RAPRatioTuple, fitness: number, inProportion: boolean, tickMarkRange: number ) => {

stateOfSimNode.innerContent = StringUtils.fillIn( ratioAndProportionStrings.a11y.screenSummaryQualitativeStateOfSim, {
color: BackgroundColorHandler.getCurrentColorRegion( fitness, inProportion ),
Expand Down
2 changes: 1 addition & 1 deletion js/create/view/CreateScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class CreateScreenView extends RAPScreenView {
} ) );

// The "lock ratio" checkbox should not be enabled when the ratio is not in proportion.
Property.multilink( [
Property.multilink<[ boolean, number ]>( [
model.inProportionProperty,
model.ratioFitnessProperty
], ( inProportion: boolean ) => {
Expand Down
2 changes: 1 addition & 1 deletion js/create/view/MyChallengeAccordionBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class MyChallengeAccordionBox extends AccordionBox {
// unlocked, see https://github.com/phetsims/ratio-and-proportion/issues/227 for extensive investigation. NOTE:
// This should be above the creation of the NumberPickers to make sure that this fires before the RAPModel.targetRatioProperty
// changes.
Property.multilink( [ targetAntecedentProperty, targetConsequentProperty ], () => {
Property.multilink<any[]>( [ targetAntecedentProperty, targetConsequentProperty ], () => {

// if currently locked, then it is about to be unlocked
ratioLockedProperty.value && this.alertDescriptionUtterance( ratioUnlockedFromMyChallenge );
Expand Down
5 changes: 3 additions & 2 deletions js/discover/view/DiscoverScreenSummaryNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import HandPositionsDescriber from '../../common/view/describers/HandPositionsDe
import TickMarkView from '../../common/view/TickMarkView.js';
import BackgroundColorHandler from '../../common/view/BackgroundColorHandler.js';
import RatioDescriber from '../../common/view/describers/RatioDescriber.js';
import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';

class DiscoverScreenSummaryNode extends Node {

constructor( ratioFitnessProperty: Property<number>, ratioTupleProperty: Property<RAPRatioTuple>,
constructor( ratioFitnessProperty: IReadOnlyProperty<number>, ratioTupleProperty: Property<RAPRatioTuple>,
targetRatioProperty: Property<number>, tickMarkViewProperty: Property<TickMarkView>,
ratioDescriber: RatioDescriber, inProportionProperty: Property<boolean>, handPositionsDescriber: HandPositionsDescriber,
ratioDescriber: RatioDescriber, inProportionProperty: IReadOnlyProperty<boolean>, handPositionsDescriber: HandPositionsDescriber,
ratioToChallengeNameMap: Map<number, { lowercase: string, capitalized: string }> ) {

const stateOfSimNode = new Node( {
Expand Down

0 comments on commit e28ac3f

Please sign in to comment.