/**
* Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.ctdp.rfdynhud.gamedata;
import java.awt.Point;
import java.awt.geom.Point2D;
/**
* Model of a track with waypoints and utility methods
*
* @author Marvin Froehlich (CTDP)
*/
public abstract class Track
{
protected static abstract class Waypoint
{
public abstract float getPosX();
public abstract float getPosY();
public abstract float getPosZ();
public abstract float getVecX();
public abstract float getVecY();
public abstract float getVecZ();
public abstract float getRoadLeft();
public abstract float getRoadRight();
public abstract byte getSector();
public abstract float getLapDistance();
public final float getRoadWidth()
{
return ( Math.abs( getRoadLeft() - getRoadRight() ) );
}
}
/**
* Gets the length of sector 1 in meters.
*
* @return the length of sector 1 in meters.
*/
public abstract float getSector1Length();
/**
* Gets the length of sector 2 (not including sector 1) in meters.
*
* @return the length of sector 2 in meters.
*/
public abstract float getSector2Length();
/**
* Gets the length of sector 2 in meters.
*
* @param includingSector1
*
* @return the length of sector 2 in meters.
*/
public final float getSector2Length( boolean includingSector1 )
{
if ( includingSector1 )
return ( getSector2Length() + getSector1Length() );
return ( getSector2Length() );
}
/**
* Gets the length of sector 3 in meters.
*
* @return the length of sector 3 in meters.
*/
public abstract float getSector3Length();
/**
* Gets the track length in meters.
*
* @return the track length in meters.
*/
public abstract float getTrackLength();
public abstract float getPitlaneLength();
public abstract float getMinXPos();
public abstract float getMinYPos();
public abstract float getMinZPos();
public abstract float getMaxXPos();
public abstract float getMaxYPos();
public abstract float getMaxZPos();
/**
* Calculates a scale factor for all values, if the track should be drawn on the given size.
*
* @param targetWidth the target width to draw on
* @param targetHeight the target height to draw on
*
* @return a scale factor for all values.
*/
public float getScale( int targetWidth, int targetHeight )
{
float scaleX = targetWidth / Math.abs( getMaxXPos() - getMinXPos() );
float scaleZ = targetHeight / Math.abs( getMaxZPos() - getMinZPos() );
return ( Math.min( scaleX, scaleZ ) );
}
/**
* Gets the extend along the x axis.
*
* @param scale the scale. See {@link #getScale(int, int)}
*
* @return the extend along the x axis.
*/
public final int getXExtend( float scale )
{
return ( Math.round( Math.abs( getMaxXPos() - getMinXPos() ) * scale ) );
}
/**
* Gets the extend along the y axis.
*
* @param scale the scale. See {@link #getScale(int, int)}
*
* @return the extend along the y axis.
*/
public final int getYExtend( float scale )
{
return ( Math.round( Math.abs( getMaxYPos() - getMinYPos() ) * scale ) );
}
/**
* Gets the extend along the z axis.
*
* @param scale the scale. See {@link #getScale(int, int)}
*
* @return the extend along the z axis.
*/
public final int getZExtend( float scale )
{
return ( Math.round( Math.abs( getMaxZPos() - getMinZPos() ) * scale ) );
}
/**
* Gets the maximum track width.
*
* @param scale the scale. See {@link #getScale(int, int)}
*
* @return the maximum track width.
*/
public abstract int getMaxTrackWidth( float scale );
/**
* Gets the number of waypoints of the main track or the pitlane.
*
* @param pitlane waypoints of main track or pitlane?
*
* @return the number of waypoints of the main track or the pitlane.
*/
public abstract int getNumWaypoints( boolean pitlane );
/**
* Gets the requested waypoint.
*
* @param pitlane waypoint of main track or pitlane?
* @param waypointIndex the index of the waypoint
*
* @return the requested waypoint.
*/
protected abstract Waypoint getWaypoint( boolean pitlane, int waypointIndex );
/**
* Gets the sector, the requested waypoint is in.
*
* @param pitlane waypoint of main track or pitlane?
* @param waypointIndex the index of the waypoint
*
* @return the sector, the requested waypoint is in.
*/
public final byte getWaypointSector( boolean pitlane, int waypointIndex )
{
Waypoint wp = getWaypoint( pitlane, waypointIndex );
return ( wp.getSector() );
}
/**
* Gets the waypoint's position.
*
* @param pitlane waypoint of main track or pitlane?
* @param waypointIndex the index of the waypoint
* @param position output buffer
*/
public final void getWaypointPosition( boolean pitlane, int waypointIndex, TelemVect3 position )
{
Waypoint wp = getWaypoint( pitlane, waypointIndex );
__GDPrivilegedAccess.setTelemVect3( wp.getPosX(), wp.getPosY(), wp.getPosZ(), position );
}
/**
* @param pitlane waypoint of main track or pitlane?
* @param waypointIndex the index of the waypoint
* @param scale the scale. See {@link #getScale(int, int)}
* @param point output buffer
*/
public final void getWaypointPosition( boolean pitlane, int waypointIndex, float scale, Point point )
{
Waypoint wp = getWaypoint( pitlane, waypointIndex );
point.setLocation( Math.round( ( -getMinXPos() + wp.getPosX() ) * scale ), Math.round( ( -getMinZPos() + wp.getPosZ() ) * scale ) );
}
/**
* @param pitlane waypoint of main track or pitlane?
* @param waypointIndex the index of the waypoint
* @param scale the scale. See {@link #getScale(int, int)}
* @param point output buffer
*/
public final void getWaypointPosition( boolean pitlane, int waypointIndex, float scale, Point2D.Float point )
{
Waypoint wp = getWaypoint( pitlane, waypointIndex );
point.setLocation( ( -getMinXPos() + wp.getPosX() ) * scale, ( -getMinZPos() + wp.getPosZ() ) * scale );
}
/**
* @param pitlane waypoint of main track or pitlane?
* @param waypointIndex the index of the waypoint
* @param vector output buffer
*/
public final void getWaypointVector( boolean pitlane, int waypointIndex, Point2D.Float vector )
{
Waypoint wp = getWaypoint( pitlane, waypointIndex );
vector.setLocation( wp.getVecX(), wp.getVecZ() );
}
/**
* @param pitlane waypoint of main track or pitlane?
* @param trackDistance the distance along the track in meters
* @param scale the scale. See {@link #getScale(int, int)}
* @param point output buffer
*/
public final void getInterpolatedPosition( boolean pitlane, float trackDistance, float scale, Point2D.Float point )
{
if ( pitlane )
{
if ( trackDistance < 0 )
trackDistance = 0;
if ( trackDistance > getPitlaneLength() )
trackDistance = getPitlaneLength();
}
else
{
while ( trackDistance < 0f )
trackDistance += getTrackLength();
trackDistance = trackDistance % getTrackLength();
}
int numWaypoints = getNumWaypoints( pitlane );
float tl = pitlane ? getPitlaneLength() : getTrackLength();
int waypointIndex = (int)( ( numWaypoints - 1 ) * trackDistance / tl );
Waypoint wp0 = getWaypoint( pitlane, waypointIndex );
while ( ( waypointIndex > 0 ) && ( wp0.getLapDistance() > trackDistance ) )
{
wp0 = getWaypoint( pitlane, --waypointIndex );
}
while ( ( waypointIndex < numWaypoints - 1 ) && ( getWaypoint( pitlane, waypointIndex + 1 ).getLapDistance() < trackDistance ) )
{
wp0 = getWaypoint( pitlane, ++waypointIndex );
}
Waypoint wp1;
float alpha;
if ( wp0.getLapDistance() > trackDistance )
{
wp1 = getWaypoint( pitlane, waypointIndex );
wp0 = getWaypoint( pitlane, numWaypoints - 1 );
float delta = wp0.getLapDistance() + ( tl - wp1.getLapDistance() );
alpha = ( ( tl - wp1.getLapDistance() ) + trackDistance ) / delta;
}
else if ( waypointIndex == numWaypoints - 1 )
{
wp1 = getWaypoint( pitlane, 0 );
float delta = ( tl - wp0.getLapDistance() ) + wp1.getLapDistance();
alpha = ( trackDistance - wp0.getLapDistance() ) / delta;
}
else
{
wp1 = getWaypoint( pitlane, waypointIndex + 1 );
float delta = wp1.getLapDistance() - wp0.getLapDistance();
alpha = ( trackDistance - wp0.getLapDistance() ) / delta;
}
float vecX = wp1.getPosX() - wp0.getPosX();
float vecZ = wp1.getPosZ() - wp0.getPosZ();
point.setLocation( ( -getMinXPos() + wp0.getPosX() + ( vecX * alpha ) ) * scale, ( -getMinZPos() + wp0.getPosZ() + ( vecZ * alpha ) ) * scale );
}
/**
* Gets an interpolated vector along the track at the given waypoint.
*
* @param pitlane waypoint of main track or pitlane?
* @param trackDistance the distance along the track in meters
* @param vector output buffer
*
* @return success?
*/
public final boolean getInterpolatedVector( boolean pitlane, float trackDistance, TelemVect3 vector )
{
//if ( ( trackDistance < 0f ) || ( trackDistance > trackLength ) )
// return ( false );
if ( pitlane )
{
if ( trackDistance < 0 )
trackDistance = 0;
if ( trackDistance > getPitlaneLength() )
trackDistance = getPitlaneLength();
}
else
{
while ( trackDistance < 0f )
trackDistance += getTrackLength();
trackDistance = trackDistance % getTrackLength();
}
int numWaypoints = getNumWaypoints( pitlane );
float tl = pitlane ? getPitlaneLength() : getTrackLength();
int waypointIndex = (int)( ( numWaypoints - 1 ) * trackDistance / tl );
Waypoint wp0 = getWaypoint( pitlane, waypointIndex );
while ( ( waypointIndex > 0 ) && ( wp0.getLapDistance() > trackDistance ) )
{
wp0 = getWaypoint( pitlane, --waypointIndex );
}
while ( ( waypointIndex < numWaypoints - 1 ) && ( getWaypoint( pitlane, waypointIndex + 1 ).getLapDistance() < trackDistance ) )
{
wp0 = getWaypoint( pitlane, ++waypointIndex );
}
Waypoint wp1;
float alpha;
if ( wp0.getLapDistance() > trackDistance )
{
wp1 = getWaypoint( pitlane, waypointIndex );
wp0 = getWaypoint( pitlane, numWaypoints - 1 );
float delta = wp0.getLapDistance() + ( getTrackLength() - wp1.getLapDistance() );
alpha = ( ( getTrackLength() - wp1.getLapDistance() ) + trackDistance ) / delta;
}
else if ( waypointIndex == numWaypoints - 1 )
{
wp1 = getWaypoint( pitlane, 0 );
float delta = ( getTrackLength() - wp0.getLapDistance() ) + wp1.getLapDistance();
alpha = ( trackDistance - wp0.getLapDistance() ) / delta;
}
else
{
wp1 = getWaypoint( pitlane, waypointIndex + 1 );
float delta = wp1.getLapDistance() - wp0.getLapDistance();
alpha = ( trackDistance - wp0.getLapDistance() ) / delta;
}
float beta = 1f - alpha;
float vecX = ( wp0.getVecX() * beta ) + ( wp1.getVecX() * alpha );
float vecY = ( wp0.getVecY() * beta ) + ( wp1.getVecY() * alpha );
float vecZ = ( wp0.getVecZ() * beta ) + ( wp1.getVecZ() * alpha );
__GDPrivilegedAccess.setTelemVect3( vecX, vecY, vecZ, vector );
return ( true );
}
private final TelemVect3 vec1 = new TelemVect3();
/**
* Gets the interpolated angle of the viewed vehicle to the road.
*
* @param scoringInfo
*
* @return the interpolated angle of the viewed vehicle to the road.
*/
public final float getInterpolatedAngleToRoad( ScoringInfo scoringInfo )
{
VehicleScoringInfo vsi = scoringInfo.getViewedVehicleScoringInfo();
float lapDistance = vsi.getLapDistance();
getInterpolatedVector( vsi.isInPits(), lapDistance, vec1 );
if ( vec1.getX() == 0f )
{
if ( vec1.getZ() < 0f )
return ( 0f );
return ( (float)Math.PI );
}
float dot = vec1.getX() * 0f + vec1.getZ() * -1f;
float angle = (float)Math.abs( Math.atan2( vec1.getX() * -1f - vec1.getZ() * 0f, dot ) );
if ( vec1.getX() < 0f )
return ( angle );
return ( -angle );
}
}