Property interpolator subsystem. A utility, primarily for Nasal scripts
This commit is contained in:
@@ -9,7 +9,8 @@ include_HEADERS = \
|
||||
strutils.hxx \
|
||||
tabbed_values.hxx \
|
||||
texcoord.hxx \
|
||||
zfstream.hxx
|
||||
zfstream.hxx \
|
||||
interpolator.hxx
|
||||
|
||||
libsgmisc_a_SOURCES = \
|
||||
sg_path.cxx \
|
||||
@@ -17,7 +18,8 @@ libsgmisc_a_SOURCES = \
|
||||
strutils.cxx \
|
||||
tabbed_values.cxx \
|
||||
texcoord.cxx \
|
||||
zfstream.cxx
|
||||
zfstream.cxx \
|
||||
interpolator.cxx
|
||||
|
||||
noinst_PROGRAMS = tabbed_value_test
|
||||
|
||||
|
||||
107
simgear/misc/interpolator.cxx
Normal file
107
simgear/misc/interpolator.cxx
Normal file
@@ -0,0 +1,107 @@
|
||||
// Written by Andrew J. Ross, started December 2003
|
||||
//
|
||||
// Copyright (C) 2003 Andrew J. Ross - andy@plausible.org
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the
|
||||
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
// Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "interpolator.hxx"
|
||||
|
||||
void SGInterpolator::addNew(SGPropertyNode* prop, int nPoints)
|
||||
{
|
||||
// Set the property type to a double, if it isn't already, and
|
||||
// make sure we aren't already managing this node.
|
||||
prop->setDoubleValue(prop->getDoubleValue());
|
||||
cancel(prop);
|
||||
|
||||
Interp* iterp = new Interp();
|
||||
iterp->target = prop;
|
||||
iterp->nPoints = nPoints;
|
||||
iterp->curve = new double[2*nPoints];
|
||||
|
||||
// Dirty trick: leave the new value sitting in _list to avoid
|
||||
// having to return a pointer to a private type.
|
||||
iterp->next = _list;
|
||||
_list = iterp;
|
||||
}
|
||||
|
||||
void SGInterpolator::interpolate(SGPropertyNode* prop, int nPoints,
|
||||
double* values, double* deltas)
|
||||
{
|
||||
addNew(prop, nPoints);
|
||||
for(int i=0; i<nPoints; i++) {
|
||||
_list->dt(i) = deltas[i];
|
||||
_list->val(i) = values[i];
|
||||
}
|
||||
}
|
||||
|
||||
void SGInterpolator::interpolate(SGPropertyNode* prop, double val, double dt)
|
||||
{
|
||||
addNew(prop, 1);
|
||||
_list->dt(0) = dt;
|
||||
_list->val(0) = val;
|
||||
}
|
||||
|
||||
//
|
||||
// Delete all the list elements where "expr" is true.
|
||||
//
|
||||
// Silly preprocessor hack to avoid writing the linked list code in
|
||||
// two places. You would think that an STL set would be the way to
|
||||
// go, but I had terrible trouble getting it to work with the
|
||||
// dynamically allocated "curve" member. Frankly, this is easier to
|
||||
// write, and the code is smaller to boot...
|
||||
//
|
||||
#define DELETE_WHERE(EXPR)\
|
||||
Interp *p = _list, **last = &_list; \
|
||||
while(p) { \
|
||||
if(EXPR) { \
|
||||
*last = p->next; \
|
||||
delete p; \
|
||||
p = (*last) ? (*last)->next : 0; \
|
||||
} else { \
|
||||
last = &(p->next); \
|
||||
p = p->next; } }
|
||||
|
||||
void SGInterpolator::cancel(SGPropertyNode* prop)
|
||||
{
|
||||
DELETE_WHERE(p->target == prop)
|
||||
}
|
||||
|
||||
void SGInterpolator::update(double dt)
|
||||
{
|
||||
DELETE_WHERE(interp(p, dt))
|
||||
}
|
||||
|
||||
// This is the where the only "real" work happens. Walk through the
|
||||
// data points until we find one with some time left, slurp it up and
|
||||
// repeat until we run out of dt.
|
||||
bool SGInterpolator::interp(Interp* rec, double dt)
|
||||
{
|
||||
double val = rec->target->getDoubleValue();
|
||||
int i;
|
||||
for(i=0; i < rec->nPoints; i++) {
|
||||
if(rec->dt(i) > 0 && dt < rec->dt(i)) {
|
||||
val += (dt / rec->dt(i)) * (rec->val(i) - val);
|
||||
rec->dt(i) -= dt;
|
||||
break;
|
||||
}
|
||||
dt -= rec->dt(i);
|
||||
val = rec->val(i);
|
||||
}
|
||||
rec->target->setDoubleValue(val);
|
||||
|
||||
// Return true if this one is done
|
||||
return i == rec->nPoints;
|
||||
}
|
||||
71
simgear/misc/interpolator.hxx
Normal file
71
simgear/misc/interpolator.hxx
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef __SG_INTERPOLATOR_HXX
|
||||
#define __SG_INTERPOLATOR_HXX
|
||||
|
||||
// SGInterpolator
|
||||
// Subsystem that manages smooth linear interpolation of property
|
||||
// values across multiple data points and arbitrary time intervals.
|
||||
|
||||
// Written by Andrew J. Ross, started December 2003
|
||||
//
|
||||
// Copyright (C) 2003 Andrew J. Ross - andy@plausible.org
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the
|
||||
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
// Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
// TODO: support a callback upon interpolation completion so that user
|
||||
// code can register another one immediately without worrying about
|
||||
// timer aliasing.
|
||||
|
||||
class SGInterpolator : public SGSubsystem {
|
||||
public:
|
||||
SGInterpolator() { _list = 0; }
|
||||
virtual void init() {}
|
||||
virtual void update(double delta_time_sec);
|
||||
|
||||
// Simple method that interpolates a double property value from
|
||||
// its current value (default of zero) to the specified target
|
||||
// over the specified time.
|
||||
void interpolate(SGPropertyNode* prop, double value, double dt_sec);
|
||||
|
||||
// More elaborate version that takes a pointer to lists of
|
||||
// arbitrary size.
|
||||
void interpolate(SGPropertyNode* prop, int nPoints,
|
||||
double* values, double* deltas);
|
||||
|
||||
// Cancels any interpolation of the specified property, leaving
|
||||
// its value at the current (mid-interpolation) state.
|
||||
void cancel(SGPropertyNode* prop);
|
||||
|
||||
private:
|
||||
struct Interp {
|
||||
SGPropertyNode_ptr target;
|
||||
int nPoints;
|
||||
double* curve; // time0, val0, time1, val1, ...
|
||||
Interp* next;
|
||||
|
||||
~Interp() { delete[] curve; }
|
||||
double& dt(int i) { return curve[2*i]; }
|
||||
double& val(int i) { return curve[2*i + 1]; }
|
||||
};
|
||||
Interp* _list;
|
||||
|
||||
bool interp(Interp* rec, double dt);
|
||||
void addNew(SGPropertyNode* prop, int nPoints);
|
||||
};
|
||||
|
||||
#endif // __SG_INTERPOLATOR_HXX
|
||||
Reference in New Issue
Block a user