diff --git a/js/energy/view/BarPlotNode.js b/js/energy/view/BarPlotNode.js index 61bd449d..ec356bd5 100644 --- a/js/energy/view/BarPlotNode.js +++ b/js/energy/view/BarPlotNode.js @@ -9,6 +9,7 @@ define( require => { 'use strict'; // modules + const Bounds2 = require( 'DOT/Bounds2' ); const ColorDef = require( 'SCENERY/util/ColorDef' ); const Dimension2 = require( 'DOT/Dimension2' ); const gasProperties = require( 'GAS_PROPERTIES/gasProperties' ); @@ -36,6 +37,7 @@ define( require => { // @private this.chartSize = chartSize; this.yScaleProperty = yScaleProperty; + this.shapeBounds = new Bounds2( 0, 0, chartSize.width, chartSize.height ); } /** @@ -67,6 +69,17 @@ define( require => { } this.shape = shape; } + + /** + * Always use the full chart bounds, as a performance optimization. + * See https://github.com/phetsims/gas-properties/issues/146 + * @returns {Bounds2} + * @public + * @override + */ + computeShapeBounds() { + return this.shapeBounds; + } } return gasProperties.register( 'BarPlotNode', BarPlotNode ); diff --git a/js/energy/view/HistogramNode.js b/js/energy/view/HistogramNode.js index a5de8ae3..130224d7 100644 --- a/js/energy/view/HistogramNode.js +++ b/js/energy/view/HistogramNode.js @@ -16,18 +16,15 @@ define( require => { const Emitter = require( 'AXON/Emitter' ); const gasProperties = require( 'GAS_PROPERTIES/gasProperties' ); const GasPropertiesColorProfile = require( 'GAS_PROPERTIES/common/GasPropertiesColorProfile' ); - const GasPropertiesConstants = require( 'GAS_PROPERTIES/common/GasPropertiesConstants' ); + const IntervalLinesNode = require( 'GAS_PROPERTIES/energy/view/IntervalLinesNode' ); const LinePlotNode = require( 'GAS_PROPERTIES/energy/view/LinePlotNode' ); const Node = require( 'SCENERY/nodes/Node' ); const NumberProperty = require( 'AXON/NumberProperty' ); - const Path = require( 'SCENERY/nodes/Path' ); const PhetFont = require( 'SCENERY_PHET/PhetFont' ); const Property = require( 'AXON/Property' ); const Rectangle = require( 'SCENERY/nodes/Rectangle' ); - const Shape = require( 'KITE/Shape' ); const Tandem = require( 'TANDEM/Tandem' ); const Text = require( 'SCENERY/nodes/Text' ); - const Util = require( 'DOT/Util' ); // Options for all histogram axis labels const HISTOGRAM_AXIS_LABEL_OPTIONS = { @@ -76,16 +73,6 @@ define( require => { plotLineWidth: 2, // lineWidth for line segment plots barColor: 'white', // {ColorDef} - // {number} space between the interval lines, in number of particles - intervalLinesSpacing: GasPropertiesConstants.HISTOGRAM_LINE_SPACING, - - // options that style the interval lines - intervalLineOptions: { - stroke: 'white', // {ColorDef} - opacity: 0.5, // (0,1) - lineWidth: 0.5 - }, - // phet-io tandem: Tandem.required @@ -93,8 +80,6 @@ define( require => { assert && assert( options.barColor !== null && ColorDef.isColorDef( options.barColor ), `invalid barColor: ${options.barColor}` ); - assert && assert( options.intervalLinesSpacing > 0 && Util.isInteger( options.intervalLinesSpacing ), - 'intervalLinesSpacing must be a positive integer: ' + options.intervalLinesSpacing ); // Background appears behind plotted data const background = new Rectangle( 0, 0, options.chartSize.width, options.chartSize.height, { @@ -121,10 +106,8 @@ define( require => { children: [ allPlotNode, heavyPlotNode, lightPlotNode ] } ); - // Horizontal lines that appear at equally-spaced intervals based on y-axis scale. - // These lines are intended to cue the student about the relative scale of the y axis. - // More lines means a larger value for 'Number of Particles'. - const intervalLines = new Path( null, options.intervalLineOptions ); + // Horizontal lines that indicate y-axis scale. + const intervalLines = new IntervalLinesNode( options.chartSize ); // x-axis label const xAxisLabelNode = new Text( xAxisString, _.extend( {}, HISTOGRAM_AXIS_LABEL_OPTIONS, { @@ -172,35 +155,13 @@ define( require => { } }; - // Update the interval lines if the y-axis scale has changed. - let previousMaxY = null; - const updateIntervalLines = () => { - const maxY = yScaleProperty.value; - if ( previousMaxY === null || previousMaxY !== maxY ) { - - const shape = new Shape(); - - const numberOfLines = Math.floor( maxY / options.intervalLinesSpacing ); - const ySpacing = ( options.intervalLinesSpacing / maxY ) * options.chartSize.height; - - for ( let i = 1; i <= numberOfLines; i++ ) { - const y = options.chartSize.height - ( i * ySpacing ); - shape.moveTo( 0, y ).lineTo( options.chartSize.width, y ); - } - - intervalLines.shape = shape; - - previousMaxY = maxY; - } - }; - // Update everything const update = () => { updatePlots(); - updateIntervalLines(); + intervalLines.update( yScaleProperty.value ); }; - // @public whether update are enabled, false ignores binCountsUpdatedEmitter. + // @public whether updates are enabled, false ignores binCountsUpdatedEmitter. // This is used to prevent updates when the accordion box containing a histogram is collapsed. this.updateEnabledProperty = new BooleanProperty( true ); this.updateEnabledProperty.lazyLink( updateEnabled => { diff --git a/js/energy/view/IntervalLinesNode.js b/js/energy/view/IntervalLinesNode.js new file mode 100644 index 00000000..555bcc5c --- /dev/null +++ b/js/energy/view/IntervalLinesNode.js @@ -0,0 +1,77 @@ +// Copyright 2019, University of Colorado Boulder + +/** + * IntervalLinesNode renders the horizontal lines that appear at equally-spaced intervals based on a histogram's + * y-axis scale. These lines are intended to cue the student about the relative scale of the y axis. More lines + * means a larger value for 'Number of Particles'. + * + * @author Chris Malley (PixelZoom, Inc.) + */ +define( require => { + 'use strict'; + + // modules + const Bounds2 = require( 'DOT/Bounds2' ); + const Dimension2 = require( 'DOT/Dimension2' ); + const gasProperties = require( 'GAS_PROPERTIES/gasProperties' ); + const GasPropertiesConstants = require( 'GAS_PROPERTIES/common/GasPropertiesConstants' ); + const Path = require( 'SCENERY/nodes/Path' ); + const Shape = require( 'KITE/Shape' ); + + class IntervalLinesNode extends Path { + + /** + * @param {Dimension2} chartSize - dimensions of the chart + */ + constructor( chartSize ) { + assert && assert( chartSize instanceof Dimension2, `invalid chartSize: ${chartSize}` ); + + super( new Shape(), { + stroke: 'white', // {ColorDef} + opacity: 0.5, // (0,1) + lineWidth: 0.5 + } ); + + // @private + this.chartSize = chartSize; + this.shapeBounds = new Bounds2( 0, 0, chartSize.width, chartSize.height ); + } + + /** + * Updates the lines to match the current y scale. + * @param {number} maxY + * @public + */ + update( maxY ) { + if ( this.previousMaxY === null || this.previousMaxY !== maxY ) { + + const shape = new Shape(); + + const numberOfLines = Math.floor( maxY / GasPropertiesConstants.HISTOGRAM_LINE_SPACING ); + const ySpacing = ( GasPropertiesConstants.HISTOGRAM_LINE_SPACING / maxY ) * this.chartSize.height; + + for ( let i = 1; i <= numberOfLines; i++ ) { + const y = this.chartSize.height - ( i * ySpacing ); + shape.moveTo( 0, y ).lineTo( this.chartSize.width, y ); + } + + this.shape = shape; + + this.previousMaxY = maxY; + } + } + + /** + * Always use the full chart bounds, as a performance optimization. + * See https://github.com/phetsims/gas-properties/issues/146 + * @returns {Bounds2} + * @public + * @override + */ + computeShapeBounds() { + return this.shapeBounds; + } + } + + return gasProperties.register( 'IntervalLinesNode', IntervalLinesNode ); +} ); \ No newline at end of file diff --git a/js/energy/view/LinePlotNode.js b/js/energy/view/LinePlotNode.js index 63ef4db7..752c381f 100644 --- a/js/energy/view/LinePlotNode.js +++ b/js/energy/view/LinePlotNode.js @@ -1,7 +1,7 @@ // Copyright 2019, University of Colorado Boulder /** - * LinePlotNode plots histogram data as a set of connected line segments. It is used to overlay specifies-specific + * LinePlotNode plots histogram data as a set of connected line segments. It is used to overlay species-specific * histogram data on top of a more typical bar-style histogram. * * @author Chris Malley (PixelZoom, Inc.) @@ -10,6 +10,7 @@ define( require => { 'use strict'; // modules + const Bounds2 = require( 'DOT/Bounds2' ); const ColorDef = require( 'SCENERY/util/ColorDef' ); const Dimension2 = require( 'DOT/Dimension2' ); const gasProperties = require( 'GAS_PROPERTIES/gasProperties' ); @@ -32,14 +33,15 @@ define( require => { assert && assert( typeof lineWidth === 'number' && lineWidth > 0, `invalid lineWidth: ${lineWidth}` ); super( new Shape(), { - fill: null, - stroke: color, // to hide seams + fill: null, // because we're drawing lines + stroke: color, lineWidth: lineWidth } ); // @private this.chartSize = chartSize; this.yScaleProperty = yScaleProperty; + this.shapeBounds = new Bounds2( 0, 0, chartSize.width, chartSize.height ); } /** @@ -73,6 +75,17 @@ define( require => { } this.shape = shape; } + + /** + * Always use the full chart bounds, as a performance optimization. + * See https://github.com/phetsims/gas-properties/issues/146 + * @returns {Bounds2} + * @public + * @override + */ + computeShapeBounds() { + return this.shapeBounds; + } } return gasProperties.register( 'LinePlotNode', LinePlotNode );