diff --git a/js/common/GasPropertiesUtils.ts b/js/common/GasPropertiesUtils.ts index c0e1fe67..238da6a4 100644 --- a/js/common/GasPropertiesUtils.ts +++ b/js/common/GasPropertiesUtils.ts @@ -60,19 +60,29 @@ const GasPropertiesUtils = { /** * Determines the position of a point that is the reflection of a specified point across a line. * Used in collision response. - * @param p - the point to reflect + * @param x - the point to reflect + * @param y - the point to reflect * @param pointOnLine - point on the line * @param lineAngle - angle of the line, in radians * @param reflectedPoint - the point to be mutated with the return value * @returns reflectedPoint mutated */ - reflectPointAcrossLine( p: Vector2, pointOnLine: Vector2, lineAngle: number, reflectedPoint: Vector2 ): Vector2 { + reflectPointAcrossLineXY( x: number, y: number, pointOnLine: Vector2, lineAngle: number, reflectedPoint: Vector2 ): Vector2 { const alpha = lineAngle % ( Math.PI * 2 ); - const gamma = Math.atan2( ( p.y - pointOnLine.y ), ( p.x - pointOnLine.x ) ) % ( Math.PI * 2 ); + const gamma = Math.atan2( ( y - pointOnLine.y ), ( x - pointOnLine.x ) ) % ( Math.PI * 2 ); const theta = ( 2 * alpha - gamma ) % ( Math.PI * 2 ); - const d = p.distance( pointOnLine ); + const d = GasPropertiesUtils.distanceXY( x, y, pointOnLine.x, pointOnLine.y ); reflectedPoint.setXY( pointOnLine.x + d * Math.cos( theta ), pointOnLine.y + d * Math.sin( theta ) ); return reflectedPoint; + }, + + /** + * Gets the distance between 2 points. + */ + distanceXY( x1: number, y1: number, x2: number, y2: number ): number { + const dx = x1 - x2; + const dy = y1 - y2; + return Math.sqrt( dx * dx + dy * dy ); } }; diff --git a/js/common/model/CollisionDetector.ts b/js/common/model/CollisionDetector.ts index c15de4db..f353329d 100644 --- a/js/common/model/CollisionDetector.ts +++ b/js/common/model/CollisionDetector.ts @@ -175,24 +175,24 @@ export default class CollisionDetector { particle.left = containerBounds.minX; // If the left wall is moving, it will do work. - particle.setVelocityXY( -( particle.velocity.x - leftWallVelocity.x ), particle.velocity.y ); + particle.setVelocityXY( -( particle.vx - leftWallVelocity.x ), particle.vy ); collided = true; } else if ( particle.right >= containerBounds.maxX ) { particle.right = containerBounds.maxX; - particle.setVelocityXY( -particle.velocity.x, particle.velocity.y ); + particle.setVelocityXY( -particle.vx, particle.vy ); collided = true; } // adjust y if ( particle.top >= containerBounds.maxY ) { particle.top = containerBounds.maxY; - particle.setVelocityXY( particle.velocity.x, -particle.velocity.y ); + particle.setVelocityXY( particle.vx, -particle.vy ); collided = true; } else if ( particle.bottom <= containerBounds.minY ) { particle.bottom = containerBounds.minY; - particle.setVelocityXY( particle.velocity.x, -particle.velocity.y ); + particle.setVelocityXY( particle.vx, -particle.vy ); collided = true; } @@ -278,11 +278,11 @@ function doParticleParticleCollisions( particles: Particle[], mutableVectors: Mu // Determine where the particles made contact. //----------------------------------------------------------------------------------------- - const dx = particle1.position.x - particle2.position.x; - const dy = particle1.position.y - particle2.position.y; - const contactRatio = particle1.radius / particle1.position.distance( particle2.position ); - const contactPointX = particle1.position.x - dx * contactRatio; - const contactPointY = particle1.position.y - dy * contactRatio; + const dx = particle1.x - particle2.x; + const dy = particle1.y - particle2.y; + const contactRatio = particle1.radius / particle1.distance( particle2 ); + const contactPointX = particle1.x - dx * contactRatio; + const contactPointY = particle1.y - dy * contactRatio; //----------------------------------------------------------------------------------------- // Adjust particle positions by reflecting across the line of impact. @@ -310,7 +310,7 @@ function doParticleParticleCollisions( particles: Particle[], mutableVectors: Mu // Compute the impulse, j. // There is no angular velocity in our model, so the denominator involves only mass. - mutableVectors.relativeVelocity.set( particle1.velocity ).subtract( particle2.velocity ); + mutableVectors.relativeVelocity.setXY( particle1.vx, particle1.vy ).subtractXY( particle2.vx, particle2.vy ); const vr = mutableVectors.relativeVelocity.dot( mutableVectors.normal ); const numerator = -vr * ( 1 + e ); const denominator = ( 1 / particle1.mass + 1 / particle2.mass ); @@ -335,23 +335,23 @@ function doParticleParticleCollisions( particles: Particle[], mutableVectors: Mu function adjustParticlePosition( particle: Particle, contactPointX: number, contactPointY: number, lineAngle: number, pointOnLine: Vector2, reflectedPoint: Vector2 ): void { - const previousDistance = particle.previousPosition.distanceXY( contactPointX, contactPointY ); + const previousDistance = GasPropertiesUtils.distanceXY( particle.previousX, particle.previousY, contactPointX, contactPointY ); const positionRatio = particle.radius / previousDistance; pointOnLine.setXY( - contactPointX - ( contactPointX - particle.previousPosition.x ) * positionRatio, - contactPointY - ( contactPointY - particle.previousPosition.y ) * positionRatio + contactPointX - ( contactPointX - particle.previousX ) * positionRatio, + contactPointY - ( contactPointY - particle.previousY ) * positionRatio ); - GasPropertiesUtils.reflectPointAcrossLine( particle.position, pointOnLine, lineAngle, reflectedPoint ); - particle.setPositionXY( reflectedPoint.x, reflectedPoint.y ); + GasPropertiesUtils.reflectPointAcrossLineXY( particle.x, particle.y, pointOnLine, lineAngle, reflectedPoint ); + particle.setXY( reflectedPoint.x, reflectedPoint.y ); } /** * Adjusts the speed of a particle in response to a collision with another particle. */ function adjustParticleSpeed( particle: Particle, scale: number, normalVector: Vector2 ): void { - const vx = normalVector.x * scale; - const vy = normalVector.y * scale; - particle.setVelocityXY( particle.velocity.x + vx, particle.velocity.y + vy ); + const vxDelta = normalVector.x * scale; + const vyDelta = normalVector.y * scale; + particle.setVelocityXY( particle.vx + vxDelta, particle.vy + vyDelta ); } gasProperties.register( 'CollisionDetector', CollisionDetector ); \ No newline at end of file diff --git a/js/common/model/Particle.ts b/js/common/model/Particle.ts index 1c527826..8930d199 100644 --- a/js/common/model/Particle.ts +++ b/js/common/model/Particle.ts @@ -12,9 +12,9 @@ */ import Bounds2 from '../../../../dot/js/Bounds2.js'; -import Vector2 from '../../../../dot/js/Vector2.js'; import { ProfileColorProperty } from '../../../../scenery/js/imports.js'; import gasProperties from '../../gasProperties.js'; +import GasPropertiesUtils from '../GasPropertiesUtils.js'; type SelfOptions = { mass: number; // AMU @@ -35,9 +35,17 @@ export default class Particle { public readonly colorProperty: ProfileColorProperty; public readonly highlightColorProperty: ProfileColorProperty; - public readonly position: Vector2; // center of the particle, pm, MUTATED! - public readonly previousPosition: Vector2; // position on previous time step, MUTATED! - public readonly velocity: Vector2; // pm/ps, initially at rest, MUTATED! + // (x,y) position of the particle, at the center of the particle + private _x: number; + private _y: number; + + // Position on the previous time step + private _previousX: number; + private _previousY: number; + + // Velocity vector components, pm/ps, initially at rest + private _vx; + private _vy; private _isDisposed: boolean; @@ -49,9 +57,14 @@ export default class Particle { this.colorProperty = providedOptions.colorProperty; this.highlightColorProperty = providedOptions.highlightColorProperty; - this.position = new Vector2( 0, 0 ); - this.previousPosition = this.position.copy(); - this.velocity = new Vector2( 0, 0 ); + this._x = 0; + this._y = 0; + + this._previousX = 0; + this._previousY = 0; + + this._vx = 0; + this._vy = 0; this._isDisposed = false; } @@ -68,36 +81,59 @@ export default class Particle { /** * ES5 getters and setters for particle position. */ - public get left(): number { return this.position.x - this.radius; } + + public get x(): number { return this._x; } + + public get y(): number { return this._y; } + + public get previousX(): number { return this._previousX; } + + public get previousY(): number { return this._previousY; } + + public get left(): number { return this._x - this.radius; } public set left( value: number ) { - this.setPositionXY( value + this.radius, this.position.y ); + this.setXY( value + this.radius, this._y ); } - public get right(): number { return this.position.x + this.radius; } + public get right(): number { return this._x + this.radius; } public set right( value: number ) { - this.setPositionXY( value - this.radius, this.position.y ); + this.setXY( value - this.radius, this._y ); } - public get top(): number { return this.position.y + this.radius; } + public get top(): number { return this._y + this.radius; } public set top( value: number ) { - this.setPositionXY( this.position.x, value - this.radius ); + this.setXY( this._x, value - this.radius ); } - public get bottom(): number { return this.position.y - this.radius; } + public get bottom(): number { return this._y - this.radius; } public set bottom( value: number ) { - this.setPositionXY( this.position.x, value + this.radius ); + this.setXY( this._x, value + this.radius ); } /** - * Gets the kinetic energy of this particle. - * @returns AMU * pm^2 / ps^2 + * ES5 getters for particle velocity. + */ + + public get vx(): number { return this._vx; } + + public get vy(): number { return this._vy; } + + /** + * Gets the particle's speed, the velocity magnitude, in pm/ps. + */ + public get speed(): number { + return Math.sqrt( this._vx * this._vx + this._vy * this._vy ); + } + + /** + * Gets the kinetic energy of this particle, in AMU * pm^2 / ps^2. */ public getKineticEnergy(): number { - return 0.5 * this.mass * this.velocity.magnitudeSquared; // KE = (1/2) * m * |v|^2 + return 0.5 * this.mass * this.speed * this.speed; // KE = (1/2) * m * |v|^2 } /** @@ -108,22 +144,32 @@ export default class Particle { assert && assert( dt > 0, `invalid dt: ${dt}` ); assert && assert( !this._isDisposed, 'attempted to step a disposed Particle' ); - this.setPositionXY( this.position.x + dt * this.velocity.x, this.position.y + dt * this.velocity.y ); + this.setXY( this._x + dt * this._vx, this._y + dt * this._vy ); + } + + /** + * Sets this particle's xy position and remembers the previous xy position. + */ + public setXY( x: number, y: number ): void { + this._previousX = this._x; + this._previousY = this._y; + this._x = x; + this._y = y; } /** - * Sets this particle's position and remembers the previous position. + * Sets this particle's x position and remembers the previous x position. */ - public setPositionXY( x: number, y: number ): void { - this.previousPosition.setXY( this.position.x, this.position.y ); - this.position.setXY( x, y ); + public setX( x: number ): void { + this.setXY( x, this._y ); } /** * Sets this particle's velocity in Cartesian coordinates. */ - public setVelocityXY( x: number, y: number ): void { - this.velocity.setXY( x, y ); + public setVelocityXY( vx: number, vy: number ): void { + this._vx = vx; + this._vy = vy; } /** @@ -137,12 +183,11 @@ export default class Particle { } /** - * Sets this particle's velocity magnitude (speed). - * @param magnitude - pm/ps + * Sets this particle's speed (velocity magnitude). */ - public setVelocityMagnitude( magnitude: number ): void { - assert && assert( magnitude >= 0, `invalid magnitude: ${magnitude}` ); - this.velocity.setMagnitude( magnitude ); + public setSpeed( speed: number ): void { + assert && assert( speed >= 0, `invalid magnitude: ${speed}` ); + this.scaleVelocity( speed / this.speed ); } /** @@ -150,14 +195,15 @@ export default class Particle { */ public scaleVelocity( scale: number ): void { assert && assert( scale > 0, `invalid scale: ${scale}` ); - this.velocity.multiply( scale ); + this._vx *= scale; + this._vy *= scale; } /** * Does this particle contact another particle now? */ public contactsParticle( particle: Particle ): boolean { - return this.position.distance( particle.position ) <= ( this.radius + particle.radius ); + return this.distance( particle ) <= ( this.radius + particle.radius ); } /** @@ -166,7 +212,21 @@ export default class Particle { * implementation, and makes the collision behavior more natural looking. */ public contactedParticle( particle: Particle ): boolean { - return this.previousPosition.distance( particle.previousPosition ) <= ( this.radius + particle.radius ); + return this.previousDistance( particle ) <= ( this.radius + particle.radius ); + } + + /** + * Distance to some other particle, in pm. + */ + public distance( particle: Particle ): number { + return GasPropertiesUtils.distanceXY( this._x, this._y, particle.x, particle.y ); + } + + /** + * Previous distance to some other particle, in pm. + */ + private previousDistance( particle: Particle ): number { + return GasPropertiesUtils.distanceXY( this._previousX, this._previousY, particle.previousX, particle.previousY ); } /** @@ -186,7 +246,7 @@ export default class Particle { * String representation of this particle. For debugging only, do not rely on format. */ public toString(): string { - return `Particle[position:(${this.position.x},${this.position.y}) mass:${this.mass} radius:${this.radius}]`; + return `Particle[position:(${this._x},${this._y}) mass:${this.mass} radius:${this.radius}]`; } } diff --git a/js/common/model/ParticleSystem.ts b/js/common/model/ParticleSystem.ts index 50444ed0..f77f5ebc 100644 --- a/js/common/model/ParticleSystem.ts +++ b/js/common/model/ParticleSystem.ts @@ -310,7 +310,7 @@ export default class ParticleSystem extends PhetioObject { const particle = createParticle(); // Position the particle just inside the container, accounting for radius. - particle.setPositionXY( this.particleEntryPosition.x - particle.radius, this.particleEntryPosition.y ); + particle.setXY( this.particleEntryPosition.x - particle.radius, this.particleEntryPosition.y ); // Initial speed, |v| = sqrt( 3kT / m ) const speed = Math.sqrt( 3 * GasPropertiesConstants.BOLTZMANN * temperatures[ i ] / particle.mass ); @@ -353,7 +353,7 @@ export default class ParticleSystem extends PhetioObject { const actualParticleKE = particle.getKineticEnergy(); const desiredParticleKE = ratio * actualParticleKE; const desiredSpeed = Math.sqrt( 2 * desiredParticleKE / particle.mass ); // |v| = Math.sqrt( 2 * KE / m ) - particle.setVelocityMagnitude( desiredSpeed ); + particle.setSpeed( desiredSpeed ); } } } diff --git a/js/common/model/ParticleUtils.ts b/js/common/model/ParticleUtils.ts index 9d57d2d0..8fa2da3c 100644 --- a/js/common/model/ParticleUtils.ts +++ b/js/common/model/ParticleUtils.ts @@ -81,7 +81,7 @@ const ParticleUtils = { assert && assert( scaleX > 0, `invalid scaleX: ${scaleX}` ); for ( let i = particles.length - 1; i >= 0; i-- ) { - particles[ i ].position.setX( scaleX * particles[ i ].position.x ); + particles[ i ].setX( scaleX * particles[ i ].x ); } }, @@ -147,7 +147,7 @@ const ParticleUtils = { let totalMass = 0; for ( let i = particles.length - 1; i >= 0; i-- ) { const particle = particles[ i ]; - numerator += ( particle.mass * particle.position.x ); + numerator += ( particle.mass * particle.x ); totalMass += particle.mass; } centerXOfMass = numerator / totalMass; diff --git a/js/common/view/ParticlePositionsNode.ts b/js/common/view/ParticlePositionsNode.ts index 6c305c2c..a5336861 100644 --- a/js/common/view/ParticlePositionsNode.ts +++ b/js/common/view/ParticlePositionsNode.ts @@ -30,8 +30,8 @@ export default class ParticlePositionsNode extends Path { public update(): void { const shape = new Shape(); this.particleArrays.forEach( particleArray => particleArray.forEach( particle => { - const x = this.modelViewTransform.modelToViewX( particle.position.x ); - const y = this.modelViewTransform.modelToViewY( particle.position.y ); + const x = this.modelViewTransform.modelToViewX( particle.x ); + const y = this.modelViewTransform.modelToViewY( particle.y ); shape.circle( x, y, 1 ); } ) ); this.shape = shape; diff --git a/js/common/view/ParticlesNode.ts b/js/common/view/ParticlesNode.ts index 491dbb2b..4bd73a7d 100644 --- a/js/common/view/ParticlesNode.ts +++ b/js/common/view/ParticlesNode.ts @@ -105,8 +105,8 @@ export default class ParticlesNode extends Sprites { // For the next SpriteInstance, set its Sprite, and transform it to the particle's position. const spriteInstance = this.spriteInstances[ spriteInstancesIndex++ ]; spriteInstance.sprite = sprite; - spriteInstance.matrix.set02( this.modelViewTransform.modelToViewX( particle.position.x ) ); - spriteInstance.matrix.set12( this.modelViewTransform.modelToViewY( particle.position.y ) ); + spriteInstance.matrix.set02( this.modelViewTransform.modelToViewX( particle.x ) ); + spriteInstance.matrix.set12( this.modelViewTransform.modelToViewY( particle.y ) ); } } diff --git a/js/diffusion/model/DiffusionData.ts b/js/diffusion/model/DiffusionData.ts index a6338bc5..fad96ec5 100644 --- a/js/diffusion/model/DiffusionData.ts +++ b/js/diffusion/model/DiffusionData.ts @@ -92,7 +92,7 @@ export default class DiffusionData { // Contribution by DiffusionParticle1 species for ( let i = particles1.length - 1; i >= 0; i-- ) { const particle = particles1[ i ]; - if ( this.bounds.containsPoint( particle.position ) ) { + if ( this.bounds.containsCoordinates( particle.x, particle.y ) ) { numberOfParticles1++; totalKE += particle.getKineticEnergy(); } @@ -102,7 +102,7 @@ export default class DiffusionData { // Note that there's a wee bit of code duplication here, but it gains us some iteration efficiency. for ( let i = particles2.length - 1; i >= 0; i-- ) { const particle = particles2[ i ]; - if ( this.bounds.containsPoint( particle.position ) ) { + if ( this.bounds.containsCoordinates( particle.x, particle.y ) ) { numberOfParticles2++; totalKE += particle.getKineticEnergy(); } diff --git a/js/diffusion/model/DiffusionModel.ts b/js/diffusion/model/DiffusionModel.ts index 3fa1397f..1c970f63 100644 --- a/js/diffusion/model/DiffusionModel.ts +++ b/js/diffusion/model/DiffusionModel.ts @@ -318,8 +318,8 @@ function addParticles( n: number, positionBounds: Bounds2, settings: DiffusionSe // Position the particle at a random position within positionBounds, accounting for particle radius. const x = dotRandom.nextDoubleBetween( positionBounds.minX + particle.radius, positionBounds.maxX - particle.radius ); const y = dotRandom.nextDoubleBetween( positionBounds.minY + particle.radius, positionBounds.maxY - particle.radius ); - particle.setPositionXY( x, y ); - assert && assert( positionBounds.containsPoint( particle.position ), 'particle is outside of positionBounds' ); + particle.setXY( x, y ); + assert && assert( positionBounds.containsCoordinates( particle.x, particle.y ), 'particle is outside of positionBounds' ); // Set the initial velocity, based on initial temperature and mass. particle.setVelocityPolar( @@ -346,7 +346,7 @@ function updateMassAndTemperature( mass: number, temperature: number, particles: particles[ i ].mass = mass; // |v| = sqrt( 3kT / m ) - particles[ i ].setVelocityMagnitude( Math.sqrt( 3 * GasPropertiesConstants.BOLTZMANN * temperature / mass ) ); + particles[ i ].setSpeed( Math.sqrt( 3 * GasPropertiesConstants.BOLTZMANN * temperature / mass ) ); } } diff --git a/js/diffusion/model/ParticleFlowRateModel.ts b/js/diffusion/model/ParticleFlowRateModel.ts index 6238ede1..a5bf5958 100644 --- a/js/diffusion/model/ParticleFlowRateModel.ts +++ b/js/diffusion/model/ParticleFlowRateModel.ts @@ -101,10 +101,10 @@ export default class ParticleFlowRateModel { let rightCount = 0; // --> for ( let i = this.particles.length - 1; i >= 0; i-- ) { const particle = this.particles[ i ]; - if ( particle.previousPosition.x >= this.dividerX && particle.position.x < this.dividerX ) { + if ( particle.previousX >= this.dividerX && particle.x < this.dividerX ) { leftCount++; } - else if ( particle.previousPosition.x <= this.dividerX && particle.position.x > this.dividerX ) { + else if ( particle.previousX <= this.dividerX && particle.x > this.dividerX ) { rightCount++; } } diff --git a/js/energy/model/AverageSpeedModel.ts b/js/energy/model/AverageSpeedModel.ts index 16a51624..3fd2d119 100644 --- a/js/energy/model/AverageSpeedModel.ts +++ b/js/energy/model/AverageSpeedModel.ts @@ -184,7 +184,7 @@ function getAverageSpeed( particles: Particle[] ): number { if ( particles.length > 0 ) { let totalSpeed = 0; for ( let i = particles.length - 1; i >= 0; i-- ) { - totalSpeed += particles[ i ].velocity.magnitude; + totalSpeed += particles[ i ].speed; } averageSpeed = totalSpeed / particles.length; } diff --git a/js/energy/model/HistogramsModel.ts b/js/energy/model/HistogramsModel.ts index 1d08d05d..48fedbce 100644 --- a/js/energy/model/HistogramsModel.ts +++ b/js/energy/model/HistogramsModel.ts @@ -306,7 +306,7 @@ export default class HistogramsModel { function getSpeedValues( particles: Particle[] ): number[] { const values = []; for ( let i = particles.length - 1; i >= 0; i-- ) { - values.push( particles[ i ].velocity.magnitude ); + values.push( particles[ i ].speed ); } return values; }