From 4c917658505b1fea12389560a898869a19cce38e Mon Sep 17 00:00:00 2001 From: jbphet Date: Fri, 8 Feb 2019 14:44:17 -0700 Subject: [PATCH] tweaked the spiral placement algorithm until it look reasonably good, see https://github.com/phetsims/energy-forms-and-changes/issues/191 --- js/common/model/EnergyChunkDistributor.js | 57 ++++++++++++------- .../RectangularThermalMovableModelElement.js | 2 +- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/js/common/model/EnergyChunkDistributor.js b/js/common/model/EnergyChunkDistributor.js index 6245c03c..340f35f1 100644 --- a/js/common/model/EnergyChunkDistributor.js +++ b/js/common/model/EnergyChunkDistributor.js @@ -36,6 +36,9 @@ define( function( require ) { var ENERGY_CHUNK_CROSS_SECTIONAL_AREA = Math.PI * Math.pow( ENERGY_CHUNK_DIAMETER, 2 ); var DRAG_COEFFICIENT = 500; // unitless, empirically chosen + // width of an energy chunk in the view, used to keep them in bounds + var ENERGY_CHUNK_VIEW_TO_MODEL_WIDTH = 0.012; + // Thresholds for deciding whether or not to perform redistribution. These value should be chosen such that particles // spread out, then stop all movement. var REDISTRIBUTION_THRESHOLD_ENERGY = 1E-7; // in joules (I think) @@ -415,42 +418,54 @@ define( function( require ) { var sliceBounds = slices[ sliceIndex ].bounds; var sliceCenter = sliceBounds.getCenter(); var numEnergyChunksInSlice = slices[ sliceIndex ].energyChunkList.length; - // var numTurns = 2.25 + ( numEnergyChunksInSlice / 8 ); // empirically determined by trial and error - var numTurns = 2; + var numTurns = 2.7 + ( numEnergyChunksInSlice / 9 ); // empirically determined by trial and error var maxAngle = numTurns * Math.PI * 2; var multiplier = 1 / maxAngle; - // in each slice, orient the spiral slightly differently to make things look more random - var spiralOffsetAngle = sliceIndex * ( 2 * Math.PI / slices.length ); + // define the variables that will be used to iterate through the spiral in the loop below + var angleIncrement = ( maxAngle / ( numEnergyChunksInSlice + 1 ) ) / 2; + var startAngle = maxAngle / 2 + angleIncrement; - // define the variables that will be used to choose the angles in the spiral in the loop below - // var initialAngle = maxAngle / 3; - // var angleRange = maxAngle - initialAngle; - // var angleIncrement = angleRange / ( numEnergyChunksInSlice + 1 ); - // experiment where everything is at max angle - var initialAngle = maxAngle; - // var angleRange = maxAngle - initialAngle; - var angleIncrement = 0; + // Define a value that will be used to offset the spiral rotation so that things are less likely to line up + // across slices. + var spiralAngleOffsetPerSlice = ( 2 * Math.PI ) / slices.length; // loop through each energy chunk in this slice, setting its position using an equation for a spiral for ( var ecIndex = 0; ecIndex < numEnergyChunksInSlice; ecIndex++ ) { + var ec = slices[ sliceIndex ].energyChunkList.get( ecIndex ); - var angle = initialAngle + ecIndex * angleIncrement; - // wind in opposite directions in every other slice to make things look more rangom + // calculate the angle to feed into the formula + var angle = startAngle + ecIndex * angleIncrement; + + // calculate a radius value within the "normalized spiral", where the radius is 1 at the max angle + var normalizedRadius = multiplier * Math.abs( angle ); + assert && assert( normalizedRadius <= 1, 'normalized length must be 1 or smaller' ); + + // calculate an angle the is rotated a bit based on the spiral offset + var adjustedAngle = angle + sliceIndex * spiralAngleOffsetPerSlice; + + // Determine the max possible radius for the current angle, which is bassicall the distance to the closest + // edge. This must be reduced a bit to account for the fact that energy chunks have some width in the view. + var maxRadius = getCenterToEdgeDistance( sliceBounds, adjustedAngle ) - ENERGY_CHUNK_VIEW_TO_MODEL_WIDTH / 2; + + // determine the radius to use as a function of the value from the normalized spiral and the max value + var radius = maxRadius * normalizedRadius; + + // reverse the radius on every other slice to get more spread between slices if ( sliceIndex % 2 === 1 ) { - angle = -angle; + radius = -radius; + adjustedAngle = -adjustedAngle; } - var normalizedLength = multiplier * ( Math.abs( angle ) - spiralOffsetAngle ); - assert && assert( normalizedLength <= 1, 'normalized length must be 1 or smaller' ); - var maxLength = getCenterToEdgeDistance( sliceBounds, angle ) * 0.95; // reduced a little so EC nodes stay inside - var length = maxLength * normalizedLength; + + // calculate the destination using polar coordinates var ecDestination = new Vector2(); - ecDestination.setPolar( length, angle ); + ecDestination.setPolar( radius, adjustedAngle ); ecDestination.add( sliceCenter ); + // animate the energy chunk towards its destination if it isn't there already if ( !ec.positionProperty.value.equals( ecDestination ) ) { - moveECTowardsDestination( slices[ sliceIndex ].energyChunkList.get( ecIndex ), ecDestination, dt ); + moveECTowardsDestination( ec, ecDestination, dt ); ecMoved = true; } } diff --git a/js/intro/model/RectangularThermalMovableModelElement.js b/js/intro/model/RectangularThermalMovableModelElement.js index a1d87566..29774662 100644 --- a/js/intro/model/RectangularThermalMovableModelElement.js +++ b/js/intro/model/RectangularThermalMovableModelElement.js @@ -525,7 +525,7 @@ define( function( require ) { var targetNumECs = EFACConstants.ENERGY_TO_NUM_CHUNKS_MAPPER( this.energy ); // start with the middle slice and cycle through in order, added chunks evenly to each - var slideIndex = Math.floor( this.slices.length / 2 ); + var slideIndex = Math.floor( this.slices.length / 2 ) - 1; var numECsAdded = 0; while ( numECsAdded < targetNumECs ) { var slice = this.slices[ slideIndex ];