Skip to content

Commit

Permalink
Initial work on improving heuristics for attachment/detachment, see #207
Browse files Browse the repository at this point in the history
  • Loading branch information
samreid committed Aug 29, 2014
1 parent 737b5d1 commit 1baf894
Showing 1 changed file with 62 additions and 23 deletions.
85 changes: 62 additions & 23 deletions js/model/EnergySkateParkBasicsModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ define( function( require ) {
//Will be filled in by the view, used to prevent control points from moving outside the visible model bounds when adjusted, see #195
availableModelBounds: null
} );

this.time = 0;

this.resetLastDetachment();
this.skater = new Skater();

//If the mass changes while the sim is paused, trigger an update so the skater image size will update, see #115
Expand Down Expand Up @@ -164,6 +168,29 @@ define( function( require ) {

return inherit( PropertySet, EnergySkateParkBasicsModel, {

resetLastDetachment: function() {

//Keep track of the last time the skater detached from the track to use as a heuristic for when reattachments are allowed.
//This information should be stored in the SkaterState, but I am too worried about making that more complex or perform more slowly.
this.lastDetachment = {

//The model time (in seconds) when the skater last detached from the track
time: 0,

//The last track the skater detached from
track: null,

//The Vector2 position the skater detached from. Initialize this as far from the play area but non-null to simplify the logic
position: new Vector2( 10000, 10000 ),

//The arc distance traveled since detaching
arcLength: 0,

//The parametric curve position the skater detached from
u: 0
};
},

//Add the tracks that will be in the track toolbox for the "Playground" screen
addDraggableTracks: function() {
for ( var i = 0; i < 4; i++ ) {
Expand All @@ -189,6 +216,7 @@ define( function( require ) {
this.skater.reset();

this.clearTracks();
this.resetLastDetachment();
},

clearTracks: function() {
Expand All @@ -208,7 +236,8 @@ define( function( require ) {
//step one frame, assuming 60fps
manualStep: function() {
var skaterState = SkaterState.createFromPool( this.skater, EMPTY_OBJECT );
var result = this.stepModel( 1.0 / 60, skaterState );
var dt = 1.0 / 60;
var result = this.stepModel( dt, skaterState );
result.setToSkater( this.skater );
this.skater.trigger( 'updated' );
},
Expand Down Expand Up @@ -364,6 +393,17 @@ define( function( require ) {
}
},

//Logic to see if it is okay to reattach to the track. See #207 #176 #194
okToAttach: function( skaterState, track, u ) {
var elapsedTime = this.time - this.lastDetachment.time;
var isTrackDifferent = track !== this.lastDetachment.track;
var dx = skaterState.positionX - this.lastDetachment.position.x;
var dy = skaterState.positionY - this.lastDetachment.position.y;
var euclideanDistance = Math.sqrt( dx * dx + dy * dy );
var deltaU = Math.abs( u - this.lastDetachment.u );
return isTrackDifferent || elapsedTime > 10.0 / 60.0 || euclideanDistance > 0.1 || deltaU > 0.1 || this.lastDetachment.arcLength > 0.2;
},

//Check to see if it should hit or attach to track during free fall
interactWithTracksWhileFalling: function( physicalTracks, skaterState, proposedPosition, initialEnergy, dt, proposedVelocity ) {

Expand All @@ -387,7 +427,7 @@ define( function( require ) {
var afterVector = proposedPosition.minus( trackPoint );

//If crossed the track, attach to it.
if ( beforeVector.dot( afterVector ) < 0 ) {
if ( beforeVector.dot( afterVector ) < 0 && this.okToAttach( skaterState, track, u ) ) {

var newVelocity = segment.times( segment.dot( proposedVelocity ) );
var newSpeed = newVelocity.magnitude();
Expand Down Expand Up @@ -458,6 +498,9 @@ define( function( require ) {
return skaterState.strikeGround( initialEnergy, proposedPosition.x );
}
else {
var dx = skaterState.positionX - proposedPosition.x;
var dy = skaterState.positionY - proposedPosition.y;
this.lastDetachment.arcLength += Math.sqrt( dx * dx + dy * dy );
return skaterState.continueFreeFall( proposedVelocity.x, proposedVelocity.y, proposedPosition.x, y, skaterState.timeSinceJump + dt );
}
},
Expand Down Expand Up @@ -647,14 +690,16 @@ define( function( require ) {

var leaveTrack = (netForceRadial < centripForce && outsideCircle) || (netForceRadial > centripForce && !outsideCircle);

if ( leaveTrack && this.detachable && this.okToDetach( skaterState, dt ) ) {
if ( leaveTrack && this.detachable ) {

//Leave the track. Make sure the velocity is pointing away from the track or keep track of frames away from the track so it doesn't immediately recollide
//Or project a ray and see if a collision is imminent ?
var freeSkater = skaterState.leaveTrack();

debug && debug( 'left middle track' );

this.recordDetachment( skaterState, track );

//Step after switching to free fall, so it doesn't look like it pauses
return this.stepFreeFall( dt, freeSkater, true );
}
Expand Down Expand Up @@ -686,31 +731,23 @@ define( function( require ) {

//There is a situation in which the `u` of the skater exceeds the track bounds before the getClosestPositionAndParameter.u does, which can cause the skater to immediately reattach
//So make sure the skater is far enough from the track so it won't reattach right away, see #167
var closestU = track.getClosestPositionAndParameter( new Vector2( skaterState.positionX, skaterState.positionY ) ).u;
if ( skaterState.track.isParameterInBounds( closestU ) ) {
return correctedState;
}
else {
return skaterState.updateTrackUDStepsSinceJump( null, 0, 0 );
}
this.recordDetachment( skaterState, track );
return skaterState.updateTrackUDStepsSinceJump( null, 0, 0 );
}
}
}
},

//Check if the skater can detach from the track without immediately reattaching, given its velocity. See #172
okToDetach: function( skaterState, dt ) {
var ss = skaterState.leaveTrack();
var count = 0;

//Make sure the skater won't immediately re-collide with the track before allowing it to detach, see #172
for ( ; count <= 1; count++ ) {
ss = this.stepFreeFall( dt, ss, false );
if ( ss.track !== null && ss.track === skaterState.track ) {
return false;
}
}
return true;
//Record the last position time the skater detached from the track, see #207 #176 #194
recordDetachment: function( skaterState, track ) {
assert && assert( track );
this.lastDetachment = {
time: this.time,
track: track,
position: new Vector2( skaterState.positionX, skaterState.positionY ),
arcLength: 0,
u: skaterState.u
};
},

//Try to match the target energy by reducing the velocity of the skaterState
Expand Down Expand Up @@ -875,6 +912,8 @@ define( function( require ) {

//Update the skater based on which state it is in
stepModel: function( dt, skaterState ) {
this.time += dt;

return skaterState.dragging ? skaterState : //User is dragging the skater, nothing to update here
!skaterState.track && skaterState.positionY <= 0 ? this.stepGround( dt, skaterState ) :
!skaterState.track && skaterState.positionY > 0 ? this.stepFreeFall( dt, skaterState, false ) :
Expand Down

0 comments on commit 1baf894

Please sign in to comment.