diff --git a/js/common/SplineEvaluation.ts b/js/common/SplineEvaluation.ts index f4f32a52..1fc8d169 100644 --- a/js/common/SplineEvaluation.ts +++ b/js/common/SplineEvaluation.ts @@ -9,12 +9,14 @@ import energySkatePark from '../energySkatePark.js'; -type Spline = { +// https://github.com/sloisel/numeric/blob/master/src/numeric.js#L2423 +export type Spline = { x: number[]; yl: number[]; yr: number[]; kl: number[]; kr: number[]; + diff(): Spline; }; // The most important function for this sim in numeric.js is just too slow because it uses tensor versions of all functions. diff --git a/js/common/model/DebugTracks.ts b/js/common/model/DebugTracks.ts index 04c450bd..8b8b661a 100644 --- a/js/common/model/DebugTracks.ts +++ b/js/common/model/DebugTracks.ts @@ -23,7 +23,7 @@ export default class DebugTracks { public static init( model: EnergySkateParkModel ): void { const createPoint = ( x: number, y: number ) => model.controlPointGroup.createNextElement( x, y, {} ); - const createTrack = ( controlPoints: ControlPoint[], options: TrackOptions = {} ) => model.trackGroup.createNextElement( controlPoints, [], options ); + const createTrack = ( controlPoints: ControlPoint[], options: TrackOptions = {} ) => model.trackGroup.createNextElement( controlPoints, options ); // Tracks to help demonstrate issues let controlPoints = null; diff --git a/js/common/model/EnergySkateParkModel.ts b/js/common/model/EnergySkateParkModel.ts index 5197b8e5..99f0316d 100644 --- a/js/common/model/EnergySkateParkModel.ts +++ b/js/common/model/EnergySkateParkModel.ts @@ -121,7 +121,7 @@ export default class EnergySkateParkModel extends PhetioObject { // TODO: https://github.com/phetsims/energy-skate-park/issues/123 the control point group doesn't have enough archetypes to // TODO: create an archetype track, https://github.com/phetsims/energy-skate-park/issues/123 // group of tracks - public readonly trackGroup: PhetioGroup; + public readonly trackGroup: PhetioGroup; // Temporary flag that keeps track of whether the track was changed in the step before the physics // update. True if the skater's track is being dragged by the user, so that energy conservation no longer applies. @@ -229,13 +229,13 @@ export default class EnergySkateParkModel extends PhetioObject { phetioDynamicElementName: 'controlPoint' } ); - this.trackGroup = new PhetioGroup( ( tandem, controlPoints, parents, options ) => { + this.trackGroup = new PhetioGroup( ( tandem, controlPoints, options ) => { assert && options && assert( !options.hasOwnProperty( 'tandem' ), 'tandem is managed by the PhetioGroup' ); - return new Track( this, controlPoints, parents, merge( {}, options, { + return new Track( this, controlPoints, merge( {}, options, { tandem: tandem, phetioDynamicElement: true } ) ); - }, [ _.range( 20 ).map( n => this.controlPointGroup.createNextElement( n * 100, 0, {} ) ), [], { + }, [ _.range( 20 ).map( n => this.controlPointGroup.createNextElement( n * 100, 0, {} ) ), { draggable: true, configurable: true } ], { @@ -1550,7 +1550,7 @@ export default class EnergySkateParkModel extends PhetioObject { const controlPointToDelete = track.controlPoints[ controlPointIndex ]; const points = _.without( track.controlPoints, controlPointToDelete ); this.controlPointGroup.disposeElement( controlPointToDelete ); - const newTrack = this.trackGroup.createNextElement( points, track.getParentsOrSelf(), Track.FULLY_INTERACTIVE_OPTIONS ); + const newTrack = this.trackGroup.createNextElement( points, Track.FULLY_INTERACTIVE_OPTIONS ); newTrack.physicalProperty.value = true; newTrack.droppedProperty.value = true; @@ -1605,10 +1605,10 @@ export default class EnergySkateParkModel extends PhetioObject { points1.push( newPoint1 ); points2.unshift( newPoint2 ); - const newTrack1 = this.trackGroup.createNextElement( points1, track.getParentsOrSelf(), Track.FULLY_INTERACTIVE_OPTIONS ); + const newTrack1 = this.trackGroup.createNextElement( points1, Track.FULLY_INTERACTIVE_OPTIONS ); newTrack1.physicalProperty.value = true; newTrack1.droppedProperty.value = true; - const newTrack2 = this.trackGroup.createNextElement( points2, track.getParentsOrSelf(), Track.FULLY_INTERACTIVE_OPTIONS ); + const newTrack2 = this.trackGroup.createNextElement( points2, Track.FULLY_INTERACTIVE_OPTIONS ); newTrack2.physicalProperty.value = true; newTrack2.droppedProperty.value = true; @@ -1699,7 +1699,7 @@ export default class EnergySkateParkModel extends PhetioObject { secondTrackBackward(); } - const newTrack = this.trackGroup.createNextElement( points, a.getParentsOrSelf().concat( b.getParentsOrSelf() ), Track.FULLY_INTERACTIVE_OPTIONS ); + const newTrack = this.trackGroup.createNextElement( points, Track.FULLY_INTERACTIVE_OPTIONS ); newTrack.physicalProperty.value = true; newTrack.droppedProperty.value = true; diff --git a/js/common/model/PremadeTracks.ts b/js/common/model/PremadeTracks.ts index 05ef8995..dc16a204 100644 --- a/js/common/model/PremadeTracks.ts +++ b/js/common/model/PremadeTracks.ts @@ -21,9 +21,6 @@ import ControlPoint from './ControlPoint.js'; import EnergySkateParkModel from './EnergySkateParkModel.js'; import Track from './Track.js'; -// constants -const PARENT_TRACKS: Track[] = []; - // limiting bounds for dragging control points const END_BOUNDS_WIDTH = 2.5; const END_BOUNDS_HEIGHT = 4; @@ -298,7 +295,7 @@ const PremadeTracks = { * Create a track from the provided control points. */ createTrack( model: EnergySkateParkModel, controlPoints: ControlPoint[], options: IntentionalAny ): Track { - return new Track( model, controlPoints, PARENT_TRACKS, options ); + return new Track( model, controlPoints, options ); }, TrackType: TrackType as IntentionalAny diff --git a/js/common/model/Track.ts b/js/common/model/Track.ts index e1272a78..eb66ab7a 100644 --- a/js/common/model/Track.ts +++ b/js/common/model/Track.ts @@ -22,14 +22,12 @@ import Tandem from '../../../../tandem/js/Tandem.js'; import ArrayIO from '../../../../tandem/js/types/ArrayIO.js'; import BooleanIO from '../../../../tandem/js/types/BooleanIO.js'; import IOType from '../../../../tandem/js/types/IOType.js'; -import ReferenceIO from '../../../../tandem/js/types/ReferenceIO.js'; +import ReferenceIO, { ReferenceIOState } from '../../../../tandem/js/types/ReferenceIO.js'; import energySkatePark from '../../energySkatePark.js'; -import SplineEvaluation from '../SplineEvaluation.js'; +import SplineEvaluation, { Spline } from '../SplineEvaluation.js'; import ControlPoint from './ControlPoint.js'; import EnergySkateParkModel from './EnergySkateParkModel.js'; -const ControlPointReferenceIO = ReferenceIO( ControlPoint.ControlPointIO ); - // options for a track that is fully interactive - it can be dragged, control points can be moved, broken into // different tracks, and combined with another track const FULLY_INTERACTIVE_OPTIONS = { @@ -39,6 +37,14 @@ const FULLY_INTERACTIVE_OPTIONS = { attachable: true }; +type TrackState = { + controlPoints: Array; + draggable: boolean; + configurable: boolean; + splittable: boolean; + attachable: boolean; +}; + type SelfOptions = { // can this track be dragged and moved in the play area? @@ -78,7 +84,6 @@ export default class Track extends PhetioObject { public readonly updateEmitter = new Emitter(); public readonly removeEmitter = new Emitter(); public readonly forwardingDragStartEmitter = new Emitter( { parameters: [ { valueType: SceneryEvent } ] } ); - private readonly parents: Track[]; private readonly trackTandem: Tandem; public readonly model: EnergySkateParkModel; public readonly draggable: boolean; @@ -112,14 +117,14 @@ export default class Track extends PhetioObject { public searchLinSpace: number[] | null; public distanceBetweenSamplePoints: null | number; private readonly disposeTrack: () => void; - public xSplineDiff: IntentionalAny; - public ySplineDiff: IntentionalAny; - public xSpline: IntentionalAny; - public ySpline: IntentionalAny; - public xSearchPoints: IntentionalAny; - public ySearchPoints: IntentionalAny; - public xSplineDiffDiff: IntentionalAny; - public ySplineDiffDiff: IntentionalAny; + public xSplineDiff: Spline | null = null; + public ySplineDiff: Spline | null = null; + public xSpline: Spline | null = null; + public ySpline: Spline | null = null; + public xSearchPoints: Float64Array | null = null; + public ySearchPoints: Float64Array | null = null; + public xSplineDiffDiff: Spline | null = null; + public ySplineDiffDiff: Spline | null = null; public minPoint!: number; public maxPoint!: number; @@ -128,13 +133,9 @@ export default class Track extends PhetioObject { * * @param model * @param controlPoints - * @param parents the original tracks that were used to make this track (if any) so they can be - * broken apart when dragged back to control panel adjusted control point from going - * offscreen, see #195 * @param [options] - required for tandem */ - public constructor( model: EnergySkateParkModel, controlPoints: ControlPoint[], parents: Track[], providedOptions?: TrackOptions ) { - assert && assert( Array.isArray( parents ), 'parents must be array' ); + public constructor( model: EnergySkateParkModel, controlPoints: ControlPoint[], providedOptions?: TrackOptions ) { const options = optionize()( { draggable: false, @@ -152,7 +153,6 @@ export default class Track extends PhetioObject { const tandem = options.tandem; - this.parents = parents; this.trackTandem = tandem; this.model = model; this.draggable = options.draggable; @@ -247,7 +247,6 @@ export default class Track extends PhetioObject { this.disposeTrack = () => { phetioStateSetEmitter.removeListener( stateListener ); - this.parents.length = 0; this.physicalProperty.dispose(); this.leftThePanelProperty.dispose(); this.draggingProperty.dispose(); @@ -258,15 +257,6 @@ export default class Track extends PhetioObject { }; } - public toStateObject(): IntentionalAny { - return { - controlPoints: this.controlPoints.map( x => ControlPointReferenceIO.toStateObject( x ) ), - parents: this.parents.map( x => ReferenceIO( IOType.ObjectIO ).toStateObject( x ) ), - draggable: this.draggable, - configurable: this.configurable - }; - } - /** * When points change, update the spline instance. */ @@ -331,20 +321,20 @@ export default class Track extends PhetioObject { // Compute the spline points for purposes of getting closest points. // keep these points around and invalidate only when necessary if ( !this.xSearchPoints ) { - this.xSearchPoints = SplineEvaluation.atArray( this.xSpline, this.searchLinSpace! ); - this.ySearchPoints = SplineEvaluation.atArray( this.ySpline, this.searchLinSpace! ); + this.xSearchPoints = SplineEvaluation.atArray( this.xSpline!, this.searchLinSpace! ); + this.ySearchPoints = SplineEvaluation.atArray( this.ySpline!, this.searchLinSpace! ); } let bestU = 0; let bestDistanceSquared = Number.POSITIVE_INFINITY; const bestPoint = new Vector2( 0, 0 ); for ( let i = 0; i < this.xSearchPoints.length; i++ ) { - const distanceSquared = point.distanceSquaredXY( this.xSearchPoints[ i ], this.ySearchPoints[ i ] ); + const distanceSquared = point.distanceSquaredXY( this.xSearchPoints[ i ], this.ySearchPoints![ i ] ); if ( distanceSquared < bestDistanceSquared ) { bestDistanceSquared = distanceSquared; bestU = this.searchLinSpace![ i ]; bestPoint.x = this.xSearchPoints[ i ]; - bestPoint.y = this.ySearchPoints[ i ]; + bestPoint.y = this.ySearchPoints![ i ]; } } @@ -353,11 +343,11 @@ export default class Track extends PhetioObject { let topU = bestU + distanceBetweenSearchPoints / 2; let bottomU = bestU - distanceBetweenSearchPoints / 2; - let topX = SplineEvaluation.atNumber( this.xSpline, topU ); - let topY = SplineEvaluation.atNumber( this.ySpline, topU ); + let topX = SplineEvaluation.atNumber( this.xSpline!, topU ); + let topY = SplineEvaluation.atNumber( this.ySpline!, topU ); - let bottomX = SplineEvaluation.atNumber( this.xSpline, bottomU ); - let bottomY = SplineEvaluation.atNumber( this.ySpline, bottomU ); + let bottomX = SplineEvaluation.atNumber( this.xSpline!, bottomU ); + let bottomY = SplineEvaluation.atNumber( this.ySpline!, bottomU ); // Even at 400 binary search iterations, performance is smooth on iPad3, so this loop doesn't seem too invasive const maxBinarySearchIterations = 40; @@ -368,20 +358,20 @@ export default class Track extends PhetioObject { if ( topDistanceSquared < bottomDistanceSquared ) { bottomU = bottomU + ( topU - bottomU ) / 4; // move halfway up - bottomX = SplineEvaluation.atNumber( this.xSpline, bottomU ); - bottomY = SplineEvaluation.atNumber( this.ySpline, bottomU ); + bottomX = SplineEvaluation.atNumber( this.xSpline!, bottomU ); + bottomY = SplineEvaluation.atNumber( this.ySpline!, bottomU ); bestDistanceSquared = topDistanceSquared; } else { topU = topU - ( topU - bottomU ) / 4; // move halfway down - topX = SplineEvaluation.atNumber( this.xSpline, topU ); - topY = SplineEvaluation.atNumber( this.ySpline, topU ); + topX = SplineEvaluation.atNumber( this.xSpline!, topU ); + topY = SplineEvaluation.atNumber( this.ySpline!, topU ); bestDistanceSquared = bottomDistanceSquared; } } bestU = ( topU + bottomU ) / 2; - bestPoint.x = SplineEvaluation.atNumber( this.xSpline, bestU ); - bestPoint.y = SplineEvaluation.atNumber( this.ySpline, bestU ); + bestPoint.x = SplineEvaluation.atNumber( this.xSpline!, bestU ); + bestPoint.y = SplineEvaluation.atNumber( this.ySpline!, bestU ); return { parametricPosition: bestU, point: bestPoint, distance: bestDistanceSquared }; } @@ -389,21 +379,21 @@ export default class Track extends PhetioObject { /** * Get x position at the parametric position. */ - public getX( parametricPosition: number ): number { return SplineEvaluation.atNumber( this.xSpline, parametricPosition ); } + public getX( parametricPosition: number ): number { return SplineEvaluation.atNumber( this.xSpline!, parametricPosition ); } /** * Get y position at the parametric position. * * @param parametricPosition */ - public getY( parametricPosition: number ): number { return SplineEvaluation.atNumber( this.ySpline, parametricPosition ); } + public getY( parametricPosition: number ): number { return SplineEvaluation.atNumber( this.ySpline!, parametricPosition ); } /** * Get the model position at the parametric position. */ public getPoint( parametricPosition: number ): Vector2 { - const x = SplineEvaluation.atNumber( this.xSpline, parametricPosition ); - const y = SplineEvaluation.atNumber( this.ySpline, parametricPosition ); + const x = SplineEvaluation.atNumber( this.xSpline!, parametricPosition ); + const y = SplineEvaluation.atNumber( this.ySpline!, parametricPosition ); return new Vector2( x, y ); } @@ -458,10 +448,10 @@ export default class Track extends PhetioObject { */ public getViewAngleAt( parametricPosition: number ): number { if ( this.xSplineDiff === null ) { - this.xSplineDiff = this.xSpline.diff(); - this.ySplineDiff = this.ySpline.diff(); + this.xSplineDiff = this.xSpline!.diff(); + this.ySplineDiff = this.ySpline!.diff(); } - return Math.atan2( -SplineEvaluation.atNumber( this.ySplineDiff, parametricPosition ), SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ) ); + return Math.atan2( -SplineEvaluation.atNumber( this.ySplineDiff!, parametricPosition ), SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ) ); } /** @@ -471,10 +461,10 @@ export default class Track extends PhetioObject { // load xSplineDiff, ySplineDiff here if not already loaded if ( this.xSplineDiff === null ) { - this.xSplineDiff = this.xSpline.diff(); - this.ySplineDiff = this.ySpline.diff(); + this.xSplineDiff = this.xSpline!.diff(); + this.ySplineDiff = this.ySpline!.diff(); } - return Math.atan2( SplineEvaluation.atNumber( this.ySplineDiff, parametricPosition ), SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ) ); + return Math.atan2( SplineEvaluation.atNumber( this.ySplineDiff!, parametricPosition ), SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ) ); } /** @@ -484,10 +474,10 @@ export default class Track extends PhetioObject { // load xSplineDiff, ySplineDiff here if not already loaded if ( this.xSplineDiff === null ) { - this.xSplineDiff = this.xSpline.diff(); - this.ySplineDiff = this.ySpline.diff(); + this.xSplineDiff = this.xSpline!.diff(); + this.ySplineDiff = this.ySpline!.diff(); } - return new Vector2( -SplineEvaluation.atNumber( this.ySplineDiff, parametricPosition ), SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ) ).normalize(); + return new Vector2( -SplineEvaluation.atNumber( this.ySplineDiff!, parametricPosition ), SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ) ).normalize(); } /** @@ -497,10 +487,10 @@ export default class Track extends PhetioObject { // load xSplineDiff, ySplineDiff here if not already loaded if ( this.xSplineDiff === null ) { - this.xSplineDiff = this.xSpline.diff(); - this.ySplineDiff = this.ySpline.diff(); + this.xSplineDiff = this.xSpline!.diff(); + this.ySplineDiff = this.ySpline!.diff(); } - return new Vector2( SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ), SplineEvaluation.atNumber( this.ySplineDiff, parametricPosition ) ).normalize(); + return new Vector2( SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ), SplineEvaluation.atNumber( this.ySplineDiff!, parametricPosition ) ).normalize(); } /** @@ -622,31 +612,6 @@ export default class Track extends PhetioObject { return false; } - /** - * Returns an array which contains all of the Tracks that would need to be reset if this Track was reset. - */ - public getParentsOrSelf(): Track[] { - return this.parents.length > 0 ? this.parents : [ this ]; - } - - /** - * Return this track to its control panel by resetting it to its initial state. - */ - public returnToControlPanel(): void { - if ( this.parents.length > 0 ) { - this.model.removeAndDisposeTrack( this ); - - for ( let i = 0; i < this.parents.length; i++ ) { - const parent = this.parents[ i ]; - parent.reset(); - this.model.tracks.add( parent ); - } - } - else { - this.reset(); - } - } - /** * Returns the arc length (in meters) between two points on a parametric curve. * This function is at the heart of many nested loops, so it must be heavily optimized @@ -663,13 +628,13 @@ export default class Track extends PhetioObject { // performance at the cost of numerical precision const numSegments = 4; const da = ( u1 - u0 ) / ( numSegments - 1 ); - let prevX = SplineEvaluation.atNumber( this.xSpline, u0 ); - let prevY = SplineEvaluation.atNumber( this.ySpline, u0 ); + let prevX = SplineEvaluation.atNumber( this.xSpline!, u0 ); + let prevY = SplineEvaluation.atNumber( this.ySpline!, u0 ); let sum = 0; for ( let i = 1; i < numSegments; i++ ) { const a = u0 + i * da; - const ptX = SplineEvaluation.atNumber( this.xSpline, a ); - const ptY = SplineEvaluation.atNumber( this.ySpline, a ); + const ptX = SplineEvaluation.atNumber( this.xSpline!, a ); + const ptY = SplineEvaluation.atNumber( this.ySpline!, a ); const dx = prevX - ptX; const dy = prevY - ptY; @@ -727,19 +692,19 @@ export default class Track extends PhetioObject { public getCurvature( parametricPosition: number, curvature: IntentionalAny ): void { if ( this.xSplineDiff === null ) { - this.xSplineDiff = this.xSpline.diff(); - this.ySplineDiff = this.ySpline.diff(); + this.xSplineDiff = this.xSpline!.diff(); + this.ySplineDiff = this.ySpline!.diff(); } if ( this.xSplineDiffDiff === null ) { this.xSplineDiffDiff = this.xSplineDiff.diff(); - this.ySplineDiffDiff = this.ySplineDiff.diff(); + this.ySplineDiffDiff = this.ySplineDiff!.diff(); } const xP = SplineEvaluation.atNumber( this.xSplineDiff, parametricPosition ); const xPP = SplineEvaluation.atNumber( this.xSplineDiffDiff, parametricPosition ); - const yP = SplineEvaluation.atNumber( this.ySplineDiff, parametricPosition ); - const yPP = SplineEvaluation.atNumber( this.ySplineDiffDiff, parametricPosition ); + const yP = SplineEvaluation.atNumber( this.ySplineDiff!, parametricPosition ); + const yPP = SplineEvaluation.atNumber( this.ySplineDiffDiff!, parametricPosition ); const k = ( xP * yPP - yP * xPP ) / Math.pow( ( xP * xP + yP * yP ), 3 / 2 ); @@ -763,15 +728,15 @@ export default class Track extends PhetioObject { */ public getLowestY(): number { if ( !this.xSearchPoints ) { - this.xSearchPoints = SplineEvaluation.atArray( this.xSpline, this.searchLinSpace! ); - this.ySearchPoints = SplineEvaluation.atArray( this.ySpline, this.searchLinSpace! ); + this.xSearchPoints = SplineEvaluation.atArray( this.xSpline!, this.searchLinSpace! ); + this.ySearchPoints = SplineEvaluation.atArray( this.ySpline!, this.searchLinSpace! ); } let min = Number.POSITIVE_INFINITY; let minIndex = -1; let y; - for ( let i = 0; i < this.ySearchPoints.length; i++ ) { - y = this.ySearchPoints[ i ]; + for ( let i = 0; i < this.ySearchPoints!.length; i++ ) { + y = this.ySearchPoints![ i ]; if ( y < min ) { min = y; minIndex = i; @@ -786,7 +751,7 @@ export default class Track extends PhetioObject { // @ts-expect-error const smallerSpace = numeric.linspace( minBound, maxBound, 200 ); - const refinedSearchPoints = SplineEvaluation.atArray( this.ySpline, smallerSpace ); + const refinedSearchPoints = SplineEvaluation.atArray( this.ySpline!, smallerSpace ); min = Number.POSITIVE_INFINITY; for ( let i = 0; i < refinedSearchPoints.length; i++ ) { @@ -1082,29 +1047,25 @@ export default class Track extends PhetioObject { } public static readonly FULLY_INTERACTIVE_OPTIONS = FULLY_INTERACTIVE_OPTIONS; - public static readonly TrackIO = new IOType( 'TrackIO', { + public static readonly TrackIO = new IOType( 'TrackIO', { valueType: Track, documentation: 'A skate track', - toStateObject: track => track.toStateObject(), stateObjectToCreateElementArguments: stateObject => { - // @ts-expect-error - const controlPoints = stateObject.controlPoints.map( x => ControlPointReferenceIO.fromStateObject( x ) ); + const controlPoints = stateObject.controlPoints.map( x => ReferenceIO( ControlPoint.ControlPointIO ).fromStateObject( x ) ); - // debugger; - // @ts-expect-error - const parents = stateObject.parents.map( x => Track.TrackIO.fromStateObject( x ) ); - return [ controlPoints, parents, { + return [ controlPoints, { draggable: stateObject.draggable, configurable: stateObject.configurable } ]; }, - stateSchema: TrackIO => ( { - controlPoints: ArrayIO( ControlPointReferenceIO ), - parents: ArrayIO( TrackIO ), + stateSchema: { + controlPoints: ArrayIO( ReferenceIO( ControlPoint.ControlPointIO ) ), draggable: BooleanIO, - configurable: BooleanIO - } ) + configurable: BooleanIO, + splittable: BooleanIO, + attachable: BooleanIO + } } ); } diff --git a/js/common/view/TrackNode.ts b/js/common/view/TrackNode.ts index b8cb67ea..bd755fea 100644 --- a/js/common/view/TrackNode.ts +++ b/js/common/view/TrackNode.ts @@ -218,8 +218,8 @@ export default class TrackNode extends Node { } // Compute points for lineTo - const xPoints = SplineEvaluation.atArray( track.xSpline, this.linSpace ); - const yPoints = SplineEvaluation.atArray( track.ySpline, this.linSpace ); + const xPoints = SplineEvaluation.atArray( track.xSpline!, this.linSpace ); + const yPoints = SplineEvaluation.atArray( track.ySpline!, this.linSpace ); const tx = this.getTranslation(); const shape = new Shape().moveTo( diff --git a/js/playground/model/EnergySkateParkPlaygroundModel.ts b/js/playground/model/EnergySkateParkPlaygroundModel.ts index 09aaebd0..a56a7bf9 100644 --- a/js/playground/model/EnergySkateParkPlaygroundModel.ts +++ b/js/playground/model/EnergySkateParkPlaygroundModel.ts @@ -56,7 +56,7 @@ export default class EnergySkateParkPlaygroundModel extends EnergySkateParkModel this.controlPointGroup.createNextElement( 1, 0, options.controlPointOptions ) ]; - return this.trackGroup.createNextElement( controlPoints, [], merge( + return this.trackGroup.createNextElement( controlPoints, merge( {}, Track.FULLY_INTERACTIVE_OPTIONS, options.trackOptions