]> git.mxchange.org Git - simgear.git/commitdiff
Property interpolator subsystem. A utility, primarily for Nasal scripts
authorandy <andy>
Fri, 5 Dec 2003 01:49:44 +0000 (01:49 +0000)
committerandy <andy>
Fri, 5 Dec 2003 01:49:44 +0000 (01:49 +0000)
simgear/misc/Makefile.am
simgear/misc/interpolator.cxx [new file with mode: 0644]
simgear/misc/interpolator.hxx [new file with mode: 0644]

index d8a4555f17b98d019f79ffee52b1ed7a7524d541..f54ba13987e918b01b9e01a1d327bcc3eae11362 100644 (file)
@@ -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 (file)
index 0000000..285ec77
--- /dev/null
@@ -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;
+}
diff --git a/simgear/misc/interpolator.hxx b/simgear/misc/interpolator.hxx
new file mode 100644 (file)
index 0000000..9a66be9
--- /dev/null
@@ -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