Skip to content

Commit

Permalink
performance improvements for SpriteInstance hierarchy, #60
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelzoom committed Aug 26, 2020
1 parent ce1648c commit 55393e7
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 44 deletions.
3 changes: 1 addition & 2 deletions js/common/view/environment/BunnySelectionRectangleSprite.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ class BunnySelectionRectangleSprite extends Sprite {

/**
* @param {HTMLImageElement} maxImage - the largest bunny image
* @param {Object} [options]
*/
constructor( maxImage, options ) {
constructor( maxImage ) {

assert && assert( maxImage instanceof HTMLImageElement, 'invalid maxImage' );
assert && assert( maxImage.width > 0 && maxImage.height > 0, 'maxImage does not have valid dimensions' );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ class BunnySelectionRectangleSpriteInstance extends OrganismSpriteInstance {
assert && assert( bunny instanceof Bunny, 'invalid bunny' );
assert && assert( sprite instanceof BunnySelectionRectangleSprite, 'invalid sprite' );

super( bunny, sprite, {
baseScale: NaturalSelectionConstants.BUNNY_IMAGE_SCALE
} );
super( bunny, sprite, NaturalSelectionConstants.BUNNY_IMAGE_SCALE );
}
}

Expand Down
4 changes: 1 addition & 3 deletions js/common/view/environment/BunnySpriteInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ class BunnySpriteInstance extends OrganismSpriteInstance {
assert && assert( bunny instanceof Bunny, 'invalid bunny' );
assert && assert( sprite instanceof Sprite, 'invalid sprite' );

super( bunny, sprite, {
baseScale: NaturalSelectionConstants.BUNNY_IMAGE_SCALE
} );
super( bunny, sprite, NaturalSelectionConstants.BUNNY_IMAGE_SCALE );
}
}

Expand Down
86 changes: 56 additions & 30 deletions js/common/view/environment/OrganismSpriteInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,68 +8,94 @@
* @author Chris Malley (PixelZoom, Inc.)
*/

import Multilink from '../../../../../axon/js/Multilink.js';
import merge from '../../../../../phet-core/js/merge.js';
import Sprite from '../../../../../scenery/js/util/Sprite.js';
import SpriteInstance from '../../../../../scenery/js/util/SpriteInstance.js';
import naturalSelection from '../../../naturalSelection.js';
import Organism from '../../model/Organism.js';
import XDirection from '../../model/XDirection.js';
import NaturalSelectionUtils from '../../NaturalSelectionUtils.js';

class OrganismSpriteInstance extends SpriteInstance {

/**
* @param {Organism} organism
* @param {Sprite} sprite
* @param {Object} [options]
* @param {number} baseScale - the base amount to scale, tuned based on the PNG file dimensions
*/
constructor( organism, sprite, options ) {
constructor( organism, sprite, baseScale ) {
// args are validated by initialize

assert && assert( organism instanceof Organism, 'invalid organism' );
assert && assert( sprite instanceof Sprite, 'invalid sprite' );
super();

options = merge( {
baseScale: 1 // the base amount to scale, tuned based on the PNG file dimensions
}, options );
// Set field in super SpriteInstance. Every Organism needs to be both translated and scaled
// because the view is a 2D projection of a 3D model position.
this.transformType = SpriteInstance.TransformType.TRANSLATION_AND_SCALE;

super();
// @private
this.organismListener = this.updateMatrix.bind( this );

this.initialize( organism, sprite, baseScale );
}

/**
* Initializes the OrganismSpriteInstance. This is factored out of the constructor (and is public) in case
* we ever want to leverage SpriteInstance's Poolable features.
* @param organism
* @param sprite
* @param baseScale
* @protected for use by Poolable
*/
initialize( organism, sprite, baseScale ) {

assert && assert( organism instanceof Organism, 'invalid organism' );
assert && assert( sprite instanceof Sprite, 'invalid sprite' );
assert && assert( NaturalSelectionUtils.isPositive( baseScale ), `invalid baseScale: ${baseScale}` );

// Set fields in super SpriteInstance
this.sprite = sprite;
this.transformType = SpriteInstance.TransformType.TRANSLATION_AND_SCALE;

// @public (read-only)
this.organism = organism;

// Update position and direction. Must be disposed.
const multilink = new Multilink(
[ organism.positionProperty, organism.xDirectionProperty ],
( position, xDirection ) => {
// @private
this.baseScale = baseScale;

// compute scale and position, in view coordinates
const viewScale = options.baseScale * organism.modelViewTransform.getViewScale( position.z );
const viewX = organism.modelViewTransform.modelToViewX( position );
const viewY = organism.modelViewTransform.modelToViewY( position );
// Update position and direction, unlink in dispose. Do not use a Multilink or define disposeOrganismSpriteInstance
// because we will be creating a large number of OrganismSpriteInstance instances.
assert && assert( this.organismListener, 'organismListener should exist by now' );
this.organism.positionProperty.link( this.organismListener );
this.organism.xDirectionProperty.link( this.organismListener );
}

// update the matrix in the most efficient way possible
this.matrix.set00( viewScale * XDirection.toSign( xDirection ) ); // reflected to account for x direction
this.matrix.set11( viewScale );
this.matrix.set02( viewX );
this.matrix.set12( viewY );
} );
/**
* Updates the matrix to match the organism's position and xDirection.
* @private
*/
updateMatrix() {

// @private
this.disposeOrganismSpriteInstance = () => {
multilink.dispose();
};
const position = this.organism.positionProperty.value;
const xDirection = this.organism.xDirectionProperty.value;

// compute scale and position, in view coordinates
const viewScale = this.baseScale * this.organism.modelViewTransform.getViewScale( position.z );
const viewX = this.organism.modelViewTransform.modelToViewX( position );
const viewY = this.organism.modelViewTransform.modelToViewY( position );

// update the matrix in the most efficient way possible
this.matrix.set00( viewScale * XDirection.toSign( xDirection ) ); // reflected to account for x direction
this.matrix.set11( viewScale );
this.matrix.set02( viewX );
this.matrix.set12( viewY );
assert && assert( this.matrix.isFinite(), 'matrix should be finite' );
}

/**
* @public
* @override
*/
dispose() {
this.disposeOrganismSpriteInstance();
this.organism.positionProperty.unlink( this.organismListener );
this.organism.xDirectionProperty.unlink( this.organismListener );
super.dispose && super.dispose();
}
}
Expand Down
4 changes: 1 addition & 3 deletions js/common/view/environment/ShrubSpriteInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ class ShrubSpriteInstance extends OrganismSpriteInstance {
assert && assert( tenderSprite instanceof Sprite, 'invalid tenderSprite' );
assert && assert( toughSprite instanceof Sprite, 'invalid toughSprite' );

super( shrub, isTough ? toughSprite : tenderSprite, {
baseScale: NaturalSelectionConstants.SHRUB_IMAGE_SCALE
} );
super( shrub, ( isTough ? toughSprite : tenderSprite ), NaturalSelectionConstants.SHRUB_IMAGE_SCALE );

// @private
this.tenderSprite = tenderSprite;
Expand Down
4 changes: 1 addition & 3 deletions js/common/view/environment/WolfSpriteInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ class WolfSpriteInstance extends OrganismSpriteInstance {
assert && assert( wolf instanceof Wolf, 'invalid wolf' );
assert && assert( sprite instanceof Sprite, 'invalid sprite' );

super( wolf, sprite, {
baseScale: NaturalSelectionConstants.WOLF_IMAGE_SCALE
} );
super( wolf, sprite, NaturalSelectionConstants.WOLF_IMAGE_SCALE );
}
}

Expand Down

0 comments on commit 55393e7

Please sign in to comment.