Skip to content

Commit

Permalink
major changes to decouple frame objects and light sources, and introd…
Browse files Browse the repository at this point in the history
…uce "scenes" (rulers and labels disabled) #217
  • Loading branch information
pixelzoom committed Feb 7, 2022
1 parent a5d8b25 commit ef4639a
Show file tree
Hide file tree
Showing 18 changed files with 1,029 additions and 504 deletions.
130 changes: 130 additions & 0 deletions js/common/model/FramedObjectScene.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2022, University of Colorado Boulder

/**
* FramedObjectScene is a scene in which rays from a single framed object interact with an optic and produce
* an Image. Rays emanate from 2 points of interest on the framed object.
*
* @author Chris Malley (PixelZoom, Inc.)
*/

import Range from '../../../../dot/js/Range.js';
import geometricOptics from '../../geometricOptics.js';
import Optic from './Optic.js';
import SourceObject from './SourceObject.js';
import SecondPoint from './SecondPoint.js';
import Target from './Target.js';
import Property from '../../../../axon/js/Property.js';
import Representation, { FRAMED_OBJECT_REPRESENTATIONS } from './Representation.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import Tandem from '../../../../tandem/js/Tandem.js';
import merge from '../../../../phet-core/js/merge.js';
import { RaysType } from './RaysType.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import LightRays from './LightRays.js';

type FramedObjectSceneOptions = {

// initial position of the source object and first light source
framedObjectPosition: Vector2,

// phet-io options
tandem: Tandem
};

class FramedObjectScene {

readonly optic: Optic;
readonly representationProperty: Property<Representation>;
readonly framedObject: SourceObject;
readonly secondPoint: SecondPoint;
readonly target1: Target;
readonly target2: Target;
readonly lightRaysTimeProperty: NumberProperty;
readonly lightRays1: LightRays;
readonly lightRays2: LightRays;

/**
* @param optic
* @param raysTypeProperty
* @param providedOptions
*/
constructor( optic: Optic, raysTypeProperty: Property<RaysType>, providedOptions: FramedObjectSceneOptions ) {

const options = merge( {
//TODO
}, providedOptions );

this.optic = optic;

this.representationProperty = new Property( FRAMED_OBJECT_REPRESENTATIONS[ 0 ], {
validValues: FRAMED_OBJECT_REPRESENTATIONS
} );

this.framedObject = new SourceObject( this.representationProperty, {
position: options.framedObjectPosition,
tandem: options.tandem.createTandem( 'framedObject' )
} );

//TODO make secondPoint a property of framedObject
this.secondPoint = new SecondPoint( this.representationProperty, this.framedObject.positionProperty );

this.target1 = new Target( this.framedObject.positionProperty, this.optic, this.representationProperty );

this.target2 = new Target( this.secondPoint.positionProperty, this.optic, this.representationProperty );

//TODO should each scene have this, or should it be shared by all scenes?
this.lightRaysTimeProperty = new NumberProperty( 0, {
units: 's',
range: new Range( 0, 10 ), // determines the duration of the light rays animation
tandem: options.tandem.createTandem( 'lightRaysTimeProperty' )
} );

// Changing raysTypeProperty resets the animation time for rays.
raysTypeProperty.link( () => this.lightRaysTimeProperty.reset() );

this.lightRays1 = new LightRays(
this.lightRaysTimeProperty,
raysTypeProperty,
this.representationProperty,
this.framedObject.positionProperty,
this.optic,
this.target1,
null //TODO projectionScreen is not relevant for this scene
);

this.lightRays2 = new LightRays(
this.lightRaysTimeProperty,
raysTypeProperty,
this.representationProperty,
this.secondPoint.positionProperty,
this.optic,
this.target2,
null //TODO projectionScreen is not relevant for this scene
);

//TODO add Guides ala LightSourcesScene, but for Lens screen only
}

//TODO is this complete?
public reset(): void {
this.representationProperty.reset();
this.framedObject.reset();
this.secondPoint.reset();
this.lightRaysTimeProperty.reset();
}

/**
* Steps the animation of light rays.
* @param dt - time step, in seconds
*/
public stepLightRays( dt: number ): void {
const t = this.lightRaysTimeProperty.value + dt;
assert && assert( this.lightRaysTimeProperty.range ); // {Range|null}
if ( this.lightRaysTimeProperty.range!.contains( t ) ) {
this.lightRaysTimeProperty.value = t;
}
}
}

geometricOptics.register( 'FramedObjectScene', FramedObjectScene );
export default FramedObjectScene;
129 changes: 22 additions & 107 deletions js/common/model/GOModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,80 +7,41 @@
* @author Chris Malley (PixelZoom, Inc.)
*/

import NumberProperty from '../../../../axon/js/NumberProperty.js';
import Property from '../../../../axon/js/Property.js';
import Range from '../../../../dot/js/Range.js';
import merge from '../../../../phet-core/js/merge.js';
import Tandem from '../../../../tandem/js/Tandem.js';
import StringIO from '../../../../tandem/js/types/StringIO.js';
import geometricOptics from '../../geometricOptics.js';
import GOConstants from '../GOConstants.js';
import LightRays from './LightRays.js';
import Optic from './Optic.js';
import { RaysType, RaysTypeValues } from './RaysType.js';
import Representation, { RepresentationStaticInstances } from './Representation.js';
import GORuler from './GORuler.js';
import SecondPoint from './SecondPoint.js';
import SourceObject from './SourceObject.js';
import Target from './Target.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import ProjectionScreen from '../../lens/model/ProjectionScreen.js';

