Refactoring: interpolating values is now handled in a separate class

InterpolationArray. This class is now used for the skidding probability
of the AI and the turn radius of all karts (which depends on speed).


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11689 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-10-16 05:51:46 +00:00
parent 4f7f1379df
commit fbb9e5f8d2
10 changed files with 284 additions and 205 deletions

View File

@ -280,11 +280,11 @@
<swatter duration="10" distance="3" squash-duration="5"
squash-slowdown="0.5"/>
<!-- turn-speed and turn-radius define the turn radius of the kart at
the given speed. The actual steering angle is dependent on the
<!-- turn-radius defines the turn radius of the kart at
a given speed. The actual steering angle is dependent on the
wheel base of the kart: radius = wheel_base/sin(steering_angle).
The values below define that at speed 0 the turn radius is 3, at
speed 10 (and up to 25 then) the radius is 15 etc.
The values below define that at speed 0 the turn radius is 2, at
speed 10 the radius is 7.5 etc.
The actual turn radius is piece-wise linearly interpolated. This
allows for tighter turning at lower speeds, and also avoids that
the kart becomes too hard to control at high speed (speeds of higher
@ -292,8 +292,7 @@
time-full-steer is the time when a player's input goes from neutral
steering to extreme left or right.
-->
<turn turn-speed= "0 10 25 45"
turn-radius="2.0 7.5 15 25"
<turn turn-radius="0:2.0 10:7.5 25:15 45:25"
time-full-steer ="0.2" />
<!-- Speed and acceleration related values: power and max-speed (in m/s)

View File

@ -95,8 +95,8 @@ src/items/projectile_manager.cpp
src/items/rubber_ball.cpp
src/items/rubber_band.cpp
src/items/swatter.cpp
src/karts/abstract_kart_animation.cpp
src/karts/abstract_kart.cpp
src/karts/abstract_kart_animation.cpp
src/karts/cannon_animation.cpp
src/karts/controller/ai_base_controller.cpp
src/karts/controller/ai_properties.cpp
@ -188,13 +188,13 @@ src/states_screens/kart_selection.cpp
src/states_screens/main_menu_screen.cpp
src/states_screens/minimal_race_gui.cpp
src/states_screens/options_screen_audio.cpp
src/states_screens/options_screen_input2.cpp
src/states_screens/options_screen_input.cpp
src/states_screens/options_screen_input2.cpp
src/states_screens/options_screen_players.cpp
src/states_screens/options_screen_ui.cpp
src/states_screens/options_screen_video.cpp
src/states_screens/race_gui_base.cpp
src/states_screens/race_gui.cpp
src/states_screens/race_gui_base.cpp
src/states_screens/race_gui_overworld.cpp
src/states_screens/race_result_gui.cpp
src/states_screens/race_setup_screen.cpp
@ -255,8 +255,8 @@ src/animations/billboard_animation.hpp
src/animations/ipo.hpp
src/animations/three_d_animation.hpp
src/audio/dummy_sfx.hpp
src/audio/music_dummy.hpp
src/audio/music.hpp
src/audio/music_dummy.hpp
src/audio/music_information.hpp
src/audio/music_manager.hpp
src/audio/music_ogg.hpp
@ -264,8 +264,8 @@ src/audio/sfx_base.hpp
src/audio/sfx_buffer.hpp
src/audio/sfx_manager.hpp
src/audio/sfx_openal.hpp
src/challenges/challenge_data.hpp
src/challenges/challenge.hpp
src/challenges/challenge_data.hpp
src/challenges/game_slot.hpp
src/challenges/unlock_manager.hpp
src/config/device_config.hpp
@ -306,11 +306,11 @@ src/guiengine/scalable_font.hpp
src/guiengine/screen.hpp
src/guiengine/skin.hpp
src/guiengine/widget.hpp
src/guiengine/widgets.hpp
src/guiengine/widgets/bubble_widget.hpp
src/guiengine/widgets/button_widget.hpp
src/guiengine/widgets/check_box_widget.hpp
src/guiengine/widgets/dynamic_ribbon_widget.hpp
src/guiengine/widgets.hpp
src/guiengine/widgets/icon_button_widget.hpp
src/guiengine/widgets/label_widget.hpp
src/guiengine/widgets/list_widget.hpp
@ -321,8 +321,8 @@ src/guiengine/widgets/spinner_widget.hpp
src/guiengine/widgets/text_box_widget.hpp
src/input/binding.hpp
src/input/device_manager.hpp
src/input/input_device.hpp
src/input/input.hpp
src/input/input_device.hpp
src/input/input_manager.hpp
src/input/wiimote_manager.hpp
src/io/file_manager.hpp
@ -343,8 +343,8 @@ src/items/projectile_manager.hpp
src/items/rubber_ball.hpp
src/items/rubber_band.hpp
src/items/swatter.hpp
src/karts/abstract_kart_animation.hpp
src/karts/abstract_kart.hpp
src/karts/abstract_kart_animation.hpp
src/karts/cannon_animation.hpp
src/karts/controller/ai_base_controller.hpp
src/karts/controller/ai_properties.hpp
@ -357,8 +357,8 @@ src/karts/controller/present_ai.hpp
src/karts/controller/skidding_ai.hpp
src/karts/explosion_animation.hpp
src/karts/ghost_kart.hpp
src/karts/kart_gfx.hpp
src/karts/kart.hpp
src/karts/kart_gfx.hpp
src/karts/kart_model.hpp
src/karts/kart_properties.hpp
src/karts/kart_properties_manager.hpp
@ -449,13 +449,13 @@ src/states_screens/kart_selection.hpp
src/states_screens/main_menu_screen.hpp
src/states_screens/minimal_race_gui.hpp
src/states_screens/options_screen_audio.hpp
src/states_screens/options_screen_input2.hpp
src/states_screens/options_screen_input.hpp
src/states_screens/options_screen_input2.hpp
src/states_screens/options_screen_players.hpp
src/states_screens/options_screen_ui.hpp
src/states_screens/options_screen_video.hpp
src/states_screens/race_gui_base.hpp
src/states_screens/race_gui.hpp
src/states_screens/race_gui_base.hpp
src/states_screens/race_gui_overworld.hpp
src/states_screens/race_result_gui.hpp
src/states_screens/race_setup_screen.hpp
@ -484,8 +484,8 @@ src/tracks/check_sphere.hpp
src/tracks/check_structure.hpp
src/tracks/graph_node.hpp
src/tracks/lod_node_loader.hpp
src/tracks/quad_graph.hpp
src/tracks/quad.hpp
src/tracks/quad_graph.hpp
src/tracks/quad_set.hpp
src/tracks/terrain_info.hpp
src/tracks/track.hpp
@ -493,11 +493,12 @@ src/tracks/track_manager.hpp
src/tracks/track_object.hpp
src/tracks/track_object_manager.hpp
src/tracks/track_sector.hpp
src/tutorial/tutorial_data.hpp
src/tutorial/tutorial.hpp
src/tutorial/tutorial_data.hpp
src/tutorial/tutorial_manager.hpp
src/utils/aligned_array.hpp
src/utils/constants.hpp
src/utils/interpolation_array.hpp
src/utils/leak_check.hpp
src/utils/no_copy.hpp
src/utils/profiler.hpp

View File

@ -1774,6 +1774,10 @@
RelativePath="..\..\utils\cpp2011.h"
>
</File>
<File
RelativePath="..\..\utils\interpolation_array.hpp"
>
</File>
<File
RelativePath="..\..\utils\leak_check.hpp"
>

View File

@ -19,6 +19,7 @@
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "utils/string_utils.hpp"
#include "utils/interpolation_array.hpp"
#include "utils/vec3.hpp"
#include <stdexcept>
@ -442,6 +443,53 @@ int XMLNode::get(const std::string &attribute, std::vector<int> *value) const
return value->size();
} // get(vector<int>)
// ----------------------------------------------------------------------------
/** Reads an InterpolatioARray. The values must be specified as:
* x0:y0 x1:y1 x2:y2 ...
* and the X values must be sorted. The function will abort (exit) with
* an error message in case of incorrectly formed x:y pairs.
* \param attribute Name of the attribute.
* \param value The InterpolationArray.
* \returns 0 in case of an error, !=0 otherwise
*/
int XMLNode::get(const std::string &attribute, InterpolationArray *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
std::vector<std::string> pairs = StringUtils::split(s, ' ');
for(unsigned int i=0; i<pairs.size(); i++)
{
std::vector<std::string> pair = StringUtils::split(pairs[i],':');
if(pair.size()!=2)
{
printf("Incorrect interpolation pair '%s' in '%s'.\n",
pairs[i].c_str(), attribute.c_str());
printf("Must be x:y.\n");
exit(-1);
}
float x;
if(!StringUtils::fromString(pair[0], x))
{
printf("Incorrect x in pair '%s' of '%s'.\n",
pairs[i].c_str(), attribute.c_str());
exit(-1);
}
float y;
if(!StringUtils::fromString(pair[1], y))
{
printf("Incorrect y in pair '%s' in '%s'.\n",
pair[1].c_str(), attribute.c_str());
exit(-1);
}
if(!value->push_back(x, y))
{
return 0;
}
} // for i
return 1;
} // get(InterpolationArray)
// ----------------------------------------------------------------------------
/** Interprets the attributes 'x', 'y', 'z' or 'h', 'p', 'r' as a 3d vector
* and set the corresponding elements of value. Not all values need to be

View File

@ -44,6 +44,7 @@ using namespace irr;
#include "utils/no_copy.hpp"
#include "utils/time.hpp"
class InterpolationArray;
class Vec3;
/**
@ -93,6 +94,7 @@ public:
int get(const std::string &attribute, std::vector<std::string> *value) const;
int get(const std::string &attribute, std::vector<float> *value) const;
int get(const std::string &attribute, std::vector<int> *value) const;
int get(const std::string &attribute, InterpolationArray *value) const;
int get(core::vector3df *value) const;
int getXYZ(core::vector3df *value) const;
int getXYZ(Vec3 *vaslue) const;

View File

@ -26,6 +26,7 @@ float AIProperties::UNDEFINED = -99.9f;
/** Constructor. Sets all properties to the special UNDEFINED value.
*/
AIProperties::AIProperties()
: m_skid_probability(/*decreasing*/false)
{
m_max_item_angle = UNDEFINED;
m_max_item_angle_high_speed = UNDEFINED;
@ -45,51 +46,14 @@ void AIProperties::load(const XMLNode *ai_node)
ai_node->get("time-full-steer", &m_time_full_steer );
ai_node->get("bad-item-closeness", &m_bad_item_closeness_2 );
ai_node->get("straight-length-for-zipper",&m_straight_length_for_zipper);
ai_node->get("rb-skid-probability", &m_skid_probability );
std::string s;
ai_node->get("rb-skid-probability", &s);
std::vector<std::string> pairs = StringUtils::split(s, ' ');
for(unsigned int i=0; i<pairs.size(); i++)
{
std::vector<std::string> pair = StringUtils::split(pairs[i],':');
if(pair.size()!=2)
{
printf("Incorrect pair '%s' in rd-skid-probability.\n",
pairs[i].c_str());
printf("Must be distance:probability.\n");
exit(-1);
}
float distance;
if(!StringUtils::fromString(pair[0], distance))
{
printf("Incorrect distance in pair '%s'.\n", pairs[i].c_str());
exit(-1);
}
float p;
if(!StringUtils::fromString(pair[1], p))
{
printf(
"Incorrect probability in pair '%s' in rb-skid-probability.\n",
pair[1].c_str());
exit(-1);
}
m_skid_distances.push_back(distance);
m_skid_probabilities.push_back(p);
} // for i
if(m_skid_distances.size()==0)
if(m_skid_probability.size()==0)
{
printf("No skid probability defined.\n");
exit(-1);
}
for(unsigned int i=0; i<m_skid_distances.size()-1; i++)
{
if(m_skid_distances[i]>=m_skid_distances[i+1])
{
printf("Skid distances must be sorted.\n");
exit(-1);
}
}
// We actually need the square of the distance later
m_bad_item_closeness_2 *= m_bad_item_closeness_2;
@ -120,30 +84,4 @@ void AIProperties::copyFrom(const AIProperties *destination)
*this = *destination;
} // copyFrom
// ----------------------------------------------------------------------------
float AIProperties::getSkiddingProbability(float distance) const
{
if(m_skid_distances.size()==1)
return m_skid_probabilities[0];
if(distance<m_skid_distances[0])
return m_skid_probabilities[0];
if(distance>m_skid_distances[m_skid_distances.size()-1])
return m_skid_probabilities[m_skid_probabilities.size()-1];
// Now distance must be between two of the distances in the
// sorted m_skid_distances array
for(unsigned int i=1; i<m_skid_distances.size(); i++)
{
if(distance >m_skid_distances[i]) continue;
float f = m_skid_probabilities[i-1] +
(m_skid_probabilities[i]-m_skid_probabilities[i-1]) *
(distance - m_skid_distances[i-1])/
(m_skid_distances[i]-m_skid_distances[i-1]);
return f;
}
assert(false);
return 0.0f;
} // getSkiddingProbability
/* EOF */

