diff --git a/js/common/PHScaleConstants.js b/js/common/PHScaleConstants.js index 6f390a1c..553f9f02 100644 --- a/js/common/PHScaleConstants.js +++ b/js/common/PHScaleConstants.js @@ -50,6 +50,7 @@ const PHScaleConstants = { // logarithmic graph LOGARITHMIC_EXPONENT_RANGE: new Range( -16, 2 ), + LOGARITHMIC_MANTISSA_DECIMAL_PLACES: 1, LINEAR_EXPONENT_RANGE: new Range( -14, 1 ), LINEAR_MANTISSA_RANGE: new Range( 0, 8 ), diff --git a/js/common/view/graph/GraphIndicatorDragListener.js b/js/common/view/graph/GraphIndicatorDragListener.js index 41bf100f..5546a629 100644 --- a/js/common/view/graph/GraphIndicatorDragListener.js +++ b/js/common/view/graph/GraphIndicatorDragListener.js @@ -7,6 +7,7 @@ */ import Utils from '../../../../../dot/js/Utils.js'; +import ScientificNotationNode from '../../../../../scenery-phet/js/ScientificNotationNode.js'; import DragListener from '../../../../../scenery/js/listeners/DragListener.js'; import Tandem from '../../../../../tandem/js/Tandem.js'; import phScale from '../../../phScale.js'; @@ -48,15 +49,30 @@ class GraphIndicatorDragListener extends DragListener { // Adjust the y-coordinate for the offset between the pointer and the indicator's origin const y = targetNode.globalToParentPoint( event.pointer.point ).y - clickYOffset; - // Convert the y-coordinate to a model value + // Convert the y-coordinate to a model value. const value = yToValue( y ); + assert && assert( value > 0 ); + + // Round the model value to the first 2 non-zero decimal places. This prevents continuous dragging from + // creating values that have too much precision, which can result in pH = 7.00 with unequal amounts of + // H3O+ and OH-. See https://github.com/phetsims/ph-scale/issues/225. + const scientificNotation = ScientificNotationNode.toScientificNotation( value, { + mantissaDecimalPlaces: PHScaleConstants.LOGARITHMIC_MANTISSA_DECIMAL_PLACES + } ); + const exponent = scientificNotation.exponent - PHScaleConstants.LOGARITHMIC_MANTISSA_DECIMAL_PLACES; + const interval = Math.pow( 10, exponent ); + const adjustedValue = Utils.roundToInterval( value, interval ); // Map the model value to pH, depending on which units we're using. - let pH = ( graphUnitsProperty.get() === GraphUnits.MOLES_PER_LITER ) ? concentrationToPH( value ) : molesToPH( value, totalVolumeProperty.get() ); + let pH = ( graphUnitsProperty.get() === GraphUnits.MOLES_PER_LITER ) ? + concentrationToPH( adjustedValue ) : + molesToPH( adjustedValue, totalVolumeProperty.get() ); // Constrain the pH to the valid range pH = Utils.clamp( pH, PHScaleConstants.PH_RANGE.min, PHScaleConstants.PH_RANGE.max ); + phet.log && phet.log( `value=${value} adjustedValue=${adjustedValue} pH=${pH}` ); + // Set the solution's pH pHProperty.set( pH ); } diff --git a/js/common/view/graph/GraphIndicatorNode.js b/js/common/view/graph/GraphIndicatorNode.js index 8719d5cc..393fa2ea 100644 --- a/js/common/view/graph/GraphIndicatorNode.js +++ b/js/common/view/graph/GraphIndicatorNode.js @@ -55,7 +55,7 @@ class GraphIndicatorNode extends Node { valueYMargin: 3, xSpacing: 8, ySpacing: 4, - mantissaDecimalPlaces: 1, + mantissaDecimalPlaces: PHScaleConstants.LOGARITHMIC_MANTISSA_DECIMAL_PLACES, exponent: null, // use this to request a specific exponent, otherwise the exponent is computed isInteractive: false, arrowFill: 'rgb( 0, 200, 0 )',