Skip to content

Commit

Permalink
Port model elements to TypeScript, see #749
Browse files Browse the repository at this point in the history
  • Loading branch information
samreid committed Oct 14, 2021
1 parent abf7b56 commit 6fcb13b
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 34 deletions.
5 changes: 4 additions & 1 deletion js/model/Ammeter.js → js/model/Ammeter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@
import Property from '../../../axon/js/Property.js';
import Vector2 from '../../../dot/js/Vector2.js';
import Vector2Property from '../../../dot/js/Vector2Property.js';
import Tandem from '../../../tandem/js/Tandem.js';
import NullableIO from '../../../tandem/js/types/NullableIO.js';
import NumberIO from '../../../tandem/js/types/NumberIO.js';
import circuitConstructionKitCommon from '../circuitConstructionKitCommon.js';
import Meter from './Meter.js';

class Ammeter extends Meter {
private readonly currentProperty: Property<null>;
private readonly probePositionProperty: Vector2Property;

/**
* @param {Tandem} tandem
* @param {number} phetioIndex
*/
constructor( tandem, phetioIndex ) {
constructor( tandem: Tandem, phetioIndex: number ) {
super( tandem, phetioIndex );

// public (read-only) {number} - lightweight index for naming the tandem view correspondingly
Expand Down
17 changes: 12 additions & 5 deletions js/model/Capacitor.js → js/model/Capacitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,38 @@
import NumberProperty from '../../../axon/js/NumberProperty.js';
import Range from '../../../dot/js/Range.js';
import merge from '../../../phet-core/js/merge.js';
import Tandem from '../../../tandem/js/Tandem.js';
import CCKCConstants from '../CCKCConstants.js';
import circuitConstructionKitCommon from '../circuitConstructionKitCommon.js';
import DynamicCircuitElement from './DynamicCircuitElement.js';
import Vertex from './Vertex.js';

type CapacitorOptions = {
capacitance: number
}; // TODO: Extends DynamicCircuitElementOptions

class Capacitor extends DynamicCircuitElement {
private readonly capacitanceProperty: NumberProperty;

/**
* @param {Vertex} startVertex
* @param {Vertex} endVertex
* @param {Tandem} tandem
* @param {Object} [options]
*/
constructor( startVertex, endVertex, tandem, options ) {
options = merge( {
constructor( startVertex: Vertex, endVertex: Vertex, tandem: Tandem, options?: CapacitorOptions ) {
const filledOptions = merge( {
capacitance: CCKCConstants.DEFAULT_CAPACITANCE,

// The number of decimal places is only used in the view, but we define it in the model as a convenient way to
// associate the value with the component
numberOfDecimalPlaces: 2
}, options );
}, options ) as CapacitorOptions;

super( startVertex, endVertex, CCKCConstants.CAPACITOR_LENGTH, tandem, options );
super( startVertex, endVertex, CCKCConstants.CAPACITOR_LENGTH, tandem, filledOptions );

// @public {Property.<number>} the capacitance in farads
this.capacitanceProperty = new NumberProperty( options.capacitance, {
this.capacitanceProperty = new NumberProperty( filledOptions.capacitance, {
range: new Range( 0.05, 0.20 ),
tandem: tandem.createTandem( 'capacitanceProperty' )
} );
Expand Down
11 changes: 10 additions & 1 deletion js/model/Charge.js → js/model/Charge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@
*/

import Emitter from '../../../axon/js/Emitter.js';
import Property from '../../../axon/js/Property.js';
import Matrix3 from '../../../dot/js/Matrix3.js';
import circuitConstructionKitCommon from '../circuitConstructionKitCommon.js';
import CircuitElement from './CircuitElement.js';

class Charge {
readonly charge: number;
circuitElement: CircuitElement;
distance: number;
private readonly matrix: Matrix3;
private readonly visibleProperty: Property<boolean>;
private readonly changedEmitter: Emitter<unknown>;
private readonly disposeEmitterCharge: Emitter<unknown>;

/**
* @param {CircuitElement} circuitElement - the circuit element the charge is in.
* @param {number} distance - how far along the circuit element it has traveled (in screen coordinates)
* @param {Property.<boolean>} visibleProperty - whether the charge should be shown.
* @param {number} charge - +1 for conventional current and -1 for electrons
*/
constructor( circuitElement, distance, visibleProperty, charge ) {
constructor( circuitElement: CircuitElement, distance: number, visibleProperty: Property<boolean>, charge: number ) {

assert && assert( charge === 1 || charge === -1, 'charge should be 1 or -1' );

Expand Down
73 changes: 47 additions & 26 deletions js/model/ChargeAnimator.js → js/model/ChargeAnimator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import RunningAverage from '../../../dot/js/RunningAverage.js';
import Utils from '../../../dot/js/Utils.js';
import CCKCConstants from '../CCKCConstants.js';
import circuitConstructionKitCommon from '../circuitConstructionKitCommon.js';
import CircuitElement from './CircuitElement';
import Charge from './Charge.js';
import Circuit from './Circuit.js';
import Vertex from './Vertex.js';

// constants

Expand All @@ -33,23 +37,34 @@ const SPEED_SCALE = 25;
// the highest allowable time step for integration
const MAX_DT = 1 / 30;

type CircuitElementPosition = {
circuitElement: CircuitElement,
distance: number,
distanceToClosestElectron: number
};

/**
* Gets the absolute value of the current in a circuit element.
* @param {CircuitElement} circuitElement
* @returns {number}
* @constructor
*/
const CURRENT_MAGNITUDE = function( circuitElement ) {
const CURRENT_MAGNITUDE = function( circuitElement: CircuitElement ) {
return Math.abs( circuitElement.currentProperty.get() );
};

class ChargeAnimator {
charges: ObservableArray<Charge>;
circuit: Circuit;
scale: number;
timeScaleRunningAverage: RunningAverage;
timeScaleProperty: NumberProperty;

/**
* @param {Circuit} circuit
* @constructor
*/
constructor( circuit ) {
constructor( circuit: Circuit ) {

// @private (read-only) {ObservableArrayDef.<Charge>} - the Charge instances
this.charges = circuit.charges;
Expand Down Expand Up @@ -82,7 +97,7 @@ class ChargeAnimator {
* @param {number} dt - elapsed time in seconds
* @public
*/
step( dt ) {
step( dt: number ) {

if ( this.charges.length === 0 || this.circuit.circuitElements.length === 0 ) {
return;
Expand All @@ -92,7 +107,8 @@ class ChargeAnimator {
dt = Math.min( dt, MAX_DT );

// Find the fastest current in any circuit element
const maxCurrentMagnitude = CURRENT_MAGNITUDE( _.maxBy( this.circuit.circuitElements, CURRENT_MAGNITUDE ) );
const maxCircuitElement = _.maxBy( this.circuit.circuitElements, CURRENT_MAGNITUDE ) as CircuitElement;
const maxCurrentMagnitude = CURRENT_MAGNITUDE( maxCircuitElement );
assert && assert( maxCurrentMagnitude >= 0, 'max current should be positive' );

const maxSpeed = maxCurrentMagnitude * SPEED_SCALE;
Expand All @@ -106,7 +122,7 @@ class ChargeAnimator {
this.timeScaleProperty.set( averageScale );

for ( let i = 0; i < this.charges.length; i++ ) {
const charge = this.charges.get( i );
const charge = this.charges[ i ];

// Don't update charges in chargeLayoutDirty circuit elements, because they will get a relayout anyways
if ( !charge.circuitElement.chargeLayoutDirty ) {
Expand All @@ -128,12 +144,12 @@ class ChargeAnimator {
* @param {number} dt - the elapsed time in seconds
* @private
*/
equalizeAll( dt ) {
equalizeAll( dt: number ) {

// Update them in a stochastic order to avoid systematic sources of error building up.
const indices = dotRandom.shuffle( _.range( this.charges.length ) );
for ( let i = 0; i < this.charges.length; i++ ) {
const charge = this.charges.get( indices[ i ] );
const charge = this.charges[ indices[ i ] ];

// No need to update charges in chargeLayoutDirty circuit elements, they will be replaced anyways. Skipping
// chargeLayoutDirty circuitElements improves performance. Also, only update electrons in circuit elements
Expand All @@ -151,7 +167,7 @@ class ChargeAnimator {
* @param {number} dt - seconds
* @private
*/
equalizeCharge( charge, dt ) {
equalizeCharge( charge: Charge, dt: number ) {

const circuitElementCharges = this.circuit.getChargesInCircuitElement( charge.circuitElement );

Expand Down Expand Up @@ -204,7 +220,7 @@ class ChargeAnimator {
* @param {number} dt - elapsed time in seconds
* @private
*/
propagate( charge, dt ) {
propagate( charge: Charge, dt: number ) {
const chargePosition = charge.distance;
assert && assert( _.isNumber( chargePosition ), 'distance along wire should be a number' );
const current = charge.circuitElement.currentProperty.get() * charge.charge;
Expand Down Expand Up @@ -238,7 +254,7 @@ class ChargeAnimator {
if ( circuitPositions.length > 0 ) {

// choose the CircuitElement with the furthest away electron
const chosenCircuitPosition = _.maxBy( circuitPositions, 'distanceToClosestElectron' );
const chosenCircuitPosition = _.maxBy( circuitPositions, 'distanceToClosestElectron' ) as CircuitElementPosition;
assert && assert( chosenCircuitPosition.distanceToClosestElectron >= 0, 'distanceToClosestElectron should be >=0' );
charge.circuitElement = chosenCircuitPosition.circuitElement;
charge.distance = chosenCircuitPosition.distance;
Expand All @@ -256,12 +272,12 @@ class ChargeAnimator {
* @returns {Object[]}
* @private
*/
getPositions( charge, overshoot, vertex, depth ) {
getPositions( charge: Charge, overshoot: number, vertex: Vertex, depth: number ) {

const circuit = this.circuit;

const adjacentCircuitElements = this.circuit.getNeighborCircuitElements( vertex );
const circuitPositions = [];
const circuitPositions: CircuitElementPosition[] = [];

// Keep only those with outgoing current.
for ( let i = 0; i < adjacentCircuitElements.length; i++ ) {
Expand Down Expand Up @@ -302,17 +318,20 @@ class ChargeAnimator {

// find closest electron to the vertex
if ( atStartOfNewCircuitElement ) {
distanceToClosestElectron = _.minBy( charges, 'distance' ).distance;
distanceToClosestElectron = ( _.minBy( charges, 'distance' ) as Charge ).distance;
}
else {
distanceToClosestElectron = circuitElement.chargePathLength - _.maxBy( charges, 'distance' ).distance;
distanceToClosestElectron = circuitElement.chargePathLength - ( _.maxBy( charges, 'distance' ) as Charge ).distance;
}

circuitPositions.push( {
circuitElement: circuitElement,
distance: distance,
distanceToClosestElectron: distanceToClosestElectron
} );
assert && assert( typeof distance === 'number', 'distance should be a number' );
if ( typeof distance === 'number' ) {
circuitPositions.push( {
circuitElement: circuitElement,
distance: distance,
distanceToClosestElectron: distanceToClosestElectron
} );
}
}
else if ( depth < 20 ) {

Expand All @@ -322,13 +341,15 @@ class ChargeAnimator {
if ( positions.length > 0 ) {

// find the one with the closest electron
const nearest = _.minBy( positions, 'distanceToClosestElectron' );

circuitPositions.push( {
circuitElement: circuitElement,
distance: distance,
distanceToClosestElectron: nearest.distanceToClosestElectron + circuitElement.chargePathLength
} );
const nearest = _.minBy( positions, 'distanceToClosestElectron' ) as CircuitElementPosition;
assert && assert( typeof distance === 'number', 'distance should be a number' );
if ( typeof distance === 'number' ) {
circuitPositions.push( {
circuitElement: circuitElement,
distance: distance,
distanceToClosestElectron: nearest.distanceToClosestElectron + circuitElement.chargePathLength
} )
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"sounds/**/*",
"mipmaps/**/*",
"images/**/*",
"../chipper/phet-types.d.ts"
"../chipper/phet-types.d.ts",
"../chipper/node_modules/@types/lodash/index.d.ts"
]
}

0 comments on commit 6fcb13b

Please sign in to comment.