View File

@ -19,6 +19,8 @@
#ifndef HEADER_AI_PROPERTIES_HPP
#define HEADER_AI_PROPERTIES_HPP
#include "utils/interpolation_array.hpp"
#include <string>
#include <vector>
@ -68,19 +70,21 @@ protected:
/** Minimum length of a straight in order to activate a zipper. */
float m_straight_length_for_zipper;
/** The distances at which the skid probability is specified. */
std::vector<float> m_skid_distances;
/** The skidding probability depending on distance, which is stored
* in m_skid_distances - the two fields must have the same length. */
std::vector<float> m_skid_probabilities;
/** The array of (distance, skid_probability) points. */
InterpolationArray m_skid_probability;
public:
AIProperties();
void load(const XMLNode *skid_node);
void copyFrom(const AIProperties *destination);
void checkAllSet(const std::string &filename) const;
float getSkiddingProbability(float distance) const;
// ------------------------------------------------------------------------
/** Returns the skidding probability dependent on the specified distance
* to the first player kart. */
float getSkiddingProbability(float distance) const
{
return m_skid_probability.get(distance);
} // getSkiddingProbability
}; // AIProperties

View File

@ -44,6 +44,7 @@ float KartProperties::UNDEFINED = -99.9f;
* defined, it is guaranteed that each kart has well defined physics values).
*/
KartProperties::KartProperties(const std::string &filename)
: m_turn_angle_at_speed(/*decreasing*/true)
{
m_icon_material = NULL;
m_minimap_icon = NULL;
@ -56,10 +57,6 @@ KartProperties::KartProperties(const std::string &filename)
m_shadow_y_offset = 0.0f;
m_groups.clear();
m_turn_angle_at_speed.clear();
m_turn_radius_at_speed.clear();
m_turn_speed.clear();
m_speed_angle_increase.clear();
m_custom_sfx_id.resize(SFXManager::NUM_CUSTOMS);
// Set all other values to undefined, so that it can later be tested
@ -242,20 +239,12 @@ void KartProperties::load(const std::string &filename, const std::string &node)
m_wheel_radius );
m_wheel_base = fabsf( m_kart_model->getWheelPhysicsPosition(0).getZ()
-m_kart_model->getWheelPhysicsPosition(2).getZ());
for(unsigned int i=0; i<m_turn_radius_at_speed.size(); i++)
// Now convert the turn radius into turn angle:
for(unsigned int i=0; i<m_turn_angle_at_speed.size(); i++)
{
m_turn_angle_at_speed.push_back(
sin(m_wheel_base/m_turn_radius_at_speed[i])
);
}
for(unsigned int i=0; i<m_turn_speed.size()-1; i++)
{
if(m_turn_speed[i]==m_turn_speed[i+1])
m_speed_angle_increase.push_back(0);
else
m_speed_angle_increase.push_back(
(m_turn_angle_at_speed[i]-m_turn_angle_at_speed[i+1])/
(m_turn_speed[i+1]-m_turn_speed[i]) );
m_turn_angle_at_speed.setY( i,
sin(m_wheel_base/m_turn_angle_at_speed.getY(i)) );
}
m_shadow_texture = irr_driver->getTexture(m_shadow_file);
@ -347,33 +336,10 @@ void KartProperties::getAllData(const XMLNode * root)
if(const XMLNode *turn_node = root->getNode("turn"))
{
turn_node->get("time-full-steer", &m_time_full_steer );
turn_node->get("turn-speed", &m_turn_speed );
turn_node->get("turn-radius", &m_turn_radius_at_speed);
turn_node->get("turn-radius", &m_turn_angle_at_speed );
// For now store the turn radius in turn angle, the correct
// value can only be determined later in ::load
if(m_turn_speed.size()==0 ||
m_turn_radius_at_speed.size() != m_turn_speed.size())
{
printf("Inconsistent turn-speed and turn-radius "
"settings for kart %s\n", getIdent().c_str());
exit(-1);
}
for(unsigned int i=0; i<m_turn_speed.size()-1; i++)
{
if(m_turn_speed[i]>m_turn_speed[i+1])
{
printf("The turn-speed must be specified with increasing "
"values for kart %s.\n", getIdent().c_str());
exit(-1);
}
if(m_turn_radius_at_speed[i]>m_turn_radius_at_speed[i+1])
{
printf("The turn-radius must be increasing for kart %s.\n",
getIdent().c_str());
exit(-1);
}
}
} // if turn_node
}
if(const XMLNode *engine_node = root->getNode("engine"))
{
@ -465,7 +431,8 @@ void KartProperties::getAllData(const XMLNode * root)
collision_node->get("bevel-factor", &m_bevel_factor );
}
//TODO: wheel front right and wheel front left is not loaded, yet is listed as an attribute in the xml file after wheel-radius
//TODO: wheel front right and wheel front left is not loaded, yet is
//TODO: listed as an attribute in the xml file after wheel-radius
//TODO: same goes for their rear equivalents
if(const XMLNode *plunger_node= root->getNode("plunger"))
@ -685,50 +652,6 @@ void KartProperties::checkAllSet(const std::string &filename)
m_ai_properties->checkAllSet(filename);
} // checkAllSet
// ----------------------------------------------------------------------------
float KartProperties::getMaxSteerAngle(float speed) const
{
if(speed<=m_turn_speed[0]) return m_turn_angle_at_speed[0];
unsigned int last = m_turn_speed.size()-1;
if(speed>=m_turn_speed[last]) return m_turn_angle_at_speed[last];
for(unsigned int i=1; i<=last; i++)
{
if(speed <= m_turn_speed[i])
{
// Interpolate between i and i+1
return m_turn_angle_at_speed[i] -
(speed-m_turn_speed[i])*m_speed_angle_increase[i-1];
}
}
// This should never be reached
assert (0);
return 0; // avoid compiler warning
} // getMaxSteerAngle
// ----------------------------------------------------------------------------
/** Returns the (maximum) speed for a given turn radius.
* \param radius The radius for which the speed needs to be computed.
*/
float KartProperties::getSpeedForTurnRadius(float radius) const
{
if(radius < m_turn_radius_at_speed[0] )
return m_turn_speed[0];
const unsigned int last = m_turn_speed.size();
for(unsigned int i=1; i<last; i++)
{
if(radius < m_turn_radius_at_speed[i])
{
return m_turn_speed[i-1]
+ (m_turn_speed[i]-m_turn_speed[i-1]) * (radius-m_turn_radius_at_speed[i-1])
/ (m_turn_radius_at_speed[i]-m_turn_radius_at_speed[i-1]);
}
} // for i < last
return m_turn_speed[last-1];
} // getSpeedForTurnRadius
// ----------------------------------------------------------------------------
/** Called the first time a kart accelerates after 'ready-set-go'. It searches
* through m_startup_times to find the appropriate slot, and returns the

View File

@ -34,6 +34,7 @@ using namespace irr;
#include "karts/kart_model.hpp"
#include "io/xml_node.hpp"
#include "race/race_manager.hpp"
#include "utils/interpolation_array.hpp"
#include "utils/vec3.hpp"
class AIProperties;
@ -124,17 +125,9 @@ private:
* braking force. */
float m_time_full_steer; /**< Time for player karts to reach full
* steer angle. */
/** Stores the speeds at which the turn angle changes. */
std::vector<float> m_turn_speed;
/** Stores the turn angle at the corresponding turn speed. */
std::vector<float> m_turn_angle_at_speed;
/** Stores the turn radius at the corresponding turn speed. */
std::vector<float> m_turn_radius_at_speed;
/** Increase of turn angle with speed. */
std::vector<float> m_speed_angle_increase;
/** The turn angle depending on speed. */
InterpolationArray m_turn_angle_at_speed;
/** If != 0 a bevelled box shape is used by using a point cloud as a
* collision shape. */
@ -300,17 +293,20 @@ private:
/** How long the slip stream speed increase will gradually be reduced. */
float m_slipstream_fade_out_time;
float m_camera_distance; /**< Distance of normal camera from kart.*/
float m_camera_forward_up_angle; /**< Up angle of the camera in relation to
the pitch of the kart when driving
forwards. */
float m_camera_backward_up_angle; /**< Up angle of the camera in relation to
the pitch of the kart when driving
backwards. */
/** Distance of normal camera from kart. */
float m_camera_distance;
/** Up angle of the camera in relation to the pitch of the kart when
* driving forwards. */
float m_camera_forward_up_angle;
/** Up angle of the camera in relation to the pitch of the kart when
* driving backwards. */
float m_camera_backward_up_angle;
/** The following two vectors define at what ratio of the maximum speed what
* gear is selected. E.g. 0.25 means: if speed <=0.25*maxSpeed --> gear 1,
* 0.5 means: if speed <=0.5 *maxSpeed --> gear 2 */
* gear is selected. E.g. 0.25 means: if speed <=0.25*maxSpeed --> gear 1,
* 0.5 means: if speed <=0.5 *maxSpeed --> gear 2 */
std::vector<float> m_gear_switch_ratio;
/** This vector contains the increase in max power (to simulate different
* gears), e.g. 2.5 as first entry means: 2.5*maxPower in gear 1. See
@ -337,12 +333,20 @@ public:
void checkAllSet (const std::string &filename);
float getStartupBoost () const;
// ------------------------------------------------------------------------
/** Returns the (maximum) speed for a given turn radius.
* \param radius The radius for which the speed needs to be computed. */
float getSpeedForTurnRadius(float radius) const {
float angle = sin(m_wheel_base / radius);
return m_turn_angle_at_speed.getReverse(angle);
} // getSpeedForTurnRadius
// ------------------------------------------------------------------------
/** Returns the maximum steering angle (depending on speed). */
float getMaxSteerAngle (float speed) const;
/** Returns the (maximum) speed for a given turn radius. */
float getSpeedForTurnRadius(float radius) const;
float getMaxSteerAngle(float speed) const {
return m_turn_angle_at_speed.get(speed);
} // getMaxSteerAngle
// ------------------------------------------------------------------------
/** Returns the material for the kart icons. */
Material* getIconMaterial () const {return m_icon_material; }
@ -360,7 +364,10 @@ public:
/** Returns the name of this kart.
\note Pass it through fridibi as needed, this is the LTR name
*/
const wchar_t* getName() const {return translations->w_gettext(m_name.c_str()); }
const wchar_t* getName() const
{
return translations->w_gettext(m_name.c_str());
}
const std::string getNonTranslatedName() const {return m_name;}
@ -547,10 +554,16 @@ public:
/** Returns the increase of maximum speed while a rubber band is
* pulling. */
float getRubberBandSpeedIncrease() const {return m_rubber_band_speed_increase;}
float getRubberBandSpeedIncrease() const
{
return m_rubber_band_speed_increase;
}
/** Return the fade out time once a rubber band is removed. */
float getRubberBandFadeOutTime () const {return m_rubber_band_fade_out_time;}
float getRubberBandFadeOutTime() const
{
return m_rubber_band_fade_out_time;
}
/** Returns duration of a plunger in your face. */
float getPlungerInFaceTime () const

