From 27477402c93e34526ad51231a3238d333464a8ed Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 5 Dec 2003 01:49:44 +0000 Subject: [PATCH] Property interpolator subsystem. A utility, primarily for Nasal scripts --- simgear/misc/Makefile.am | 6 +- simgear/misc/interpolator.cxx | 107 ++++++++++++++++++++++++++++++++++ simgear/misc/interpolator.hxx | 71 ++++++++++++++++++++++ 3 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 simgear/misc/interpolator.cxx create mode 100644 simgear/misc/interpolator.hxx diff --git a/simgear/misc/Makefile.am b/simgear/misc/Makefile.am index d8a4555f..f54ba139 100644 --- a/simgear/misc/Makefile.am +++ b/simgear/misc/Makefile.am @@ -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 diff --git a/simgear/misc/interpolator.cxx b/simgear/misc/interpolator.cxx new file mode 100644 index 00000000..285ec773 --- /dev/null +++ b/simgear/misc/interpolator.cxx @@ -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; idt(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; +} diff --git a/simgear/misc/interpolator.hxx b/simgear/misc/interpolator.hxx new file mode 100644 index 00000000..9a66be9e --- /dev/null +++ b/simgear/misc/interpolator.hxx @@ -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 +#include + +// 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 -- 2.39.5