Skip to content

Commit

Permalink
add positionOffset and dimensions to the Representation (see #24 and #…
Browse files Browse the repository at this point in the history
  • Loading branch information
veillette committed Jul 27, 2021
1 parent 05fad6f commit dba3ffc
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 108 deletions.
4 changes: 2 additions & 2 deletions js/common/model/GeometricOpticsModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ class GeometricOpticsModel {
this.secondFocalPoint = new FocalPoint( this.optic.positionProperty, this.optic.focalLengthProperty, tandem, { multiplicativeFactor: -1 } );

// @public {Target} target/ image
this.firstTarget = new Target( this.sourceObject.firstPositionProperty, this.optic, tandem );
this.firstTarget = new Target( this.sourceObject.firstPositionProperty, this.optic, this.representationProperty, tandem );

// @public {Target} target/ image associated with the second source
this.secondTarget = new Target( this.sourceObject.secondPositionProperty, this.optic, tandem );
this.secondTarget = new Target( this.sourceObject.secondPositionProperty, this.optic, this.representationProperty, tandem );

// @public {ProjectorScreen}
this.projectorScreen = new ProjectorScreen(
Expand Down
14 changes: 7 additions & 7 deletions js/common/model/Representation.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import pencilLogoImage from '../../../images/pencil-logo_png.js';
import projectorScreen3dImage from '../../../images/projector-screen-3d_png.js';
import rocket3dLeftFacingInvertedImage from '../../../images/rocket-3d-left-facing-inverted_png.js';
import rocket3dLeftFacingUprightImage from '../../../images/rocket-3d-left-facing-upright_png.js';
import rocket3dRightFacingUprightImage from '../../../images/rocket-3d-right-facing-upright_png.js';
import rocket3dRightFacingInvertedImage from '../../../images/rocket-3d-right-facing-inverted_png.js';
import rocket3dRightFacingUprightImage from '../../../images/rocket-3d-right-facing-upright_png.js';
import rocketLogoImage from '../../../images/rocket-logo_png.js';
import tree3dLeftFacingInvertedImage from '../../../images/tree-3d-left-facing-inverted_png.js';
import tree3dLeftFacingUprightImage from '../../../images/tree-3d-left-facing-upright_png.js';
import tree3dRightFacingUprightImage from '../../../images/tree-3d-right-facing-upright_png.js';
import tree3dRightFacingInvertedImage from '../../../images/tree-3d-right-facing-inverted_png.js';
import tree3dRightFacingUprightImage from '../../../images/tree-3d-right-facing-upright_png.js';
import treeLogoImage from '../../../images/tree-logo_png.js';
import geometricOptics from '../../geometricOptics.js';
import geometricOpticsStrings from '../../geometricOpticsStrings.js';
Expand All @@ -45,7 +45,7 @@ class RepresentationGenerator {
* @param {HTMLImageElement} rightFacingInverted
* @param {HTMLImageElement} leftFacingUpright
* @param {HTMLImageElement} leftFacingInverted
* @param {Dimensions2} dimensions
* @param {Dimension2} dimensions
* @param {Vector2} leftFacingUprightOffsetPosition
* @param {string} label
* @param {boolean} isObject
Expand Down Expand Up @@ -87,31 +87,31 @@ const Representation = Enumeration.byMap( {
pencil3dLeftFacingUprightImage,
pencil3dLeftFacingInvertedImage,
new Dimension2( 111, 365 ),
new Vector2( -32, 35 ),
new Vector2( -64, 70 ),
pencilString, true ),
TREE: new RepresentationGenerator( treeLogoImage,
tree3dRightFacingUprightImage,
tree3dRightFacingInvertedImage,
tree3dLeftFacingUprightImage,
tree3dLeftFacingInvertedImage,
new Dimension2( 135, 391 ),
new Vector2( -40, 44 ),
new Vector2( -80, 88 ),
treeString, true ),
ROCKET: new RepresentationGenerator( rocketLogoImage,
rocket3dRightFacingUprightImage,
rocket3dRightFacingInvertedImage,
rocket3dLeftFacingUprightImage,
rocket3dLeftFacingInvertedImage,
new Dimension2( 116, 414 ),
new Vector2( -34, 56 ),
new Vector2( -68, 112 ),
rocketString, true ),
LIGHT: new RepresentationGenerator( lampBlueLogoImage,
lampBlueImage,
lampBlueImage,
projectorScreen3dImage,
projectorScreen3dImage,
new Dimension2( 100, 100 ),
new Vector2( -33, 14 ),
new Vector2( -66, 28 ),
lightString, false, { source: lampRedImage } )
} );

Expand Down
19 changes: 7 additions & 12 deletions js/common/model/SourceObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import GeometricOpticsConstants from '../GeometricOpticsConstants.js';
const DEFAULT_SOURCE_POINT_1 = GeometricOpticsConstants.DEFAULT_SOURCE_POINT_1;
const DEFAULT_SOURCE_POINT_2 = GeometricOpticsConstants.DEFAULT_SOURCE_POINT_2;
const verticalOffsetRange = new RangeWithValue( -50, 0, -30 ); // in centimeters
const OBJECT_SCALE_FACTOR = 2;
const SOURCE_SCALE_FACTOR = 1;
const OBJECT_SCALE_FACTOR = 4;
const SOURCE_SCALE_FACTOR = 2;

class SourceObject {

Expand All @@ -33,7 +33,6 @@ class SourceObject {
constructor( opticPositionProperty, representationProperty, tandem ) {
assert && assert( tandem instanceof Tandem, 'invalid tandem' );


const scale = representationProperty.value.isObject ? OBJECT_SCALE_FACTOR : SOURCE_SCALE_FACTOR;

// @public {Vector2} displacement vector from the firstPosition to the left top - value depends on representation
Expand All @@ -48,15 +47,11 @@ class SourceObject {
return leftTop.minus( this.offsetPosition );
} );

this.imageDimensionsProperty = new DerivedProperty( [ representationProperty ], representation => {
const scale = representation.isObject ? OBJECT_SCALE_FACTOR : SOURCE_SCALE_FACTOR;
return new Dimension2( representation.dimensions.width / scale,
representation.dimensions.height / scale );
} );


this.boundsProperty = new DerivedProperty( [ this.leftTopProperty, this.imageDimensionsProperty ],
( leftTop, dimensions ) => {
this.boundsProperty = new DerivedProperty( [ this.leftTopProperty, representationProperty ],
( leftTop, representation ) => {
const scale = representation.isObject ? OBJECT_SCALE_FACTOR : SOURCE_SCALE_FACTOR;
const dimensions = new Dimension2( representation.dimensions.width / scale,
representation.dimensions.height / scale );
return dimensions.toBounds( leftTop.x, leftTop.y - dimensions.height );
} );

Expand Down
46 changes: 44 additions & 2 deletions js/common/model/Target.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@
*/

import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import Tandem from '../../../../tandem/js/Tandem.js';
import geometricOptics from '../../geometricOptics.js';

const OBJECT_SCALE_FACTOR = 4;

class Target {

/**
* @param {Property.<Vector2>} objectPositionProperty
* @param {Optic} optic
* @param {Property.<representation>} representationProperty
* @param {Tandem} tandem
*/
constructor( objectPositionProperty, optic, tandem ) {
constructor( objectPositionProperty, optic, representationProperty, tandem ) {
assert && assert( tandem instanceof Tandem, 'invalid tandem' );

// @private {Property.<Vector2>}
Expand Down Expand Up @@ -56,7 +60,6 @@ class Target {
return this.getPosition( objectPosition, opticPosition, focalLength );
} );


// @public (read-only) {Property.<number>}
this.scaleProperty = new DerivedProperty( [ objectPositionProperty,
optic.positionProperty,
Expand All @@ -81,6 +84,33 @@ class Target {
return this.isVirtual();
} );

this.boundsProperty = new DerivedProperty( [
this.positionProperty,
representationProperty,
this.scaleProperty,
this.isInvertedProperty ],
( position, representation, scale ) => {

// @public {Vector2} displacement vector from the firstPosition to the left top - value depends on representation
// values are in centimeters
const initialOffsetPosition = representation.offsetPosition.timesScalar( 1 / OBJECT_SCALE_FACTOR );
const initialWidth = representation.dimensions.width / OBJECT_SCALE_FACTOR;
const initialHeight = representation.dimensions.height / OBJECT_SCALE_FACTOR;

const offsetPosition = initialOffsetPosition.timesScalar( scale );
const width = initialWidth * scale;
const height = initialHeight * scale;

const x1 = ( offsetPosition.x ) * this.opticGetTypeSign();
const x2 = ( offsetPosition.x + width ) * this.opticGetTypeSign();
const y1 = offsetPosition.y;
const y2 = offsetPosition.y - height;

const bounds = new Bounds2( Math.min( x1, x2 ), Math.min( y1, y2 ), Math.max( x1, x2 ), Math.max( y1, y2 ) );

return bounds.shifted( position );
} );


// light intensity of the image (Hollywood) - a value between 0 and 1
// @public (read-only) {Property.<number>}
Expand All @@ -89,6 +119,18 @@ class Target {
const diameterFactor = optic.getNormalizedDiameter( diameter );
return distanceFactor * diameterFactor;
} );


// {Property.<HTMLImageElement>}
this.imageProperty = new DerivedProperty( [ representationProperty, this.isVirtualProperty ],
( representation, isVirtual ) => {
const realImage = optic.isLens() ? representation.leftFacingInverted :
representation.rightFacingInverted;
const virtualImage = optic.isLens() ? representation.rightFacingUpright :
representation.leftFacingUpright;
return isVirtual ? virtualImage : realImage;
} );

}

/**
Expand Down
15 changes: 10 additions & 5 deletions js/common/view/LabelsNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,26 @@ class LabelsNode extends Node {
} );

// define image label position
const imageLabelPositionProperty = new DerivedProperty( [ model.firstTarget.positionProperty ],
position => position.minusXY( 0, 14 ) );
const imageLabelPositionProperty = new DerivedProperty( [ model.firstTarget.boundsProperty ],
bounds => bounds.centerTop );

// create image label
const imageLabel = new LabelNode( imageString, imageLabelPositionProperty, new BooleanProperty( true ), modelViewTransformProperty );

// define object label position
const objectLabelPositionProperty = new DerivedProperty( [ model.sourceObject.firstPositionProperty ],
position => position.minusXY( 0, 66 ) );
const objectLabelPositionProperty = new DerivedProperty( [ model.sourceObject.boundsProperty ],
// because the we use a Y inverted reference frame, the bottom of the image is the top of the model bounds.
bounds => bounds.centerTop );

// create object label
const objectLabel = new LabelNode( objectString, objectLabelPositionProperty, new BooleanProperty( true ), modelViewTransformProperty );

// update the visibility of the object and image labels
Property.multilink( [ model.representationProperty, model.enableFirstTargetProperty, model.firstTarget.isVirtualProperty, visibleProperties.visibleVirtualImageProperty ],
Property.multilink( [
model.representationProperty,
model.enableFirstTargetProperty,
model.firstTarget.isVirtualProperty,
visibleProperties.visibleVirtualImageProperty ],
( representation, isEnabled, isVirtual, showVirtual ) => {
objectLabel.visible = representation.isObject;
imageLabel.visible = isEnabled && ( isVirtual ? showVirtual : true ) && representation.isObject;
Expand Down
25 changes: 17 additions & 8 deletions js/common/view/SourceObjectNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const SECOND_SOURCE_POINT_OPTIONS = GeometricOpticsConstants.SECOND_SOURCE_POINT
const SECOND_SOURCE_POINT_FILL = geometricOpticsColorProfile.secondSourcePointFillProperty;
const SECOND_SOURCE_POINT_STROKE = geometricOpticsColorProfile.secondSourcePointStrokeProperty;

const OVERALL_SCALE_FACTOR = 0.5;
const OVERALL_SCALE_FACTOR = 1;
const LIGHT_OFFSET_VECTOR = new Vector2( 50, -23 ); // in model coordinates
const CUEING_ARROW_LENGTH = 20;
const CUEING_ARROW_OPTIONS = {
Expand Down Expand Up @@ -68,14 +68,18 @@ class SourceObjectNode extends Node {
this.addChild( sourceObjectImage );

/**
* scale image to bounds
* scale image to size of model bounds
* @param {Node} image
* @param {Bounds2} bounds
*/
const scaleFunction = ( image, bounds ) => {
const initialWidth = sourceObjectImage.width;
const initialHeight = sourceObjectImage.height;
image.scale( bounds.width / initialWidth, bounds.height / initialHeight );

// bounds that we want for the image
const viewBounds = modelViewTransform.modelToViewBounds( bounds );
image.scale( viewBounds.width / initialWidth,
viewBounds.height / initialHeight );
};

/**
Expand All @@ -87,14 +91,15 @@ class SourceObjectNode extends Node {
};

// keep at least half of the projector screen within visible bounds and right of the optic
const dragBoundsProperty = new DerivedProperty( [ visibleModelBoundsProperty ],
const dragBoundsProperty = new DerivedProperty( [ visibleModelBoundsProperty, representationProperty ],
visibleBounds => {
return new Bounds2( visibleBounds.minX,
visibleBounds.minY - sourceObject.boundsProperty.value.height / 2,
sourceObject.getOpticPosition().x - sourceObject.boundsProperty.value.width / 2,
visibleBounds.minY + sourceObject.boundsProperty.value.height,
sourceObject.getOpticPosition().x - sourceObject.boundsProperty.value.width,
visibleBounds.maxY );
} );


// create drag listener for source
const sourceObjectDragListener = new DragListener( {
positionProperty: sourceObject.leftTopProperty,
Expand All @@ -110,6 +115,12 @@ class SourceObjectNode extends Node {
setImagePosition( sourceObjectImage, position );
} );


dragBoundsProperty.link( dragBounds => {
sourceObject.leftTopProperty.value = dragBounds.closestPointTo( sourceObject.leftTopProperty.value );
} );


// create a node to hold the second source
const secondNode = new Node();
this.addChild( secondNode );
Expand Down Expand Up @@ -183,8 +194,6 @@ class SourceObjectNode extends Node {
secondNode.touchArea = circleIcon.bounds.dilated( 10 );
secondNode.addChild( this.cueingArrowsLayer );

sourceObjectImage.setScaleMagnitude( OVERALL_SCALE_FACTOR );

// address position of source of light #79
}
else {
Expand Down
Loading

0 comments on commit dba3ffc

Please sign in to comment.