View File

@ -0,0 +1,147 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2012 Joerg Henrichs
//
// 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 3
// 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.
#ifndef HEADER_INTERPOLATION_ARRAY_HPP
#define HEADER_INTERPOLATION_ARRAY_HPP
#include <assert.h>
#include <vector>
/** This class manages a set of (x_i,y_i) points, x_i must be sorted.
* Those values are then used to linearly interpolate the y value for a
* given x. If x is less than the minimum x_0, y_0 is returned, if x is
* more than the maximum x_n, y_n is returned.
*/
class InterpolationArray
{
private:
/** The sorted x values. */
std::vector<float> m_x;
/** The y values. */
std::vector<float> m_y;
/* Pre-computed (x[i+1]-x[i])/(y[i+1]/-y[i]) . */
std::vector<float> m_delta;
/** True if the Y values are decreasing. This flag is important when
* doing a reverse loopup (find X given Y). */
bool m_decreasing;
public:
InterpolationArray(bool decreasing) : m_decreasing(decreasing) {};
/** Adds the value pair x/y to the list of all points. It is tested
* that the x values are added in order.
* \param x, y The pair to add.
* \returns 0 If the x values are not sorted, 1 otherwise. */
int push_back(float x, float y)
{
if(m_x.size()>0 && x < m_x[m_x.size()-1])
return 0;
m_x.push_back(x);
m_y.push_back(y);
if(m_y.size()>1)
{
const unsigned int last=m_x.size()-1;
m_delta.push_back( (m_y[last]-m_y[last-1])
/(m_x[last]-m_x[last-1]) );
}
return 1;
} // push_back
// ------------------------------------------------------------------------
/** Returns the number of X/Y points. */
unsigned int size() const { return m_x.size(); }
// ------------------------------------------------------------------------
/** Returns the X value for a specified point. */
float getX(unsigned int i) const { return m_x[i]; }
// ------------------------------------------------------------------------
/** Returns the Y value for a specified point. */
float getY(unsigned int i) const { return m_y[i]; }
// ------------------------------------------------------------------------
/** Sets the Y value for a specified point. */
void setY(unsigned int i, float y)
{
m_y[i] = y;
if(i>0)
m_delta[i-1] = (m_y[i]-m_y[i-1])
/(m_x[i]-m_x[i-1]);
if(i<m_y.size()-1)
m_delta[i] = (m_y[i+1]-m_y[i])
/(m_x[i+1]-m_x[i]);
}
// ------------------------------------------------------------------------
/** Returns the interpolated Y value for a given x. */
float get(float x) const
{
if(m_x.size()==1 || x<m_x[0])
return m_y[0];
if(x>m_x[m_x.size()-1])
return m_y[m_y.size()-1];
// Now x must be between two points in m_x
// The array size in STK are pretty small (typically 3 or 4),
// so not worth the effort to do a binary search
for(unsigned int i=1; i<m_x.size(); i++)
{
if(x >m_x[i]) continue;
return m_y[i-1] + m_delta[i-1] * (x - m_x[i-1]);
}
assert(false); return 0; // keep compiler happy
} // get
// ------------------------------------------------------------------------
/** Returns the X value necessary for a specified Y value. If it's not
* possible to find a corresponding X (y is too small or too large),
* x_min or x_max is returned. */
float getReverse(float y) const
{
if(m_y.size()==1) return m_x[0];
if(m_decreasing)
{
if(y > m_y[0]) return m_x[0];
const unsigned int last = m_x.size();
for(unsigned int i=1; i<last; i++)
{
if(y < m_y[i]) continue;
return m_x[i-1] + (y-m_y[i-1])/ m_delta[i-1];
} // for i < last
return m_y[last-1];
}
else // increasing
{
if(y < m_y[0]) return m_x[0];
const unsigned int last = m_x.size();
for(unsigned int i=1; i<last; i++)
{
if(y > m_y[i]) continue;
return m_x[i-1] + (y-m_y[i-1]) / m_delta[i-1];
} // for i < last
return m_y[last-1];
} // increasing
} // getReverse
}; // InterpolationArray
#endif