From 6fcb13bc0af311327790adaf165b32f10876e6ac Mon Sep 17 00:00:00 2001 From: Sam Reid Date: Thu, 14 Oct 2021 14:50:35 -0600 Subject: [PATCH] Port model elements to TypeScript, see https://github.com/phetsims/circuit-construction-kit-common/issues/749 --- js/model/{Ammeter.js => Ammeter.ts} | 5 +- js/model/{Capacitor.js => Capacitor.ts} | 17 +++-- js/model/{Charge.js => Charge.ts} | 11 ++- .../{ChargeAnimator.js => ChargeAnimator.ts} | 73 ++++++++++++------- tsconfig.json | 3 +- 5 files changed, 75 insertions(+), 34 deletions(-) rename js/model/{Ammeter.js => Ammeter.ts} (89%) rename js/model/{Capacitor.js => Capacitor.ts} (77%) rename js/model/{Charge.js => Charge.ts} (85%) rename js/model/{ChargeAnimator.js => ChargeAnimator.ts} (86%) diff --git a/js/model/Ammeter.js b/js/model/Ammeter.ts similarity index 89% rename from js/model/Ammeter.js rename to js/model/Ammeter.ts index 65852a81..7f736a92 100644 --- a/js/model/Ammeter.js +++ b/js/model/Ammeter.ts @@ -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; + 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 diff --git a/js/model/Capacitor.js b/js/model/Capacitor.ts similarity index 77% rename from js/model/Capacitor.js rename to js/model/Capacitor.ts index b3b6e888..8fbbce62 100644 --- a/js/model/Capacitor.js +++ b/js/model/Capacitor.ts @@ -9,11 +9,18 @@ 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 @@ -21,19 +28,19 @@ class Capacitor extends DynamicCircuitElement { * @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.} 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' ) } ); diff --git a/js/model/Charge.js b/js/model/Charge.ts similarity index 85% rename from js/model/Charge.js rename to js/model/Charge.ts index 02fbce0d..db828eef 100644 --- a/js/model/Charge.js +++ b/js/model/Charge.ts @@ -7,10 +7,19 @@ */ 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; + private readonly changedEmitter: Emitter; + private readonly disposeEmitterCharge: Emitter; /** * @param {CircuitElement} circuitElement - the circuit element the charge is in. @@ -18,7 +27,7 @@ class Charge { * @param {Property.} 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, charge: number ) { assert && assert( charge === 1 || charge === -1, 'charge should be 1 or -1' ); diff --git a/js/model/ChargeAnimator.js b/js/model/ChargeAnimator.ts similarity index 86% rename from js/model/ChargeAnimator.js rename to js/model/ChargeAnimator.ts index 1307e00a..76146f0d 100644 --- a/js/model/ChargeAnimator.js +++ b/js/model/ChargeAnimator.ts @@ -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 @@ -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; + circuit: Circuit; + scale: number; + timeScaleRunningAverage: RunningAverage; + timeScaleProperty: NumberProperty; /** * @param {Circuit} circuit * @constructor */ - constructor( circuit ) { + constructor( circuit: Circuit ) { // @private (read-only) {ObservableArrayDef.} - the Charge instances this.charges = circuit.charges; @@ -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; @@ -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; @@ -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 ) { @@ -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 @@ -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 ); @@ -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; @@ -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; @@ -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++ ) { @@ -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 ) { @@ -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 + } ) + } } } } diff --git a/tsconfig.json b/tsconfig.json index 71b6fd03..7848b51d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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" ] } \ No newline at end of file