Skip to content

Commit

Permalink
optimizations for OpticalAxisForegroundNode clipArea, #286
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelzoom committed Jan 12, 2022
1 parent 01d2693 commit 16d7973
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 53 deletions.
23 changes: 16 additions & 7 deletions js/common/view/GOScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,16 +269,25 @@ class GOScreenView extends ScreenView {
const opticNode = options.createOpticNode( model.optic, modelBoundsProperty, modelViewTransform, options.tandem );

// The complete optical axis, to be put in the background
const opticalAxisNode = new OpticalAxisNode( model.optic.positionProperty, modelBoundsProperty, modelViewTransform, {
const opticalAxisNode = new OpticalAxisNode(
model.optic.positionProperty,
this.visibleBoundsProperty,
modelViewTransform, {
visibleProperty: model.optic.opticalAxisVisibleProperty
} );

// The part of the optical axis that appears to be in front of the things. This makes it looks like
// the axis is going through the source object, real/virtual image, etc.
const opticalAxisForegroundNode = new OpticalAxisForegroundNode( model.lightRays1.raysProcessedEmitter,
model.optic.positionProperty, model.representationProperty, model.sourceObject.positionProperty,
model.firstTarget.positionProperty, model.barrier, modelBoundsProperty, modelViewTransform,
targetNode.boundsProperty, {
// The parts of the optical axis that appear to be in front of Nodes that have 3D perspective.
const opticalAxisForegroundNode = new OpticalAxisForegroundNode(
model.optic.positionProperty,
this.visibleBoundsProperty,
modelViewTransform,
model.lightRays1.raysProcessedEmitter,
model.representationProperty,
model.sourceObject.positionProperty,
sourceObjectNode,
model.firstTarget.positionProperty,
targetNode,
model.barrier, {
visibleProperty: model.optic.opticalAxisVisibleProperty
} );

Expand Down
74 changes: 33 additions & 41 deletions js/common/view/OpticalAxisForegroundNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* @author Chris Malley (PixelZoom, Inc.)
*/

import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import Vector2 from '../../../../dot/js/Vector2.js';
Expand All @@ -25,30 +24,33 @@ import Representation from '../model/Representation.js';
import OpticalAxisNode, { OpticalAxisNodeOptions } from './OpticalAxisNode.js';
import GOQueryParameters from '../GOQueryParameters.js';
import Emitter from '../../../../axon/js/Emitter.js';
import { Node } from '../../../../scenery/js/imports.js';

class OpticalAxisForegroundNode extends OpticalAxisNode {

/**
* @param lightRaysProcessedEmitter
* @param opticPositionProperty
* @param visibleBoundsProperty
* @param modelViewTransform
* @param lightRaysProcessedEmitter
* @param representationProperty
* @param sourceObjectPositionProperty
* @param sourceObjectNode
* @param targetPositionProperty
* @param targetNode
* @param barrier
* @param modelBoundsProperty
* @param modelViewTransform
* @param targetNodeBoundsProperty
* @param providedOptions
*/
constructor( lightRaysProcessedEmitter: Emitter<[]>,
opticPositionProperty: IReadOnlyProperty<Vector2>,
constructor( opticPositionProperty: IReadOnlyProperty<Vector2>,
visibleBoundsProperty: IReadOnlyProperty<Bounds2>,
modelViewTransform: ModelViewTransform2,
lightRaysProcessedEmitter: Emitter<[]>,
representationProperty: IReadOnlyProperty<Representation>,
sourceObjectPositionProperty: IReadOnlyProperty<Vector2>,
sourceObjectNode: Node,
targetPositionProperty: IReadOnlyProperty<Vector2>,
targetNode: Node,
barrier: Barrier | null,
modelBoundsProperty: IReadOnlyProperty<Bounds2>,
modelViewTransform: ModelViewTransform2,
targetNodeBoundsProperty: IReadOnlyProperty<Bounds2>,
providedOptions: OpticalAxisNodeOptions ) {

const options = merge( {}, providedOptions ) as OpticalAxisNodeOptions;
Expand All @@ -57,14 +59,7 @@ class OpticalAxisForegroundNode extends OpticalAxisNode {
}

// create optical axis line, with arbitrary length values.
super( opticPositionProperty, modelBoundsProperty, modelViewTransform, options );

// If there is no barrier (as in the Mirror screen), then create an artificial barrier position that is just
// to the right of the model bounds, and therefore out of view.
const barrierPositionProperty = barrier ?
barrier.positionProperty :
new DerivedProperty( [ modelBoundsProperty ], ( modelBounds: Bounds2 ) =>
new Vector2( modelBounds.right + 1, modelBounds.centerX ) );
super( opticPositionProperty, visibleBoundsProperty, modelViewTransform, options );

// Update the clipArea, to make the axis look like it passes through things.
// This shows only the parts of this Node that are in the foreground, i.e. not occluded by other things.
Expand All @@ -73,43 +68,39 @@ class OpticalAxisForegroundNode extends OpticalAxisNode {
// don't end up computing intermediate states as things move around.
const updateClipArea = () => {

const opticPosition = opticPositionProperty.value;
const sourceObjectPosition = sourceObjectPositionProperty.value;
const targetPosition = targetPositionProperty.value;
const viewBounds = modelViewTransform.modelToViewBounds( modelBoundsProperty.value );
const minY = viewBounds.minY;
const maxY = viewBounds.maxY;
let clipArea: Shape; // in view coordinates

const minY = visibleBoundsProperty.value.minY;
const maxY = visibleBoundsProperty.value.maxY;
const clipHeight = maxY - minY;

let clipArea: Shape;
if ( representationProperty.value.isObject ) {

const opticX = modelViewTransform.modelToViewX( opticPositionProperty.value.x );
const sourceObjectX = modelViewTransform.modelToViewX( sourceObjectPositionProperty.value.x );
const targetX = modelViewTransform.modelToViewX( targetPositionProperty.value.x );

// For a source object...
if ( targetPosition.x > opticPosition.x ) {
if ( targetX > opticX ) {

// If the image is to the right of the optic, clipArea is 1 rectangle, between the object and image.
const minX = modelViewTransform.modelToViewX( sourceObjectPosition.x );
const maxX = modelViewTransform.modelToViewX( targetPosition.x );
const clipWidth = maxX - minX;
clipArea = Shape.rectangle( minX, minY, clipWidth, clipHeight );
clipArea = Shape.rectangle( sourceObjectX, minY, targetX - sourceObjectX, clipHeight );
}
else {

// If the image is to the left of the optic, clipArea requires 2 rectangles.

// Determine the relative position of the source object and image.
const targetToRight = ( targetPosition.x > sourceObjectPosition.x );
const leftPosition = targetToRight ? sourceObjectPosition : targetPosition;
const rightPosition = targetToRight ? targetPosition : sourceObjectPosition;
const targetOnRight = ( targetX > sourceObjectX );

// The first rectangle is between the thing on the right and the optic.
const x1 = modelViewTransform.modelToViewX( rightPosition.x );
const clipWidth1 = modelViewTransform.modelToViewX( opticPosition.x ) - x1;
const x1 = targetOnRight ? targetX : sourceObjectX;
const clipWidth1 = opticX - x1;

// The second rectangle is between the thing on the left and the left edge of the picture frame on the right.
const x2 = modelViewTransform.modelToViewX( leftPosition.x );
const halfFrameWidth = targetNodeBoundsProperty.value.width / 2;
const clipWidth2 = modelViewTransform.modelToViewX( rightPosition.x ) - halfFrameWidth - x2;
const x2 = targetOnRight ? sourceObjectX : targetX;
const halfFrameWidth = ( targetOnRight ? targetNode.visibleBounds.width : sourceObjectNode.visibleBounds.width ) / 2;
const clipWidth2 = x1 - x2 - halfFrameWidth;

clipArea = new Shape()
.rect( x1, minY, clipWidth1, clipHeight )
Expand All @@ -119,9 +110,10 @@ class OpticalAxisForegroundNode extends OpticalAxisNode {
else {

// For a light source, clipArea is 1 rectangle, between the optic and the projection screen.
const minX = modelViewTransform.modelToViewX( opticPosition.x );
const maxX = modelViewTransform.modelToViewX( barrierPositionProperty.value.x );
clipArea = Shape.rectangle( minX, minY, maxX - minX, maxY - minY );
const minX = modelViewTransform.modelToViewX( opticPositionProperty.value.x );
assert && assert( barrier );
const maxX = modelViewTransform.modelToViewX( barrier!.positionProperty.value.x );
clipArea = Shape.rectangle( minX, minY, maxX - minX, clipHeight );
}
this.clipArea = clipArea;
};
Expand Down
11 changes: 6 additions & 5 deletions js/common/view/OpticalAxisNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ class OpticalAxisNode extends Line {

/**
* @param opticPositionProperty
* @param modelBoundsProperty
* @param visibleBoundsProperty
* @param modelViewTransform
* @param providedOptions
*/
constructor( opticPositionProperty: IReadOnlyProperty<Vector2>, modelBoundsProperty: IReadOnlyProperty<Bounds2>,
constructor( opticPositionProperty: IReadOnlyProperty<Vector2>, visibleBoundsProperty: IReadOnlyProperty<Bounds2>,
modelViewTransform: ModelViewTransform2, providedOptions: OpticalAxisNodeOptions ) {

// create optical axis line, with arbitrary length values.
Expand All @@ -43,9 +43,10 @@ class OpticalAxisNode extends Line {
}, providedOptions ) );

// set the horizontal extent of the optical axis line
modelBoundsProperty.link( bounds => {
this.setX1( modelViewTransform.modelToViewX( bounds.minX ) );
this.setX2( modelViewTransform.modelToViewX( bounds.maxX ) );
visibleBoundsProperty.link( visibleBounds => {
console.log( `visibleBounds=${visibleBounds}` );//XXX
this.setX1( visibleBounds.minX );
this.setX2( visibleBounds.maxX );
} );

// update y-position of line based on position of optic
Expand Down

0 comments on commit 16d7973

Please sign in to comment.