{"action":"created","issue":{"url":"https://api.github.com/repos/phetsims/states-of-matter/issues/188","repository_url":"https://api.github.com/repos/phetsims/states-of-matter","labels_url":"https://api.github.com/repos/phetsims/states-of-matter/issues/188/labels{/name}","comments_url":"https://api.github.com/repos/phetsims/states-of-matter/issues/188/comments","events_url":"https://api.github.com/repos/phetsims/states-of-matter/issues/188/events","html_url":"https://github.com/phetsims/states-of-matter/issues/188","id":193330042,"number":188,"title":"Duplicated code","user":{"login":"samreid","id":679486,"avatar_url":"https://avatars.githubusercontent.com/u/679486?v=3","gravatar_id":"","url":"https://api.github.com/users/samreid","html_url":"https://github.com/samreid","followers_url":"https://api.github.com/users/samreid/followers","following_url":"https://api.github.com/users/samreid/following{/other_user}","gists_url":"https://api.github.com/users/samreid/gists{/gist_id}","starred_url":"https://api.github.com/users/samreid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samreid/subscriptions","organizations_url":"https://api.github.com/users/samreid/orgs","repos_url":"https://api.github.com/users/samreid/repos","events_url":"https://api.github.com/users/samreid/events{/privacy}","received_events_url":"https://api.github.com/users/samreid/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":{"login":"samreid","id":679486,"avatar_url":"https://avatars.githubusercontent.com/u/679486?v=3","gravatar_id":"","url":"https://api.github.com/users/samreid","html_url":"https://github.com/samreid","followers_url":"https://api.github.com/users/samreid/followers","following_url":"https://api.github.com/users/samreid/following{/other_user}","gists_url":"https://api.github.com/users/samreid/gists{/gist_id}","starred_url":"https://api.github.com/users/samreid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samreid/subscriptions","organizations_url":"https://api.github.com/users/samreid/orgs","repos_url":"https://api.github.com/users/samreid/repos","events_url":"https://api.github.com/users/samreid/events{/privacy}","received_events_url":"https://api.github.com/users/samreid/received_events","type":"User","site_admin":false},"assignees":[{"login":"samreid","id":679486,"avatar_url":"https://avatars.githubusercontent.com/u/679486?v=3","gravatar_id":"","url":"https://api.github.com/users/samreid","html_url":"https://github.com/samreid","followers_url":"https://api.github.com/users/samreid/followers","following_url":"https://api.github.com/users/samreid/following{/other_user}","gists_url":"https://api.github.com/users/samreid/gists{/gist_id}","starred_url":"https://api.github.com/users/samreid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samreid/subscriptions","organizations_url":"https://api.github.com/users/samreid/orgs","repos_url":"https://api.github.com/users/samreid/repos","events_url":"https://api.github.com/users/samreid/events{/privacy}","received_events_url":"https://api.github.com/users/samreid/received_events","type":"User","site_admin":false}],"milestone":null,"comments":4,"created_at":"2016-12-04T03:57:33Z","updated_at":"2016-12-15T01:03:44Z","closed_at":null,"body":"While looking into https://github.com/phetsims/chipper/issues/526 I noticed that states-of-matter had around 280 lines of duplicated code.  It seemed higher than I would expect, so I thought I would make a note of it.  Here is the subset of the report from the chipper issue that points out the duplicated code in states-of-matter:\r\n\r\n```\r\n\t- ../fluid-pressure-and-flow/js/flow/view/FlowView.js: 229-244\r\n\t ../states-of-matter/js/atomic-interactions/view/AtomicInteractionsScreenView.js: 197-212\r\n\r\n 229 │ 197 │       ( slowMotionRadioBox.localBounds.minX + speedControlMaxWidth ),   \r\n 230 │ 198 │       slowMotionRadioBox.localBounds.maxY + touchAreaYDilation          \r\n 231 │ 199 │     );                                                                  \r\n 232 │ 200 │                                                                         \r\n 233 │ 201 │     normalMotionRadioBox.touchArea = new Bounds2(                       \r\n 234 │ 202 │       normalMotionRadioBox.localBounds.minX,                            \r\n 235 │ 203 │       normalMotionRadioBox.localBounds.minY - touchAreaYDilation,       \r\n 236 │ 204 │       ( normalMotionRadioBox.localBounds.minX + speedControlMaxWidth ), \r\n 237 │ 205 │       normalMotionRadioBox.localBounds.maxY + touchAreaYDilation        \r\n 238 │ 206 │     );                                                                  \r\n 239 │ 207 │                                                                         \r\n 240 │ 208 │     var speedControl = new VBox( {                                      \r\n 241 │ 209 │       align: 'left',                                                    \r\n 242 │ 210 │       spacing: radioButtonSpacing,                                      \r\n 243 │ 211 │       children: [ slowMotionRadioBox, normalMotionRadioBox ]            \r\n 244 │ 212 │     } );                                                                \r\n\r\n\t- ../states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 74-85\r\n\t ../states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 115-126\r\n\r\n 74 │ 115 │         endDragY = self.epsilonLine.globalToParentPoint( event.pointer.point ).y;                           \r\n 75 │ 116 │         var d = endDragY - startDragY;                                                                      \r\n 76 │ 117 │         startDragY = endDragY;                                                                              \r\n 77 │ 118 │         var scaleFactor = StatesOfMatterConstants.MAX_EPSILON / ( self.getGraphHeight() / 2 );              \r\n 78 │ 119 │         dualAtomModel.interactionStrengthProperty.value = dualAtomModel.getEpsilon() + ( d * scaleFactor ); \r\n 79 │ 120 │       },                                                                                                    \r\n 80 │ 121 │                                                                                                             \r\n 81 │ 122 │       end: function() {                                                                                     \r\n 82 │ 123 │         dualAtomModel.setMotionPaused( false );                                                             \r\n 83 │ 124 │       }                                                                                                     \r\n 84 │ 125 │     } ) );                                                                                                  \r\n 85 │ 126 │     this.epsilonLineLayer.addChild( this.epsilonLine );                                                     \r\n\r\n\t- ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 149-162\r\n\t ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 226-239\r\n\r\n 149 │ 226 │       this.multipleParticleModel.setTemperature( StatesOfMatterConstants.LIQUID_TEMPERATURE );         \r\n 150 │ 227 │                                                                                                        \r\n 151 │ 228 │       // Get references to the various elements of the data set.                                       \r\n 152 │ 229 │       var moleculeDataSet = this.multipleParticleModel.moleculeDataSet;                                \r\n 153 │ 230 │       var moleculeCenterOfMassPositions = moleculeDataSet.getMoleculeCenterOfMassPositions();          \r\n 154 │ 231 │       var moleculeVelocities = moleculeDataSet.getMoleculeVelocities();                                \r\n 155 │ 232 │       var moleculeRotationAngles = moleculeDataSet.getMoleculeRotationAngles();                        \r\n 156 │ 233 │       var moleculeRotationRates = moleculeDataSet.getMoleculeRotationRates();                          \r\n 157 │ 234 │       var moleculesInsideContainer = this.multipleParticleModel.moleculeDataSet.insideContainer;       \r\n 158 │ 235 │                                                                                                        \r\n 159 │ 236 │       // Create and initialize other variables needed to do the job.                                   \r\n 160 │ 237 │       var temperatureSqrt = Math.sqrt( this.multipleParticleModel.temperatureSetPointProperty.get() ); \r\n 161 │ 238 │       var numberOfMolecules = moleculeDataSet.getNumberOfMolecules();                                  \r\n 162 │ 239 │                                                                                                        \r\n\r\n\t- ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 263-273\r\n\t ../states-of-matter/js/common/model/engine/MonatomicPhaseStateChanger.js: 223-233\r\n\r\n 263 │ 223 │       for ( i = 0; i < numberOfMolecules; i++ ) {                                                            \r\n 264 │ 224 │         for ( var j = 0; j < this.MAX_PLACEMENT_ATTEMPTS; j++ ) {                                            \r\n 265 │ 225 │                                                                                                              \r\n 266 │ 226 │           // Pick a random position.                                                                         \r\n 267 │ 227 │           newPosX = this.MIN_INITIAL_PARTICLE_TO_WALL_DISTANCE + ( phet.joist.random.nextDouble() * rangeX); \r\n 268 │ 228 │           newPosY = this.MIN_INITIAL_PARTICLE_TO_WALL_DISTANCE + ( phet.joist.random.nextDouble() * rangeY); \r\n 269 │ 229 │           var positionAvailable = true;                                                                      \r\n 270 │ 230 │                                                                                                              \r\n 271 │ 231 │           // See if this position is available.                                                              \r\n 272 │ 232 │           for ( var k = 0; k < i; k++ ) {                                                                    \r\n 273 │ 233 │             if ( moleculeCenterOfMassPositions[ k ].distanceXY( newPosX, newPosY ) <                         \r\n\r\n\t- ../states-of-matter/js/common/model/engine/MonatomicPhaseStateChanger.js: 36-58\r\n\t ../states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 47-69\r\n\r\n 36 │ 47 │   return inherit( AbstractPhaseStateChanger, MonatomicPhaseStateChanger, { \r\n 37 │ 48 │                                                                            \r\n 38 │ 49 │     /**                                                                    \r\n 39 │ 50 │      * @public                                                             \r\n 40 │ 51 │      * @param {number} phaseID - state(solid/liquid/gas) of Molecule       \r\n 41 │ 52 │      */                                                                    \r\n 42 │ 53 │     setPhase: function( phaseID ) {                                        \r\n 43 │ 54 │       switch( phaseID ) {                                                  \r\n 44 │ 55 │         case PhaseStateEnum.SOLID:                                         \r\n 45 │ 56 │           this.setPhaseSolid();                                            \r\n 46 │ 57 │           break;                                                           \r\n 47 │ 58 │         case PhaseStateEnum.LIQUID:                                        \r\n 48 │ 59 │           this.setPhaseLiquid();                                           \r\n 49 │ 60 │           break;                                                           \r\n 50 │ 61 │         case PhaseStateEnum.GAS:                                           \r\n 51 │ 62 │           this.setPhaseGas();                                              \r\n 52 │ 63 │           break;                                                           \r\n 53 │ 64 │         default:                                                           \r\n 54 │ 65 │           throw new Error( 'invalid phaseID: ' + phaseID );                \r\n 55 │ 66 │       }                                                                    \r\n 56 │ 67 │                                                                            \r\n 57 │ 68 │       var moleculeDataSet = this.multipleParticleModel.moleculeDataSet;    \r\n 58 │ 69 │                                                                            \r\n\r\n\t- ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 161-171\r\n\t ../states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 100-110\r\n\r\n 161 │ 100 │       var numberOfMolecules = moleculeDataSet.getNumberOfMolecules();                                                            \r\n 162 │ 101 │                                                                                                                                  \r\n 163 │ 102 │       // Initialize the velocities and angles of the molecules.                                                                  \r\n 164 │ 103 │       for ( var i = 0; i < numberOfMolecules; i++ ) {                                                                            \r\n 165 │ 104 │                                                                                                                                  \r\n 166 │ 105 │         // Assign each molecule an initial velocity.                                                                             \r\n 167 │ 106 │         moleculeVelocities[ i ].setXY( temperatureSqrt * this.rand.nextGaussian(), temperatureSqrt * this.rand.nextGaussian() ); \r\n 168 │ 107 │                                                                                                                                  \r\n 169 │ 108 │         // Assign each molecule an initial rotation rate.                                                                        \r\n 170 │ 109 │         moleculeRotationRates[ i ] = phet.joist.random.nextDouble() * temperatureSqrt * Math.PI * 2;                             \r\n 171 │ 110 │                                                                                                                                  \r\n\r\n\t- ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 160-171\r\n\t ../states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 160-171\r\n\r\n 160 │ 160 │       var temperatureSqrt = Math.sqrt( this.multipleParticleModel.temperatureSetPointProperty.get() );                           \r\n 161 │ 161 │       var numberOfMolecules = moleculeDataSet.getNumberOfMolecules();                                                            \r\n 162 │ 162 │                                                                                                                                  \r\n 163 │ 163 │       // Initialize the velocities and angles of the molecules.                                                                  \r\n 164 │ 164 │       for ( var i = 0; i < numberOfMolecules; i++ ) {                                                                            \r\n 165 │ 165 │                                                                                                                                  \r\n 166 │ 166 │         // Assign each molecule an initial velocity.                                                                             \r\n 167 │ 167 │         moleculeVelocities[ i ].setXY( temperatureSqrt * this.rand.nextGaussian(), temperatureSqrt * this.rand.nextGaussian() ); \r\n 168 │ 168 │                                                                                                                                  \r\n 169 │ 169 │         // Assign each molecule an initial rotation rate.                                                                        \r\n 170 │ 170 │         moleculeRotationRates[ i ] = phet.joist.random.nextDouble() * temperatureSqrt * Math.PI * 2;                             \r\n 171 │ 171 │                                                                                                                                  \r\n\r\n\t- ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 178-202\r\n\t ../states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 179-203\r\n\r\n 178 │ 179 │       var centerPointY = this.multipleParticleModel.normalizedContainerHeight / 4;                                        \r\n 179 │ 180 │       var currentLayer = 0;                                                                                               \r\n 180 │ 181 │       var particlesOnCurrentLayer = 0;                                                                                    \r\n 181 │ 182 │       var particlesThatWillFitOnCurrentLayer = 1;                                                                         \r\n 182 │ 183 │       for ( i = 0; i < numberOfMolecules; i++ ) {                                                                         \r\n 183 │ 184 │         for ( var j = 0; j < this.MAX_PLACEMENT_ATTEMPTS; j++ ) {                                                         \r\n 184 │ 185 │           var distanceFromCenter = currentLayer * MIN_INITIAL_DIAMETER_DISTANCE * LIQUID_SPACING_FACTOR;                  \r\n 185 │ 186 │           var angle = (particlesOnCurrentLayer / particlesThatWillFitOnCurrentLayer * 2 * Math.PI) +                      \r\n 186 │ 187 │                       (particlesThatWillFitOnCurrentLayer / (4 * Math.PI));                                               \r\n 187 │ 188 │           var xPos = centerPointX + (distanceFromCenter * Math.cos( angle ));                                             \r\n 188 │ 189 │           var yPos = centerPointY + (distanceFromCenter * Math.sin( angle ));                                             \r\n 189 │ 190 │                                                                                                                           \r\n 190 │ 191 │           // Consider this spot used even if we don't actually put the particle there.                                    \r\n 191 │ 192 │           particlesOnCurrentLayer++;                                                                                      \r\n 192 │ 193 │           if ( particlesOnCurrentLayer >= particlesThatWillFitOnCurrentLayer ) {                                          \r\n 193 │ 194 │                                                                                                                           \r\n 194 │ 195 │             // This layer is full - move to the next one.                                                                 \r\n 195 │ 196 │             currentLayer++;                                                                                               \r\n 196 │ 197 │             particlesThatWillFitOnCurrentLayer = Math.floor( currentLayer * 2 * Math.PI /                                 \r\n 197 │ 198 │                                                              ( MIN_INITIAL_DIAMETER_DISTANCE * LIQUID_SPACING_FACTOR ) ); \r\n 198 │ 199 │             particlesOnCurrentLayer = 0;                                                                                  \r\n 199 │ 200 │           }                                                                                                               \r\n 200 │ 201 │           // Check if the position is too close to the wall.  Note                                                        \r\n 201 │ 202 │           // that we don't check inter-particle distances here - we rely                                                  \r\n 202 │ 203 │           // on the placement algorithm to make sure that this is not a                                                   \r\n\r\n\t- ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 237-252\r\n\t ../states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 239-254\r\n\r\n 237 │ 239 │       var temperatureSqrt = Math.sqrt( this.multipleParticleModel.temperatureSetPointProperty.get() );                           \r\n 238 │ 240 │       var numberOfMolecules = moleculeDataSet.getNumberOfMolecules();                                                            \r\n 239 │ 241 │       for ( var i = 0; i < numberOfMolecules; i++ ) {                                                                            \r\n 240 │ 242 │                                                                                                                                  \r\n 241 │ 243 │         // Temporarily position the molecules at (0,0).                                                                          \r\n 242 │ 244 │         moleculeCenterOfMassPositions[ i ].setXY( 0, 0 );                                                                        \r\n 243 │ 245 │                                                                                                                                  \r\n 244 │ 246 │         // Assign each molecule an initial velocity.                                                                             \r\n 245 │ 247 │         moleculeVelocities[ i ].setXY( temperatureSqrt * this.rand.nextGaussian(), temperatureSqrt * this.rand.nextGaussian() ); \r\n 246 │ 248 │                                                                                                                                  \r\n 247 │ 249 │         // Assign each molecule an initial rotational position.                                                                  \r\n 248 │ 250 │         moleculeRotationAngles[ i ] = phet.joist.random.nextDouble() * Math.PI * 2;                                              \r\n 249 │ 251 │                                                                                                                                  \r\n 250 │ 252 │         // Assign each molecule an initial rotation rate.                                                                        \r\n 251 │ 253 │         moleculeRotationRates[ i ] = phet.joist.random.nextDouble() * temperatureSqrt * Math.PI * 2;                             \r\n 252 │ 254 │                                                                                                                                  \r\n\r\n\t- ../states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 262-286\r\n\t ../states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 264-288\r\n\r\n 262 │ 264 │       var rangeY = this.multipleParticleModel.normalizedContainerWidth - ( 2 * this.MIN_INITIAL_PARTICLE_TO_WALL_DISTANCE); \r\n 263 │ 265 │       for ( i = 0; i < numberOfMolecules; i++ ) {                                                                           \r\n 264 │ 266 │         for ( var j = 0; j < this.MAX_PLACEMENT_ATTEMPTS; j++ ) {                                                           \r\n 265 │ 267 │                                                                                                                             \r\n 266 │ 268 │           // Pick a random position.                                                                                        \r\n 267 │ 269 │           newPosX = this.MIN_INITIAL_PARTICLE_TO_WALL_DISTANCE + ( phet.joist.random.nextDouble() * rangeX);                \r\n 268 │ 270 │           newPosY = this.MIN_INITIAL_PARTICLE_TO_WALL_DISTANCE + ( phet.joist.random.nextDouble() * rangeY);                \r\n 269 │ 271 │           var positionAvailable = true;                                                                                     \r\n 270 │ 272 │                                                                                                                             \r\n 271 │ 273 │           // See if this position is available.                                                                             \r\n 272 │ 274 │           for ( var k = 0; k < i; k++ ) {                                                                                   \r\n 273 │ 275 │             if ( moleculeCenterOfMassPositions[ k ].distanceXY( newPosX, newPosY ) <                                        \r\n 274 │ 276 │                  MIN_INITIAL_DIAMETER_DISTANCE * GAS_SPACING_FACTOR ) {                                                     \r\n 275 │ 277 │               positionAvailable = false;                                                                                    \r\n 276 │ 278 │               break;                                                                                                        \r\n 277 │ 279 │             }                                                                                                               \r\n 278 │ 280 │           }                                                                                                                 \r\n 279 │ 281 │           if ( positionAvailable ) {                                                                                        \r\n 280 │ 282 │                                                                                                                             \r\n 281 │ 283 │             // We found an open position.                                                                                   \r\n 282 │ 284 │             moleculeCenterOfMassPositions[ i ].setXY( newPosX, newPosY );                                                   \r\n 283 │ 285 │             break;                                                                                                          \r\n 284 │ 286 │           }                                                                                                                 \r\n 285 │ 287 │           else if ( j === this.MAX_PLACEMENT_ATTEMPTS - 1 ) {                                                               \r\n 286 │ 288 │                                                                                                                             \r\n\r\n\t- ../states-of-matter/js/common/view/ParticleImageCanvasNode.js: 143-158\r\n\t ../states-of-matter/js/common/view/ParticleImageCanvasNode.js: 162-177\r\n\r\n 143 │ 162 │         if ( particle.renderBelowOxygen ){                                                                  \r\n 144 │ 163 │           particleViewRadius = this.particleRadii[ particle.getType() ];                                    \r\n 145 │ 164 │           context.drawImage(                                                                                \r\n 146 │ 165 │             this.particleImageCanvas,                                                                       \r\n 147 │ 166 │             this.mapAtomTypeToImageXPosition[ particle.getType() ],                                         \r\n 148 │ 167 │             this.useStrokedParticles ? PARTICLE_IMAGE_CANVAS_LENGTH : 0,                                    \r\n 149 │ 168 │             PARTICLE_IMAGE_CANVAS_LENGTH,                                                                   \r\n 150 │ 169 │             PARTICLE_IMAGE_CANVAS_LENGTH,                                                                   \r\n 151 │ 170 │             this.modelViewTransform.modelToViewX( particle.positionProperty.value.x ) - particleViewRadius, \r\n 152 │ 171 │             this.modelViewTransform.modelToViewY( particle.positionProperty.value.y ) - particleViewRadius, \r\n 153 │ 172 │             particleViewRadius * 2,                                                                         \r\n 154 │ 173 │             particleViewRadius * 2                                                                          \r\n 155 │ 174 │           );                                                                                                \r\n 156 │ 175 │         }                                                                                                   \r\n 157 │ 176 │       }                                                                                                     \r\n 158 │ 177 │                                                                                                             \r\n\r\n\t- ../states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 111-119\r\n\t ../states-of-matter/js/phase-changes/view/EpsilonControlInteractionPotentialDiagram.js: 94-102\r\n\r\n 111 │ 94  │         startDragY = self.epsilonResizeHandle.globalToParentPoint( event.pointer.point ).y;                 \r\n 112 │ 95  │       },                                                                                                    \r\n 113 │ 96  │                                                                                                             \r\n 114 │ 97  │       drag: function( event ) {                                                                             \r\n 115 │ 98  │         endDragY = self.epsilonResizeHandle.globalToParentPoint( event.pointer.point ).y;                   \r\n 116 │ 99  │         var d = endDragY - startDragY;                                                                      \r\n 117 │ 100 │         startDragY = endDragY;                                                                              \r\n 118 │ 101 │         var scaleFactor = StatesOfMatterConstants.MAX_EPSILON / ( self.getGraphHeight() / 2 );              \r\n 119 │ 102 │         dualAtomModel.interactionStrengthProperty.value = dualAtomModel.getEpsilon() + ( d * scaleFactor ); \r\n\r\n\t- ../states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 70-78\r\n\t ../states-of-matter/js/phase-changes/view/EpsilonControlInteractionPotentialDiagram.js: 116-124\r\n\r\n 70 │ 116 │         startDragY = self.epsilonLine.globalToParentPoint( event.pointer.point ).y;                         \r\n 71 │ 117 │       },                                                                                                    \r\n 72 │ 118 │                                                                                                             \r\n 73 │ 119 │       drag: function( event ) {                                                                             \r\n 74 │ 120 │         endDragY = self.epsilonLine.globalToParentPoint( event.pointer.point ).y;                           \r\n 75 │ 121 │         var d = endDragY - startDragY;                                                                      \r\n 76 │ 122 │         startDragY = endDragY;                                                                              \r\n 77 │ 123 │         var scaleFactor = StatesOfMatterConstants.MAX_EPSILON / ( self.getGraphHeight() / 2 );              \r\n 78 │ 124 │         dualAtomModel.interactionStrengthProperty.value = dualAtomModel.getEpsilon() + ( d * scaleFactor ); \r\n\r\n\t- ../states-of-matter/js/phase-changes/view/PhaseChangesMoleculesControlPanel.js: 48-74\r\n\t ../states-of-matter/js/states/view/StatesMoleculesControlPanel.js: 34-60\r\n\r\n 48 │ 34 │   var ADJUSTABLE_ATTRACTION_ICON = new Circle( 6, { fill: StatesOfMatterConstants.ADJUSTABLE_ATTRACTION_COLOR } ); \r\n 49 │ 35 │                                                                                                                    \r\n 50 │ 36 │   // icon for the neon button                                                                                      \r\n 51 │ 37 │   var NEON_ICON = new Circle( 5, { fill: StatesOfMatterConstants.NEON_COLOR } );                                   \r\n 52 │ 38 │                                                                                                                    \r\n 53 │ 39 │   // icon for the argon button                                                                                     \r\n 54 │ 40 │   var ARGON_ICON = new Circle( 6, { fill: StatesOfMatterConstants.ARGON_COLOR } );                                 \r\n 55 │ 41 │                                                                                                                    \r\n 56 │ 42 │   // icon for the water button                                                                                     \r\n 57 │ 43 │   var dot1 = new Circle( 5, { fill: StatesOfMatterConstants.OXYGEN_COLOR } );                                      \r\n 58 │ 44 │   var dot2 = new Circle( 3, {                                                                                      \r\n 59 │ 45 │     fill: StatesOfMatterConstants.HYDROGEN_COLOR,                                                                  \r\n 60 │ 46 │     stroke: StatesOfMatterConstants.HYDROGEN_COLOR, bottom: dot1.top + 5, right: dot1.left + 5                     \r\n 61 │ 47 │   } );                                                                                                             \r\n 62 │ 48 │   var dot3 = new Circle( 3, {                                                                                      \r\n 63 │ 49 │     fill: StatesOfMatterConstants.HYDROGEN_COLOR,                                                                  \r\n 64 │ 50 │     stroke: StatesOfMatterConstants.HYDROGEN_COLOR, bottom: dot1.top + 5, left: dot1.right - 5                     \r\n 65 │ 51 │   } );                                                                                                             \r\n 66 │ 52 │   var WATER_ICON = new Node( { children: [ dot3, dot1, dot2 ] } );                                                 \r\n 67 │ 53 │                                                                                                                    \r\n 68 │ 54 │   // icon for the oxygen button                                                                                    \r\n 69 │ 55 │   var oxygen1 = new Circle( 5, { fill: StatesOfMatterConstants.OXYGEN_COLOR } );                                   \r\n 70 │ 56 │   var oxygen2 = new Circle( 5, { fill: StatesOfMatterConstants.OXYGEN_COLOR, left: oxygen1.right - 4 } );          \r\n 71 │ 57 │   var OXYGEN_ICON = new Node( { children: [ oxygen1, oxygen2 ] } );                                                \r\n 72 │ 58 │                                                                                                                    \r\n 73 │ 59 │   /**                                                                                                              \r\n 74 │ 60 │    * @param { MultipleParticleModel } multipleParticleModel - model of the simulation                              \r\n\r\n\t- ../states-of-matter/js/phase-changes/view/PhaseChangesScreenView.js: 141-160\r\n\t ../states-of-matter/js/states/view/StatesScreenView.js: 119-138\r\n\r\n 141 │ 119 │       listener: function() { multipleParticleModel.stepInternal( 1 / 60 ); },             \r\n 142 │ 120 │       radius: 12,                                                                         \r\n 143 │ 121 │       stroke: 'black',                                                                    \r\n 144 │ 122 │       fill: '#005566',                                                                    \r\n 145 │ 123 │       right: heaterCoolerNode.left - STEP_BUTTON_X_OFFSET,                                \r\n 146 │ 124 │       centerY: heaterCoolerNode.centerY,                                                  \r\n 147 │ 125 │       touchAreaDilation: 4                                                                \r\n 148 │ 126 │     } );                                                                                  \r\n 149 │ 127 │     this.addChild( stepButton );                                                          \r\n 150 │ 128 │                                                                                           \r\n 151 │ 129 │     var playPauseButton = new PlayPauseButton( multipleParticleModel.isPlayingProperty, { \r\n 152 │ 130 │       radius: 18, stroke: 'black',                                                        \r\n 153 │ 131 │       fill: '#005566',                                                                    \r\n 154 │ 132 │       y: stepButton.centerY,                                                              \r\n 155 │ 133 │       right: stepButton.left - 10,                                                        \r\n 156 │ 134 │       touchAreaDilation: 4                                                                \r\n 157 │ 135 │     } );                                                                                  \r\n 158 │ 136 │     this.addChild( playPauseButton );                                                     \r\n 159 │ 137 │                                                                                           \r\n 160 │ 138 │     // add bicycle pump node                                                              \r\n\r\n\t- ../states-of-matter/js/phase-changes/view/PhaseChangesScreenView.js: 313-353\r\n\t ../states-of-matter/js/states/view/StatesScreenView.js: 154-194\r\n\r\n 313 │ 154 │       self.updatePhaseDiagram();                                                                            \r\n 314 │ 155 │     } );                                                                                                    \r\n 315 │ 156 │                                                                                                             \r\n 316 │ 157 │     // if the appropriate query param is set, show some information used in debugging time step adjustments \r\n 317 │ 158 │     // TODO: Consider removing this once performance issues are worked out.                                 \r\n 318 │ 159 │     if ( StatesOfMatterQueryParameters.debugTimeStep ) {                                                    \r\n 319 │ 160 │       var keepingUpReadout = new Text( '', {                                                                \r\n 320 │ 161 │         font: new PhetFont( 20 ),                                                                           \r\n 321 │ 162 │         fill: 'red',                                                                                        \r\n 322 │ 163 │         top: 40,                                                                                            \r\n 323 │ 164 │         left: 20                                                                                            \r\n 324 │ 165 │       } );                                                                                                  \r\n 325 │ 166 │       this.addChild( keepingUpReadout );                                                                    \r\n 326 │ 167 │       multipleParticleModel.keepingUpProperty.link( function( keepingUp ) {                                 \r\n 327 │ 168 │         keepingUpReadout.text = keepingUp.toString();                                                       \r\n 328 │ 169 │       } );                                                                                                  \r\n 329 │ 170 │       var averageDtReadout = new Text( '', {                                                                \r\n 330 │ 171 │         font: new PhetFont( 20 ),                                                                           \r\n 331 │ 172 │         fill: 'red',                                                                                        \r\n 332 │ 173 │         top: keepingUpReadout.bottom + 5,                                                                   \r\n 333 │ 174 │         left: keepingUpReadout.left                                                                         \r\n 334 │ 175 │       } );                                                                                                  \r\n 335 │ 176 │       this.addChild( averageDtReadout );                                                                    \r\n 336 │ 177 │       multipleParticleModel.averageDtProperty.link( function( averageDt ) {                                 \r\n 337 │ 178 │         averageDtReadout.text = averageDt.toFixed( 3 );                                                     \r\n 338 │ 179 │       } );                                                                                                  \r\n 339 │ 180 │       var maxAdvanceTimePerStep = new Text( '', {                                                           \r\n 340 │ 181 │         font: new PhetFont( 20 ),                                                                           \r\n 341 │ 182 │         fill: 'red',                                                                                        \r\n 342 │ 183 │         top: averageDtReadout.bottom + 5,                                                                   \r\n 343 │ 184 │         left: averageDtReadout.left                                                                         \r\n 344 │ 185 │       } );                                                                                                  \r\n 345 │ 186 │       this.addChild( maxAdvanceTimePerStep );                                                               \r\n 346 │ 187 │       multipleParticleModel.maxParticleMoveTimePerStepProperty.link( function( maxAdvance ) {               \r\n 347 │ 188 │         maxAdvanceTimePerStep.text = maxAdvance.toFixed( 3 );                                               \r\n 348 │ 189 │       } );                                                                                                  \r\n 349 │ 190 │     }                                                                                                       \r\n 350 │ 191 │   }                                                                                                         \r\n 351 │ 192 │                                                                                                             \r\n 352 │ 193 │   statesOfMatter.register( 'PhaseChangesScreenView', PhaseChangesScreenView );                              \r\n 353 │ 194 │                                                                                                             \r\n```"},"comment":{"url":"https://api.github.com/repos/phetsims/states-of-matter/issues/comments/267206052","html_url":"https://github.com/phetsims/states-of-matter/issues/188#issuecomment-267206052","issue_url":"https://api.github.com/repos/phetsims/states-of-matter/issues/188","id":267206052,"user":{"login":"samreid","id":679486,"avatar_url":"https://avatars.githubusercontent.com/u/679486?v=3","gravatar_id":"","url":"https://api.github.com/users/samreid","html_url":"https://github.com/samreid","followers_url":"https://api.github.com/users/samreid/followers","following_url":"https://api.github.com/users/samreid/following{/other_user}","gists_url":"https://api.github.com/users/samreid/gists{/gist_id}","starred_url":"https://api.github.com/users/samreid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samreid/subscriptions","organizations_url":"https://api.github.com/users/samreid/orgs","repos_url":"https://api.github.com/users/samreid/repos","events_url":"https://api.github.com/users/samreid/events{/privacy}","received_events_url":"https://api.github.com/users/samreid/received_events","type":"User","site_admin":false},"created_at":"2016-12-15T01:03:44Z","updated_at":"2016-12-15T01:03:44Z","body":"In my opinion, this seems like a good opportunity to factor out code.  For the model, factoring out the code will help ensure that the AbstractPhaseStateChanger and WaterPhaseStateChanger don't accidentally diverge in the future (or if there are bugfixes they would only have to be done in one place.  In the view, factoring out the code will help ensure that the buttons maintain similar looks and styling if changed in the future.\r\n\r\nFor the model part, how about factoring out a function in AbstractPhaseStateChanger called initializeMoleculeVelocitiesAndAngles().\r\n\r\nFor the StepForwardButton and PlayPauseButton, I'd recommend to factor out StatesOfMatterStepForwardButton and StatesOfMatterPlayPauseButton which can be called from PhaseChangesScreenView and StatesScreenView.  \r\n"}}
{"actor":{"display_login":"samreid"}}