diff --git a/js/resistance-in-a-wire/ResistanceInAWireA11yStrings.js b/js/resistance-in-a-wire/ResistanceInAWireA11yStrings.js index 603bf8a..891185d 100644 --- a/js/resistance-in-a-wire/ResistanceInAWireA11yStrings.js +++ b/js/resistance-in-a-wire/ResistanceInAWireA11yStrings.js @@ -27,13 +27,13 @@ define( function( require ) { value: 'resistance, R, is {{value}} ohms', }, summaryResistivityPatternString: { - value: 'resistivity, rho is {{value}} ohm centimeters', + value: 'resistivity, rho, is {{value}} ohm centimeters', }, summaryLengthPatternString: { - value: 'length, L is {{value}} centimeters', + value: 'length, L, is {{value}} centimeters', }, summaryAreaPatternString: { - value: 'area, A is {{value}} centimeters squared', + value: 'area, A, is {{value}} centimeters squared', }, resistanceEquationString: { value: 'Resistance Equation' @@ -163,6 +163,30 @@ define( function( require ) { }, slidersDescriptionString: { value: 'Resistivity, Length, and Area sliders allow changes to equation and wire.' + }, + sizeChangeAlertPatternString: { + value: 'As letter {{letter}} {{letterChange}}, letter R {{rChange}}. Resistance now {{resistance}} ohms.' + }, + letterRhoString: { + value: 'rho' + }, + letterLString: { + value: 'L' + }, + letterAString: { + value: 'A' + }, + growsString: { + value: 'grows' + }, + shrinksString: { + value: 'shrinks' + }, + growsALotString: { + value: 'grows a lot' + }, + shrinksALotString: { + value: 'shrinks a lot' } }; diff --git a/js/resistance-in-a-wire/ResistanceInAWireConstants.js b/js/resistance-in-a-wire/ResistanceInAWireConstants.js index b0b73cb..2960ba1 100644 --- a/js/resistance-in-a-wire/ResistanceInAWireConstants.js +++ b/js/resistance-in-a-wire/ResistanceInAWireConstants.js @@ -14,6 +14,7 @@ define( function( require ) { var Range = require( 'DOT/Range' ); var ResistanceInAWireA11yStrings = require( 'RESISTANCE_IN_A_WIRE/resistance-in-a-wire/ResistanceInAWireA11yStrings' ); var resistanceInAWire = require( 'RESISTANCE_IN_A_WIRE/resistanceInAWire' ); + var Util = require( 'DOT/Util' ); // a11y strings var muchMuchSmallerThanString = ResistanceInAWireA11yStrings.muchMuchSmallerThanString.value; @@ -45,6 +46,7 @@ define( function( require ) { var aVeryLargeAmountOfImpuritiesString = ResistanceInAWireA11yStrings.aVeryLargeAmountOfImpuritiesString.value; var aHugeAmountOfImpuritiesString = ResistanceInAWireA11yStrings.aHugeAmountOfImpuritiesString.value; + // constants var RESISTIVITY_RANGE = new RangeWithValue( 0.01, 1.00, 0.5 ); // in Ohm * cm var LENGTH_RANGE = new RangeWithValue( 0.1, 20, 10 ); // in cm @@ -54,6 +56,8 @@ define( function( require ) { var AREA_DESCRIPTIONS = [ extremelyThinString, veryThinString, thinString, ofMediumThicknessString, thickString, veryThickString, extremelyThickString ]; var RESISTIVITY_DESCRIPTIONS = [ aTinyAmountOfImpuritiesString, aVerySmallAmountOfImpuritiesString, aSmallAmountOfImpuritiesString, aMediumAmountOfImpuritiesString, aLargeAmountOfImpuritiesString, aVeryLargeAmountOfImpuritiesString, aHugeAmountOfImpuritiesString ]; + var RELATIVE_SIZE_STRINGS = [ muchMuchSmallerThanString, muchSmallerThanString, slightlySmallerThanString, comparableToString, slightlyLargerThanString, muchLargerThanString, muchMuchLargerThanString ]; + /** * Generate a map from physical value to accessible descripton. Each described range has a length of * valueRange / descriptionArray.length @@ -138,6 +142,8 @@ define( function( require ) { AREA_TO_DESCRIPTION_MAP: AREA_TO_DESCRIPTION_MAP, RESISTIVITY_TO_DESCRIPTION_MAP: RESISTIVITY_TO_DESCRIPTION_MAP, + RELATIVE_SIZE_STRINGS: RELATIVE_SIZE_STRINGS, + // a11y - used to map relative scale magnitudes of the letters to relative size description RELATIVE_SIZE_MAP: { @@ -197,8 +203,18 @@ define( function( require ) { return entry.description; } } - } + }, + /** + * Get a formatted value for resistance - depending on size of resistance, number of decimals will change. Used + * for visual readout as well as for readable values in a11y. + * + * @param {number} value + * @return {string} + */ + getFormattedResistanceValue: function( value ) { + return Util.toFixed( value, this.getResistanceDecimals( value ) ); + } }; resistanceInAWire.register( 'ResistanceInAWireConstants', ResistanceInAWireConstants ); diff --git a/js/resistance-in-a-wire/model/ResistanceInAWireModel.js b/js/resistance-in-a-wire/model/ResistanceInAWireModel.js index 814d4fc..5879bb6 100644 --- a/js/resistance-in-a-wire/model/ResistanceInAWireModel.js +++ b/js/resistance-in-a-wire/model/ResistanceInAWireModel.js @@ -14,6 +14,7 @@ define( function( require ) { var DerivedPropertyIO = require( 'AXON/DerivedPropertyIO' ); var inherit = require( 'PHET_CORE/inherit' ); var NumberProperty = require( 'AXON/NumberProperty' ); + var Range = require( 'DOT/Range' ); var resistanceInAWire = require( 'RESISTANCE_IN_A_WIRE/resistanceInAWire' ); var ResistanceInAWireConstants = require( 'RESISTANCE_IN_A_WIRE/resistance-in-a-wire/ResistanceInAWireConstants' ); @@ -73,5 +74,17 @@ define( function( require ) { this.lengthProperty.reset(); this.areaProperty.reset(); } + }, { + + /** + * Get the total range of the derived resistance from the independent Properties of this model. + * + * @return {Range} + */ + getResistanceRange: function() { + var minResistance = ResistanceInAWireConstants.RESISTIVITY_RANGE.min * ResistanceInAWireConstants.LENGTH_RANGE.min / ResistanceInAWireConstants.AREA_RANGE.min; + var maxResistance = ResistanceInAWireConstants.RESISTIVITY_RANGE.max * ResistanceInAWireConstants.LENGTH_RANGE.max / ResistanceInAWireConstants.AREA_RANGE.max; + return new Range( minResistance, maxResistance ); + } } ); } ); \ No newline at end of file diff --git a/js/resistance-in-a-wire/view/ControlPanel.js b/js/resistance-in-a-wire/view/ControlPanel.js index 2144ed6..b48f39a 100644 --- a/js/resistance-in-a-wire/view/ControlPanel.js +++ b/js/resistance-in-a-wire/view/ControlPanel.js @@ -16,10 +16,13 @@ define( function( require ) { var resistanceInAWire = require( 'RESISTANCE_IN_A_WIRE/resistanceInAWire' ); var ResistanceInAWireA11yStrings = require( 'RESISTANCE_IN_A_WIRE/resistance-in-a-wire/ResistanceInAWireA11yStrings' ); var ResistanceInAWireConstants = require( 'RESISTANCE_IN_A_WIRE/resistance-in-a-wire/ResistanceInAWireConstants' ); + var ResistanceInAWireModel = require( 'RESISTANCE_IN_A_WIRE/resistance-in-a-wire/model/ResistanceInAWireModel' ); var SliderUnit = require( 'RESISTANCE_IN_A_WIRE/resistance-in-a-wire/view/SliderUnit' ); var StringUtils = require( 'PHETCOMMON/util/StringUtils' ); var Text = require( 'SCENERY/nodes/Text' ); var Util = require( 'DOT/Util' ); + var Utterance = require( 'SCENERY_PHET/accessibility/Utterance' ); + var utteranceQueue = require( 'SCENERY_PHET/accessibility/utteranceQueue' ); // strings var areaString = require( 'string!RESISTANCE_IN_A_WIRE/area' ); @@ -44,10 +47,22 @@ define( function( require ) { var areaSliderLabelString = ResistanceInAWireA11yStrings.areaSliderLabelString.value; var sliderControlsString = ResistanceInAWireA11yStrings.sliderControlsString.value; var slidersDescriptionString = ResistanceInAWireA11yStrings.slidersDescriptionString.value; + var sizeChangeAlertPatternString = ResistanceInAWireA11yStrings.sizeChangeAlertPatternString.value; + var letterRhoString = ResistanceInAWireA11yStrings.letterRhoString.value; + var letterLString = ResistanceInAWireA11yStrings.letterLString.value; + var letterAString = ResistanceInAWireA11yStrings.letterAString.value; + var growsString = ResistanceInAWireA11yStrings.growsString.value; + var shrinksString = ResistanceInAWireA11yStrings.shrinksString.value; + var growsALotString = ResistanceInAWireA11yStrings.growsALotString.value; + var shrinksALotString = ResistanceInAWireA11yStrings.shrinksALotString.value; // constants var SLIDER_SPACING = 50; + // a11y - if resistance changes 2 * the range of the resistance / the number of relative size descriptions, larger change + // is signified in description + var LARGE_RESISTANCE_DELTA = ( ( ResistanceInAWireModel.getResistanceRange().max - ResistanceInAWireModel.getResistanceRange().min ) / ResistanceInAWireConstants.RELATIVE_SIZE_STRINGS.length ) * 2; + /** * @param {ResistanceInAWireModel} model * @param {Tandem} tandem @@ -98,7 +113,12 @@ define( function( require ) { resistanceReadout.centerX = 0; } ); + // a11y - when using a slider, we store the initial value on start drag so that we can describe size change after + // interaction + var resistanceOnStart = model.resistanceProperty.get(); + // Create and add the resistivity slider with readout and labels. + var rhoOnStart = model.resistivityProperty.get(); var resistivitySlider = new SliderUnit( model.resistivityProperty, ResistanceInAWireConstants.RESISTIVITY_RANGE, @@ -109,11 +129,29 @@ define( function( require ) { tandem.createTandem( 'resistivitySlider' ), { keyboardStep: 0.05, // ohm-cm shiftKeyStep: 0.01, // ohms-cm - accessibleValuePattern: resistivityUnitsPatternString + accessibleValuePattern: resistivityUnitsPatternString, + startDrag: function() { + rhoOnStart = model.resistivityProperty.get(); + resistanceOnStart = model.resistanceProperty.get(); + }, + endDrag: function() { + var resistance = model.resistanceProperty.get(); + var deltaRho = model.resistivityProperty.get() - rhoOnStart; + var deltaResistance = resistance - resistanceOnStart; + + // announce to assistive technology if there is a change - no need to queue many alerts when pressing keys + // rapidly + if ( deltaRho ) { + utteranceQueue.addToBack( new Utterance( getSizeChangeAlert( resistance, deltaResistance, deltaRho, letterRhoString ), { + typeId: 'rhoChangeAlert' + } ) ); + } + } } ); // Create and add the length slider with readout and labels. + var lengthOnStart = model.lengthProperty.get(); var lengthSlider = new SliderUnit( model.lengthProperty, ResistanceInAWireConstants.LENGTH_RANGE, @@ -124,12 +162,30 @@ define( function( require ) { tandem.createTandem( 'lengthSlider' ), { keyboardStep: 1.0, // cm shiftKeyboardStep: 0.01, // cm - accessibleValuePattern: lengthUnitsPatternString + accessibleValuePattern: lengthUnitsPatternString, + startDrag: function() { + lengthOnStart = model.lengthProperty.get(); + resistanceOnStart = model.resistanceProperty.get(); + }, + endDrag: function() { + var resistance = model.resistanceProperty.get(); + var deltaLength = model.lengthProperty.get() - lengthOnStart; + var deltaResistance = resistance - resistanceOnStart; + + // announce to assistive technology if there is a change - no need to queue many alerts when pressing keys + // rapidly + if ( deltaLength ) { + utteranceQueue.addToBack( new Utterance( getSizeChangeAlert( resistance, deltaResistance, deltaLength, letterLString ), { + typeId: 'rhoChangeAlert' + } ) ); + } + } } ); // Create and add the area slider with readout and labels. For keyboard dragging, the range ranges doesn't split into even steps, // so we calculate a keyboard step by breaking the range into 100. + var areaOnStart = model.areaProperty.get(); var areaSlider = new SliderUnit( model.areaProperty, ResistanceInAWireConstants.AREA_RANGE, @@ -140,7 +196,24 @@ define( function( require ) { tandem.createTandem( 'areaSlider' ), { keyboardStep: 1.0, // cm^2 shiftKeyboardStep: 0.01, // cm^2 - accessibleValuePattern: areaUnitsPatternString + accessibleValuePattern: areaUnitsPatternString, + startDrag: function() { + areaOnStart = model.areaProperty.get(); + resistanceOnStart = model.resistanceProperty.get(); + }, + endDrag: function() { + var resistance = model.resistanceProperty.get(); + var deltaArea = model.areaProperty.get() - areaOnStart; + var deltaResistance = resistance - resistanceOnStart; + + // announce to assistive technology if there is a change - no need to queue many alerts when pressing keys + // rapidly + if ( deltaArea ) { + utteranceQueue.addToBack( new Utterance( getSizeChangeAlert( resistance, deltaResistance, deltaArea, letterAString ), { + typeId: 'rhoChangeAlert' + } ) ); + } + } } ); @@ -166,5 +239,58 @@ define( function( require ) { resistanceInAWire.register( 'ControlPanel', ControlPanel ); + /** + * Get a description for whether a letter grows or shrinks. Optionally, if the size changes enough, an additional + * fragment is included that signifies this. Will return something like + * + * 'grows' + * 'shrinks' + * 'grows a lot' + * 'shrinks a lot' + * + * @param {number} delta + * @param {boolean} describeLargeChanges + * @return {string} + */ + var getSizeChangeFromDelta = function( delta, describeLargeChanges ) { + assert && assert ( delta !== 0, 'trying to describe no change in size' ); + var description; + + var useALot = ( describeLargeChanges && Math.abs( delta ) > LARGE_RESISTANCE_DELTA ); + + if ( delta > 0 ) { + description = useALot ? growsALotString : growsString; + } + else if ( delta < 0 ){ + description = useALot ? shrinksALotString : shrinksString; + } + + return description; + }; + + /** + * Get a full alert for size letter size and how R changes as well. Will return something like + * + * "As letter rho grows, letter R grows. Resistance no 0.667 ohms." or + * "As letter A grows, letter R shrinks a lot. Resistance now 1.20 ohms" + * + * @param {number} resistance - current value of resistance + * @param {number} deltaResistance - change in + * @param {number} otherDelta - change in other variable, resistivity, length, or area + * @param {string} letterString - the letter with size changes to describe + * @return {string} + */ + var getSizeChangeAlert = function( resistance, deltaResistance, otherDelta, letterString ) { + var resistanceChangeString = getSizeChangeFromDelta( deltaResistance, true /*include 'a lot' */ ); + var letterChangeString = getSizeChangeFromDelta( otherDelta, false /*dont include 'a lot */ ); + + return StringUtils.fillIn( sizeChangeAlertPatternString, { + letter: letterString, + letterChange: letterChangeString, + rChange: resistanceChangeString, + resistance: ResistanceInAWireConstants.getFormattedResistanceValue( resistance ) + } ); + }; + return inherit( Panel, ControlPanel ); } ); \ No newline at end of file diff --git a/js/resistance-in-a-wire/view/SliderUnit.js b/js/resistance-in-a-wire/view/SliderUnit.js index c09a24f..e7bd9bd 100644 --- a/js/resistance-in-a-wire/view/SliderUnit.js +++ b/js/resistance-in-a-wire/view/SliderUnit.js @@ -37,7 +37,9 @@ define( function( require ) { accessibleDecimalPlaces: 2, keyboardStep: 1, shiftKeyboardStep: 0.01, - accessibleValuePattern: '{{value}}' // string pattern used for formatting the value read by the screen reader + accessibleValuePattern: '{{value}}', // string pattern used for formatting the value read by the screen reader + endDrag: function() {}, // called at end of drag by HSlider + startDrag: function() {} }, options ); // text for the symbol, text bounds must be accurate for correct layou @@ -69,6 +71,8 @@ define( function( require ) { shiftKeyboardStep: options.shiftKeyboardStep, // delta when holding shift accessibleValuePattern: options.accessibleValuePattern, accessibleDecimalPlaces: options.accessibleDecimalPlaces, // demimal places for readout + startDrag: options.startDrag, + endDrag: options.endDrag, parentContainerTagName: 'li', labelTagName: 'label',