// constants
const RAYS_ANIMATION_TIME = 10; // length of the rays animation, in seconds
import FramedObjectScene from './FramedObjectScene.js';
import Representation, { FRAMED_OBJECT_REPRESENTATIONS, LIGHT_SOURCE_REPRESENTATION } from './Representation.js';
import Lens from '../../lens/model/Lens.js';

type GeometricOpticsModelOptions = {

// initial position of the source object and first light source
sourceObjectPosition: Vector2,

// optional projection screen that may block rays
projectionScreen?: ProjectionScreen | null,

// representations of the source object supported by the model
representations?: Representation[],

// initial representation of the source object
representation?: Representation,
framedObjectPosition: Vector2,

// phet-io options
tandem: Tandem
};

class GOModel {

// model of the optic
readonly optic: Optic;

// representation of the source object
readonly representationProperty: Property<Representation>;

// the Object or first Light Source
readonly sourceObject: SourceObject;

// the second point on the Object, and the second Light Source
readonly secondPoint: SecondPoint;

// Image associated with the Object or first Light Source
readonly firstTarget: Target;

// Image associated with secondPoint
readonly secondTarget: Target;

// optional projection screen that may block rays
readonly projectionScreen: ProjectionScreen | null;

// elapsed time of light rays animation
readonly lightRaysTimeProperty: NumberProperty;
// model of the optic
readonly optic: Optic;

// determines the representation used for rays
// representation used for rays
readonly raysTypeProperty: Property<RaysType>;

// light rays associated with the first light source
readonly lightRays1: LightRays;

// light rays associated with the second light source
readonly lightRays2: LightRays;
// scenes
readonly framedObjectScene: FramedObjectScene;

// rulers
readonly horizontalRuler: GORuler;
Expand All @@ -98,64 +59,28 @@ class GOModel {
constructor( optic: Optic, providedOptions: GeometricOpticsModelOptions ) {

const options = merge( {
projectionScreen: null,
representations: RepresentationStaticInstances,
representation: RepresentationStaticInstances[ 0 ]
//TODO
}, providedOptions );

this.optic = optic;

this.representationProperty = new Property( options.representation, {
validValues: options.representations
//TODO this is a bit of a hack
this.representationProperty = new Property( FRAMED_OBJECT_REPRESENTATIONS[ 0 ], {
validValues: ( optic instanceof Lens ) ?
[ ...FRAMED_OBJECT_REPRESENTATIONS, LIGHT_SOURCE_REPRESENTATION ] :
[ ...FRAMED_OBJECT_REPRESENTATIONS ]
} );

this.sourceObject = new SourceObject( this.representationProperty, {
position: options.sourceObjectPosition,
tandem: options.tandem.createTandem( 'sourceObject' )
} );

this.secondPoint = new SecondPoint( this.representationProperty, this.sourceObject.positionProperty );

this.firstTarget = new Target( this.sourceObject.positionProperty, this.optic, this.representationProperty );

this.secondTarget = new Target( this.secondPoint.positionProperty, this.optic, this.representationProperty );

this.projectionScreen = options.projectionScreen;

this.lightRaysTimeProperty = new NumberProperty( 0, {
units: 's',
range: new Range( 0, RAYS_ANIMATION_TIME ),
tandem: options.tandem.createTandem( 'lightRaysTimeProperty' )
} );
this.optic = optic;

this.raysTypeProperty = new Property( 'marginal', {
validValues: RaysTypeValues,
tandem: options.tandem.createTandem( 'raysTypeProperty' ),
phetioType: Property.PropertyIO( StringIO )
} );

// Changing raysTypeProperty resets the animation time for rays.
this.raysTypeProperty.link( () => this.lightRaysTimeProperty.reset() );

this.lightRays1 = new LightRays(
this.lightRaysTimeProperty,
this.raysTypeProperty,
this.representationProperty,
this.sourceObject.positionProperty,
this.optic,
this.firstTarget,
options.projectionScreen
);

this.lightRays2 = new LightRays(
this.lightRaysTimeProperty,
this.raysTypeProperty,
this.representationProperty,
this.secondPoint.positionProperty,
this.optic,
this.secondTarget,
options.projectionScreen
);
this.framedObjectScene = new FramedObjectScene( this.optic, this.raysTypeProperty, {
framedObjectPosition: options.framedObjectPosition,
tandem: options.tandem.createTandem( 'framedObjectScene' )
} );

this.horizontalRuler = new GORuler( {
orientation: 'horizontal',
Expand All @@ -175,24 +100,14 @@ class GOModel {
public reset(): void {
this.representationProperty.reset();
this.optic.reset();
this.sourceObject.reset();
this.secondPoint.reset();
this.lightRaysTimeProperty.reset();
this.raysTypeProperty.reset();
this.framedObjectScene.reset();
this.horizontalRuler.reset();
this.verticalRuler.reset();
}

/**
* Steps the animation of light rays.
* @param dt - time step, in seconds
*/
public stepLightRays( dt: number ): void {
const t = this.lightRaysTimeProperty.value + dt;
assert && assert( this.lightRaysTimeProperty.range ); // {Range|null}
if ( this.lightRaysTimeProperty.range!.contains( t ) ) {
this.lightRaysTimeProperty.value = t;
}
this.framedObjectScene.stepLightRays( dt );
}
}

Expand Down
4 changes: 4 additions & 0 deletions js/common/model/OpticalObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class OpticalObject {
tandem: options.tandem.createTandem( 'positionProperty' )
} );
}

public reset(): void {
this.positionProperty.reset();
}
}

geometricOptics.register( 'OpticalObject', OpticalObject );
Expand Down
Loading

0 comments on commit ef4639a

Please sign in to comment.