Browse Source

*** empty log message ***

David Rose 25 years ago
parent
commit
2057c6b11c
46 changed files with 6342 additions and 4862 deletions
  1. 2 0
      panda/src/egg2sg/Sources.pp
  2. 7 0
      panda/src/egg2sg/config_egg2sg.cxx
  3. 1 0
      panda/src/egg2sg/config_egg2sg.h
  4. 18 7
      panda/src/egg2sg/eggLoader.cxx
  5. 2 2
      panda/src/grutil/lineSegs.I
  6. 2 2
      panda/src/grutil/lineSegs.cxx
  7. 3 3
      panda/src/grutil/lineSegs.h
  8. 27 10
      panda/src/parametrics/Sources.pp
  9. 15 0
      panda/src/parametrics/classicNurbsCurve.I
  10. 614 0
      panda/src/parametrics/classicNurbsCurve.cxx
  11. 142 0
      panda/src/parametrics/classicNurbsCurve.h
  12. 21 8
      panda/src/parametrics/config_parametrics.cxx
  13. 610 0
      panda/src/parametrics/cubicCurveseg.cxx
  14. 142 0
      panda/src/parametrics/cubicCurveseg.h
  15. 0 1834
      panda/src/parametrics/curve.cxx
  16. 0 385
      panda/src/parametrics/curve.h
  17. 0 727
      panda/src/parametrics/curveDrawer.cxx
  18. 0 149
      panda/src/parametrics/curveDrawer.h
  19. 42 0
      panda/src/parametrics/curveFitter.I
  20. 203 286
      panda/src/parametrics/curveFitter.cxx
  21. 32 35
      panda/src/parametrics/curveFitter.h
  22. 46 136
      panda/src/parametrics/hermiteCurve.cxx
  23. 14 35
      panda/src/parametrics/hermiteCurve.h
  24. 0 985
      panda/src/parametrics/nurbsCurve.cxx
  25. 0 169
      panda/src/parametrics/nurbsCurve.h
  26. 31 26
      panda/src/parametrics/nurbsCurveDrawer.cxx
  27. 6 31
      panda/src/parametrics/nurbsCurveDrawer.h
  28. 81 0
      panda/src/parametrics/nurbsCurveInterface.I
  29. 172 0
      panda/src/parametrics/nurbsCurveInterface.cxx
  30. 83 0
      panda/src/parametrics/nurbsCurveInterface.h
  31. 4 0
      panda/src/parametrics/nurbsPPCurve.I
  32. 691 0
      panda/src/parametrics/nurbsPPCurve.cxx
  33. 123 0
      panda/src/parametrics/nurbsPPCurve.h
  34. 818 0
      panda/src/parametrics/parametricCurve.cxx
  35. 149 0
      panda/src/parametrics/parametricCurve.h
  36. 75 0
      panda/src/parametrics/parametricCurveCollection.I
  37. 721 0
      panda/src/parametrics/parametricCurveCollection.cxx
  38. 95 0
      panda/src/parametrics/parametricCurveCollection.h
  39. 31 0
      panda/src/parametrics/parametricCurveDrawer.I
  40. 451 0
      panda/src/parametrics/parametricCurveDrawer.cxx
  41. 93 0
      panda/src/parametrics/parametricCurveDrawer.h
  42. 626 0
      panda/src/parametrics/piecewiseCurve.cxx
  43. 107 0
      panda/src/parametrics/piecewiseCurve.h
  44. 10 1
      panda/src/sgmanip/nodePathCollection.I
  45. 30 30
      panda/src/sgmanip/nodePathCollection.cxx
  46. 2 1
      panda/src/sgmanip/nodePathCollection.h

+ 2 - 0
panda/src/egg2sg/Sources.pp

@@ -1,6 +1,8 @@
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
                    dtoolutil:c dtoolbase:c dtool:m
                    dtoolutil:c dtoolbase:c dtool:m
 
 
+#define USE_NURBSPP yes
+
 #begin lib_target
 #begin lib_target
   #define TARGET egg2sg
   #define TARGET egg2sg
   #define LOCAL_LIBS \
   #define LOCAL_LIBS \

+ 7 - 0
panda/src/egg2sg/config_egg2sg.cxx

@@ -48,6 +48,13 @@ bool egg_show_collision_solids = config_egg2sg.GetBool("egg-show-collision-solid
 // the relative paths, chosen arbitrarily).
 // the relative paths, chosen arbitrarily).
 bool egg_keep_texture_pathnames = config_egg2sg.GetBool("egg-keep-texture-pathnames", false);
 bool egg_keep_texture_pathnames = config_egg2sg.GetBool("egg-keep-texture-pathnames", false);
 
 
+// When this is true, a <NurbsCurve> entry appearing in an egg file
+// will load a ClassicNurbsCurve object instead of the default, a
+// NurbsCurve object.  This only makes a difference when the NURBS++
+// library is available, in which case the default, NurbsCurve, is
+// actually a NurbsPPCurve object.
+bool egg_load_classic_nurbs_curves = config_egg2sg.GetBool("egg-load-classic-nurbs-curves", false);
+
 CoordinateSystem egg_coordinate_system;
 CoordinateSystem egg_coordinate_system;
 
 
 ConfigureFn(config_egg2sg) {
 ConfigureFn(config_egg2sg) {

+ 1 - 0
panda/src/egg2sg/config_egg2sg.h

@@ -40,6 +40,7 @@ extern EXPCL_PANDAEGG bool egg_flatten;
 extern EXPCL_PANDAEGG bool egg_flatten_siblings;
 extern EXPCL_PANDAEGG bool egg_flatten_siblings;
 extern EXPCL_PANDAEGG bool egg_show_collision_solids;
 extern EXPCL_PANDAEGG bool egg_show_collision_solids;
 extern EXPCL_PANDAEGG bool egg_keep_texture_pathnames;
 extern EXPCL_PANDAEGG bool egg_keep_texture_pathnames;
+extern EXPCL_PANDAEGG bool egg_load_classic_nurbs_curves;
 
 
 extern EXPCL_PANDAEGG void init_libegg2sg();
 extern EXPCL_PANDAEGG void init_libegg2sg();
 
 

+ 18 - 7
panda/src/egg2sg/eggLoader.cxx

@@ -30,6 +30,8 @@
 #include <eggTextureCollection.h>
 #include <eggTextureCollection.h>
 #include <eggBin.h>
 #include <eggBin.h>
 #include <nurbsCurve.h>
 #include <nurbsCurve.h>
+#include <classicNurbsCurve.h>
+#include <nurbsCurveInterface.h>
 #include <builderBucket.h>
 #include <builderBucket.h>
 #include <builderPrim.h>
 #include <builderPrim.h>
 #include <builderVertex.h>
 #include <builderVertex.h>
@@ -1078,7 +1080,16 @@ make_node(EggNurbsCurve *egg_curve, NamedNode *parent) {
   assert(parent != NULL);
   assert(parent != NULL);
   assert(!parent->is_of_type(GeomNode::get_class_type()));
   assert(!parent->is_of_type(GeomNode::get_class_type()));
 
 
-  PT(NurbsCurve) curve = new NurbsCurve;
+  PT(ParametricCurve) curve;
+  
+  if (egg_load_classic_nurbs_curves) {
+    curve = new ClassicNurbsCurve;
+  } else {
+    curve = new NurbsCurve;
+  }
+
+  NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
+  nassertr(nurbs != (NurbsCurveInterface *)NULL, (RenderRelation *)NULL);
 
 
   if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
   if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
     egg2sg_cat.error()
     egg2sg_cat.error()
@@ -1087,24 +1098,24 @@ make_node(EggNurbsCurve *egg_curve, NamedNode *parent) {
     return (RenderRelation *)NULL;
     return (RenderRelation *)NULL;
   }
   }
 
 
-  curve->set_order(egg_curve->get_order());
+  nurbs->set_order(egg_curve->get_order());
 
 
   EggPrimitive::const_iterator pi;
   EggPrimitive::const_iterator pi;
   for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
   for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
-    curve->append_cv(LCAST(float, (*pi)->get_pos4()));
+    nurbs->append_cv(LCAST(float, (*pi)->get_pos4()));
   }
   }
 
 
   int num_knots = egg_curve->get_num_knots();
   int num_knots = egg_curve->get_num_knots();
-  if (num_knots != curve->get_num_knots()) {
+  if (num_knots != nurbs->get_num_knots()) {
     egg2sg_cat.error()
     egg2sg_cat.error()
       << "Invalid NURBSCurve number of knots for "
       << "Invalid NURBSCurve number of knots for "
       << egg_curve->get_name() << ": got " << num_knots
       << egg_curve->get_name() << ": got " << num_knots
-      << " knots, expected " << curve->get_num_knots() << "\n";
+      << " knots, expected " << nurbs->get_num_knots() << "\n";
     return (RenderRelation *)NULL;
     return (RenderRelation *)NULL;
   }
   }
     
     
   for (int i = 0; i < num_knots; i++) {
   for (int i = 0; i < num_knots; i++) {
-    curve->set_knot(i, egg_curve->get_knot(i));
+    nurbs->set_knot(i, egg_curve->get_knot(i));
   }
   }
 
 
   switch (egg_curve->get_curve_type()) {
   switch (egg_curve->get_curve_type()) {
@@ -1125,7 +1136,7 @@ make_node(EggNurbsCurve *egg_curve, NamedNode *parent) {
   }
   }
   curve->set_name(egg_curve->get_name());
   curve->set_name(egg_curve->get_name());
 
 
-  if (!curve->recompute()) {
+  if (!nurbs->recompute()) {
     egg2sg_cat.error()
     egg2sg_cat.error()
       << "Invalid NURBSCurve " << egg_curve->get_name() << "\n";
       << "Invalid NURBSCurve " << egg_curve->get_name() << "\n";
     return (RenderRelation *)NULL;
     return (RenderRelation *)NULL;

+ 2 - 2
panda/src/grutil/lineSegs.I

@@ -18,8 +18,8 @@ Point() {
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE LineSegs::Point::
 INLINE LineSegs::Point::
-Point(const Vertexf &point, const Colorf &color) :
-  _point(point), 
+Point(const LVecBase3f &point, const Colorf &color) :
+  _point(point[0], point[1], point[2]), 
   _color(color) 
   _color(color) 
 {
 {
 }
 }

+ 2 - 2
panda/src/grutil/lineSegs.cxx

@@ -53,7 +53,7 @@ reset() {
 //               move_to() or create(), this creates a single point.
 //               move_to() or create(), this creates a single point.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void LineSegs::
 void LineSegs::
-move_to(const Vertexf &v) {
+move_to(const LVecBase3f &v) {
   // We create a new SegmentList with the initial point in it.
   // We create a new SegmentList with the initial point in it.
   SegmentList segs;
   SegmentList segs;
   segs.push_back(Point(v, _color));
   segs.push_back(Point(v, _color));
@@ -72,7 +72,7 @@ move_to(const Vertexf &v) {
 //               is called.
 //               is called.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void LineSegs::
 void LineSegs::
-draw_to(const Vertexf &v) {
+draw_to(const LVecBase3f &v) {
   if (_list.empty()) {
   if (_list.empty()) {
     // Let our first call to draw_to() be an implicit move_to().
     // Let our first call to draw_to() be an implicit move_to().
     move_to(v);
     move_to(v);

+ 3 - 3
panda/src/grutil/lineSegs.h

@@ -36,10 +36,10 @@ PUBLISHED:
   INLINE void set_thickness(float thick);
   INLINE void set_thickness(float thick);
 
 
   INLINE void move_to(float x, float y, float z);
   INLINE void move_to(float x, float y, float z);
-  void move_to(const Vertexf &v);
+  void move_to(const LVecBase3f &v);
 
 
   INLINE void draw_to(float x, float y, float z);
   INLINE void draw_to(float x, float y, float z);
-  void draw_to(const Vertexf &v);
+  void draw_to(const LVecBase3f &v);
 
 
   const Vertexf &get_current_position();
   const Vertexf &get_current_position();
   bool is_empty();
   bool is_empty();
@@ -62,7 +62,7 @@ private:
   class Point {
   class Point {
   public:
   public:
     INLINE Point();
     INLINE Point();
-    INLINE Point(const Vertexf &point, const Colorf &color);
+    INLINE Point(const LVecBase3f &point, const Colorf &color);
     INLINE Point(const Point &copy);
     INLINE Point(const Point &copy);
     INLINE void operator = (const Point &copy);
     INLINE void operator = (const Point &copy);
 
 

+ 27 - 10
panda/src/parametrics/Sources.pp

@@ -1,28 +1,45 @@
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
                    dtoolutil:c dtoolbase:c dtool:m
                    dtoolutil:c dtoolbase:c dtool:m
 
 
+#define USE_NURBSPP yes
+
 #begin lib_target
 #begin lib_target
   #define TARGET parametrics
   #define TARGET parametrics
   #define LOCAL_LIBS \
   #define LOCAL_LIBS \
-    grutil linmath express putil pandabase
+    grutil sgattrib linmath express putil pandabase
 
 
   #define SOURCES \
   #define SOURCES \
+    classicNurbsCurve.I classicNurbsCurve.cxx classicNurbsCurve.h \
     config_parametrics.cxx config_parametrics.h \
     config_parametrics.cxx config_parametrics.h \
-    curve.cxx curve.h \
-    curveDrawer.cxx curveDrawer.h \
-    curveFitter.cxx curveFitter.h \
+    cubicCurveseg.cxx cubicCurveseg.h \
+    parametricCurveDrawer.I parametricCurveDrawer.cxx parametricCurveDrawer.h \
+    curveFitter.I curveFitter.cxx curveFitter.h \
     hermiteCurve.cxx hermiteCurve.h \
     hermiteCurve.cxx hermiteCurve.h \
-    nurbsCurve.cxx nurbsCurve.h \
-    nurbsCurveDrawer.cxx nurbsCurveDrawer.h
+    nurbsCurve.h \
+    nurbsCurveDrawer.cxx nurbsCurveDrawer.h \
+    nurbsCurveInterface.I nurbsCurveInterface.cxx nurbsCurveInterface.h \
+    parametricCurve.cxx parametricCurve.h \
+    parametricCurveCollection.I parametricCurveCollection.cxx \
+    parametricCurveCollection.h \
+    piecewiseCurve.cxx piecewiseCurve.h
+
+  #define IF_NURBSPP_SOURCES \
+    nurbsPPCurve.cxx nurbsPPCurve.I nurbsPPCurve.h
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
+    classicNurbsCurve.I classicNurbsCurve.h \
     config_parametrics.h \
     config_parametrics.h \
-    curve.h \
-    curveDrawer.h \
-    curveFitter.h \
+    cubicCurveseg.h \
+    parametricCurveDrawer.I parametricCurveDrawer.h \
+    curveFitter.I curveFitter.h \
     hermiteCurve.h \
     hermiteCurve.h \
     nurbsCurve.h \
     nurbsCurve.h \
-    nurbsCurveDrawer.h
+    nurbsCurveDrawer.h \
+    nurbsCurveInterface.I nurbsCurveInterface.h \
+    nurbsPPCurve.h nurbsPPCurve.I \
+    parametricCurve.h \
+    parametricCurveCollection.I parametricCurveCollection.h \
+    piecewiseCurve.h
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

+ 15 - 0
panda/src/parametrics/classicNurbsCurve.I

@@ -0,0 +1,15 @@
+// Filename: classicNurbsCurve.I
+// Created by:  drose (02Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::get_curveseg
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CubicCurveseg *ClassicNurbsCurve::
+get_curveseg(int ti) {
+  return (CubicCurveseg *)PiecewiseCurve::get_curveseg(ti);
+}

+ 614 - 0
panda/src/parametrics/classicNurbsCurve.cxx

@@ -0,0 +1,614 @@
+// Filename: classicNurbsCurve.C
+// Created by:  drose (27Feb98)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "classicNurbsCurve.h"
+#include "config_parametrics.h"
+
+#include <indent.h>
+#include <datagram.h>
+#include <datagramIterator.h>
+#include <bamWriter.h>
+#include <bamReader.h>
+
+////////////////////////////////////////////////////////////////////
+// Statics
+////////////////////////////////////////////////////////////////////
+
+TypeHandle ClassicNurbsCurve::_type_handle;
+TypeHandle ClassicNurbsCurve::_orig_type_handle;
+
+static const LVecBase3f zero = LVecBase3f(0.0, 0.0, 0.0);
+// This is returned occasionally from some of the functions, and is
+// used from time to time as an initializer.
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ClassicNurbsCurve::
+ClassicNurbsCurve() {
+  _order = 4;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::Copy Constructor
+//       Access: Published
+//  Description: Constructs a NURBS curve equivalent to the indicated
+//               (possibly non-NURBS) curve.
+////////////////////////////////////////////////////////////////////
+ClassicNurbsCurve::
+ClassicNurbsCurve(const ParametricCurve &pc) {
+  _order = 4;
+  
+  if (!pc.convert_to_nurbs(this)) {
+    parametrics_cat->warning() 
+      << "Cannot make a NURBS from the indicated curve.\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::Constructor
+//       Access: Published
+//  Description: Constructs a NURBS curve according to the indicated
+//               NURBS parameters.
+////////////////////////////////////////////////////////////////////
+ClassicNurbsCurve::
+ClassicNurbsCurve(int order, int num_cvs,
+		  const float knots[], const LVecBase4f cvs[]) {
+  _order = order;
+
+  int i;
+  _cvs.reserve(num_cvs);
+  for (i = 0; i < num_cvs; i++) {
+    append_cv(cvs[i]);
+  }
+
+  int num_knots = num_cvs + order;
+  for (i = 0; i < num_knots; i++) {
+    set_knot(i, knots[i]);
+  }
+
+  recompute();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ClassicNurbsCurve::
+~ClassicNurbsCurve() {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::set_order
+//       Access: Published, Virtual
+//  Description: Changes the order of the curve.  Must be a value from
+//               1 to 4.  Can only be done when there are no cv's.
+////////////////////////////////////////////////////////////////////
+void ClassicNurbsCurve::
+set_order(int order) {
+  nassertv(order >= 1 && order <= 4);
+  nassertv(_cvs.empty());
+
+  _order = order;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::get_order
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int ClassicNurbsCurve::
+get_order() const {
+  return _order;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::get_num_cvs
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int ClassicNurbsCurve::
+get_num_cvs() const {
+  return _cvs.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::get_num_knots
+//       Access: Published, Virtual
+//  Description: Returns the number of knots on the curve.
+////////////////////////////////////////////////////////////////////
+int ClassicNurbsCurve::
+get_num_knots() const {
+  return _cvs.size() + _order;
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::insert_cv
+//       Access: Published, Virtual
+//  Description: Inserts a new CV into the middle of the curve at the
+//               indicated parametric value.  This doesn't change the
+//               shape or timing of the curve; however, it is
+//               irreversible: if the new CV is immediately removed,
+//               the curve will be changed.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+insert_cv(float t) {
+  if (_cvs.empty()) {
+    append_cv(0.0, 0.0, 0.0);
+    return true;
+  }
+
+  if (t <= 0) {
+    t = 0.0;
+  }
+
+  int k = find_cv(t);
+  if (k < 0) {
+    append_cv(_cvs.back()._p);
+    return true;
+  }
+
+  // Now we are inserting a knot between k-1 and k.  We'll adjust the
+  // CV's according to Bohm's rule.
+
+  // First, get the new values of all the CV's that will change.
+  // These are the CV's in the range [k - (_order-1), k-1].
+
+  LVecBase4f new_cvs[3];
+  int i;
+  for (i = 0; i < _order-1; i++) {
+    int nk = i + k - (_order-1);
+    float ti = get_knot(nk);
+    float d = get_knot(nk + _order-1) - ti;
+    if (d == 0.0) {
+      new_cvs[i] = _cvs[nk-1]._p;
+    } else {
+      float a = (t - ti) / d;
+      new_cvs[i] = (1.0-a)*_cvs[nk-1]._p + a*_cvs[nk]._p;
+    }
+  }
+
+  // Now insert the new CV
+  _cvs.insert(_cvs.begin() + k-1, CV());
+
+  // Set all the new position values
+  for (i = 0; i < _order-1; i++) {
+    int nk = i + k - (_order-1);
+    _cvs[nk]._p = new_cvs[i];
+  }
+
+  // And set the new knot value.
+  _cvs[k-1]._t = t;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::remove_cv
+//       Access: Published, Virtual
+//  Description: Removes the indicated CV from the curve.  Returns
+//               true if the CV index was valid, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+remove_cv(int n) {
+  if (n < 0 || n >= (int)_cvs.size()) {
+    return false;
+  }
+
+  _cvs.erase(_cvs.begin() + n);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::remove_all_cvs
+//       Access: Published, Virtual
+//  Description: Removes all CV's from the curve.
+////////////////////////////////////////////////////////////////////
+void ClassicNurbsCurve::
+remove_all_cvs() {
+  _cvs.erase(_cvs.begin(), _cvs.end());
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::set_cv
+//       Access: Published, Virtual
+//  Description: Repositions the indicated CV.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+set_cv(int n, const LVecBase4f &v) {
+  nassertr(n >= 0 && n < get_num_cvs(), false);
+
+  _cvs[n]._p = v;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::get_cv
+//       Access: Published, Virtual
+//  Description: Returns the position in homogeneous space of the
+//               indicated CV.
+////////////////////////////////////////////////////////////////////
+LVecBase4f ClassicNurbsCurve::
+get_cv(int n) const {
+  nassertr(n >= 0 && n < get_num_cvs(), LVecBase4f::zero());
+
+  return _cvs[n]._p;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::set_knot
+//       Access: Published, Virtual
+//  Description: Sets the value of the indicated knot.  There are
+//               get_num_cvs() + _order knot values, but the first
+//               _order - 1 and the last 1 knot values cannot be
+//               changed.  It is also an error to set a knot value
+//               outside the range of its neighbors.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+set_knot(int n, float t) {
+  nassertr(n >= 0 && n < get_num_knots(), false);
+
+  if (n < _order || n-1 >= (int)_cvs.size()) {
+    return false;
+  }
+  _cvs[n-1]._t = t;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::get_knot
+//       Access: Published, Virtual
+//  Description: Retrieves the value of the indicated knot.
+////////////////////////////////////////////////////////////////////
+float ClassicNurbsCurve::
+get_knot(int n) const {
+  if (n < _order || _cvs.empty()) {
+    return 0.0;
+  } else if (n-1 >= (int)_cvs.size()) {
+    return _cvs.back()._t;
+  } else {
+    return _cvs[n-1]._t;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::recompute
+//       Access: Published, Virtual
+//  Description: Recalculates the curve basis according to the latest
+//               position of the CV's, knots, etc.  Until this
+//               function is called, adjusting the NURBS parameters
+//               will have no visible effect on the curve.  Returns
+//               true if the resulting curve is valid, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+recompute() {
+  _segs.erase(_segs.begin(), _segs.end());
+
+  float knots[8];
+  LVecBase4f cvs[4];
+
+  if ((int)_cvs.size() > _order-1) {
+    for (int cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
+      if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
+	// There are _order consecutive CV's that define each segment,
+	// beginning at cv.  Collect the CV's and knot values that define
+	// this segment.
+	int c;
+	for (c = 0; c < _order; c++) {
+	  cvs[c] = _cvs[c+cv]._p;
+	}
+	for (c = 0; c < _order+_order; c++) {
+	  knots[c] = get_knot(c+cv);
+	}
+	
+	insert_curveseg(_segs.size(), new CubicCurveseg(_order, knots, cvs),
+			knots[_order] - knots[_order-1]);
+      }
+    }
+  }
+
+  return !_segs.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::rebuild_curveseg
+//       Access: Public, Virtual
+//  Description: Rebuilds the current curve segment (as selected by
+//               the most recent call to find_curve()) according to
+//               the specified properties (see
+//               CubicCurveseg::compute_seg).  Returns true if
+//               possible, false if something goes horribly wrong.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+rebuild_curveseg(int rtype0, float t0, const LVecBase4f &v0,
+		 int rtype1, float t1, const LVecBase4f &v1,
+		 int rtype2, float t2, const LVecBase4f &v2,
+		 int rtype3, float t3, const LVecBase4f &v3) {
+  // Figure out which CV's contributed to this segment.
+  int seg = 0;
+
+  nassertr((int)_cvs.size() > _order-1, false);
+
+  int cv = 0;
+  for (cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
+    if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
+      if (seg == _last_ti) {
+	break;
+      }
+      seg++;
+    }
+  }
+
+  // Now copy the cvs and knots in question.
+  LMatrix4f G;
+  float knots[8];
+
+  int c;
+
+  // We only need to build the geometry matrix if at least one of the
+  // properties depends on the original value.
+  if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
+    for (c = 0; c < 4; c++) {
+      static const LVecBase4f zero(0.0, 0.0, 0.0, 0.0);
+      const LVecBase4f &s = (c < _order) ? _cvs[c+cv]._p : zero;
+      
+      G.set_col(c, s);
+    }
+  }
+
+  // But we always need the knot vector to determine the basis matrix.
+  for (c = 0; c < _order+_order; c++) {
+    knots[c] = get_knot(c+cv);
+  }
+
+  LMatrix4f B;
+  compute_nurbs_basis(_order, knots, B);
+
+  LMatrix4f Bi;
+  Bi = invert(B);
+
+  if (!CubicCurveseg::compute_seg(rtype0, t0, v0,
+				  rtype1, t1, v1,
+				  rtype2, t2, v2,
+				  rtype3, t3, v3,
+				  B, Bi, G)) {
+    return false;
+  }
+
+  // Now extract the new CV's from the new G matrix, and restore them
+  // to the curve.
+  for (c = 0; c < _order; c++) {
+    _cvs[c+cv]._p = G.get_col(c);
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::stitch
+//       Access: Published, Virtual
+//  Description: Regenerates this curve as one long curve: the first
+//               curve connected end-to-end with the second one.
+//               Either a or b may be the same as 'this'.
+//
+//               Returns true if successful, false on failure or if
+//               the curve type does not support stitching.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+stitch(const ParametricCurve *a, const ParametricCurve *b) {
+  // First, make a copy of both of our curves.  This ensures they are
+  // of the correct type, and also protects us in case one of them is
+  // the same as 'this'.
+  PT(ClassicNurbsCurve) na = new ClassicNurbsCurve(*a);
+  PT(ClassicNurbsCurve) nb = new ClassicNurbsCurve(*b);
+
+  if (na->get_num_cvs() == 0 || nb->get_num_cvs() == 0) {
+    return false;
+  }
+
+  if (na->get_order() != nb->get_order()) {
+    parametrics_cat->error()
+      << "Cannot stitch NURBS curves of different orders!\n";
+    return false;
+  }
+
+  // First, translate curve B to move its first CV to curve A's last
+  // CV.
+  LVecBase3f point_offset = 
+    na->get_cv_point(na->get_num_cvs() - 1) - nb->get_cv_point(0);
+  int num_b_cvs = nb->get_num_cvs();
+  for (int i = 0; i < num_b_cvs; i++) {
+    nb->set_cv_point(i, nb->get_cv_point(i) + point_offset);
+  }
+
+  // Now define a vector of all of A's CV's except the last one.
+  _cvs = na->_cvs;
+  if (!_cvs.empty()) {
+    _cvs.pop_back();
+  }
+
+  float t = na->get_max_t();
+
+  // Now add all the new CV's.
+  vector<CV>::iterator ci;
+  for (ci = nb->_cvs.begin(); ci != nb->_cvs.end(); ++ci) {
+    CV new_cv = (*ci);
+    new_cv._t += t;
+    _cvs.push_back(new_cv);
+  }
+
+  recompute();
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::get_nurbs_interface
+//       Access: Public, Virtual
+//  Description: Returns a pointer to the object as a
+//               NurbsCurveInterface object if it happens to be a
+//               NURBS-style curve; otherwise, returns NULL.
+////////////////////////////////////////////////////////////////////
+NurbsCurveInterface *ClassicNurbsCurve::
+get_nurbs_interface() {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::convert_to_nurbs
+//       Access: Public, Virtual
+//  Description: Stores in the indicated NurbsCurve a NURBS
+//               representation of an equivalent curve.  Returns true
+//               if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+convert_to_nurbs(ParametricCurve *nc) const {
+  nc->set_curve_type(_curve_type);
+  return NurbsCurveInterface::convert_to_nurbs(nc);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ClassicNurbsCurve::
+write(ostream &out, int indent_level) const {
+  NurbsCurveInterface::write(out, indent_level);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::append_cv_impl
+//       Access: Protected, Virtual
+//  Description: Adds a new CV to the end of the curve.  Creates a new
+//               knot value by adding 1 to the last knot value.
+//               Returns the index of the new CV.
+////////////////////////////////////////////////////////////////////
+int ClassicNurbsCurve::
+append_cv_impl(const LVecBase4f &v) {
+  _cvs.push_back(CV(v, get_knot(_cvs.size())+1.0));
+  return _cvs.size()-1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::format_egg
+//       Access: Protected, Virtual
+//  Description: Formats the curve as an egg structure to write to the
+//               indicated stream.  Returns true on success, false on
+//               failure.
+////////////////////////////////////////////////////////////////////
+bool ClassicNurbsCurve::
+format_egg(ostream &out, const string &name, const string &curve_type,
+	   int indent_level) const {
+  return NurbsCurveInterface::format_egg(out, name, curve_type, indent_level);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::find_cv
+//       Access: Protected
+//  Description: Finds the first knot whose value is >= t, or -1 if t
+//               is beyond the end of the curve.
+////////////////////////////////////////////////////////////////////
+int ClassicNurbsCurve::
+find_cv(float t) {
+  int i;
+  for (i = _order-1; i < (int)_cvs.size(); i++) {
+    if (_cvs[i]._t >= t) {
+      return i+1;
+    }
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::register_with_factory
+//       Access: Public, Static
+//  Description: Initializes the factory for reading these things from
+//               Bam files.
+////////////////////////////////////////////////////////////////////
+void ClassicNurbsCurve::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_ClassicNurbsCurve);
+  BamReader::get_factory()->register_factory(_orig_type_handle, make_ClassicNurbsCurve);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::make_ClassicNurbsCurve
+//       Access: Protected
+//  Description: Factory method to generate an object of this type.
+////////////////////////////////////////////////////////////////////
+TypedWriteable *ClassicNurbsCurve::
+make_ClassicNurbsCurve(const FactoryParams &params) {
+  ClassicNurbsCurve *me = new ClassicNurbsCurve;
+  BamReader *manager;
+  Datagram packet;
+
+  parse_params(params, manager, packet);
+  DatagramIterator scan(packet);
+
+  me->fillin(scan, manager);
+  return me;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::write_datagram
+//       Access: Protected, Virtual
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void ClassicNurbsCurve::
+write_datagram(BamWriter *manager, Datagram &me) {
+  PiecewiseCurve::write_datagram(manager, me);
+
+  me.add_int8(_order);
+
+  me.add_uint32(_cvs.size());
+  size_t i;
+  for (i = 0; i < _cvs.size(); i++) {
+    const CV &cv = _cvs[i];
+    cv._p.write_datagram(me);
+    me.add_float64(cv._t);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClassicNurbsCurve::fillin
+//       Access: Protected
+//  Description: Function that reads out of the datagram (or asks
+//               manager to read) all of the data that is needed to
+//               re-create this object and stores it in the appropiate
+//               place
+////////////////////////////////////////////////////////////////////
+void ClassicNurbsCurve::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PiecewiseCurve::fillin(scan, manager);
+
+  _order = scan.get_int8();
+
+  size_t num_cvs = scan.get_uint32();
+
+  _cvs.reserve(num_cvs);
+  size_t i;
+  for (i = 0; i < num_cvs; i++) {
+    CV cv;
+    cv._p.read_datagram(scan);
+    cv._t = scan.get_float64();
+    _cvs.push_back(cv);
+  }
+}

+ 142 - 0
panda/src/parametrics/classicNurbsCurve.h

@@ -0,0 +1,142 @@
+// Filename: classicNurbsCurve.h
+// Created by:  drose (27Feb98)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLASSICNURBSCURVE_H
+#define CLASSICNURBSCURVE_H
+
+#include <pandabase.h>
+
+#include "piecewiseCurve.h"
+#include "nurbsCurveInterface.h"
+#include "cubicCurveseg.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : ClassicNurbsCurve
+// Description : A Nonuniform Rational B-Spline.
+//
+//               This class is actually implemented as a
+//               PiecewiseCurve made up of several CubicCurvesegs,
+//               each of which is created using the nurbs_basis()
+//               method.  The list of CV's and knots is kept here,
+//               within the ClassicNurbsCurve class.
+//
+//               This class is the original Panda-native
+//               implementation of a NURBS curve.  It is typedeffed as
+//               "NurbsCurve" and performs all NURBS curve functions
+//               if we do not have the NURBS++ library available.
+//
+//               However, if we *do* have the NURBS++ library, another
+//               class exists, the NurbsPPCurve, which is a wrapper
+//               around that library and provides some additional
+//               functionality.  In that case, the other class is
+//               typedeffed to "NurbsCurve" instead of this one, and
+//               performs most of the NURBS curve functions.  This
+//               class then becomes vestigial.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ClassicNurbsCurve : public PiecewiseCurve, public NurbsCurveInterface {
+PUBLISHED:
+  ClassicNurbsCurve();
+  ClassicNurbsCurve(const ParametricCurve &pc);
+  ClassicNurbsCurve(int order, int num_cvs,
+		    const float knots[], const LVecBase4f cvs[]);
+  virtual ~ClassicNurbsCurve();
+
+public:
+  // We don't need to re-publish these, since they're all published
+  // from NurbsCurveInterface.
+  virtual void set_order(int order);
+  virtual int get_order() const;
+
+  virtual int get_num_cvs() const;
+  virtual int get_num_knots() const;
+
+  virtual bool insert_cv(float t);
+
+  virtual bool remove_cv(int n);
+  virtual void remove_all_cvs();
+
+  virtual bool set_cv(int n, const LVecBase4f &v);
+  virtual LVecBase4f get_cv(int n) const;
+
+  virtual bool set_knot(int n, float t);
+  virtual float get_knot(int n) const;
+
+  virtual bool recompute();
+
+public:
+  virtual bool
+  rebuild_curveseg(int rtype0, float t0, const LVecBase4f &v0,
+		   int rtype1, float t1, const LVecBase4f &v1,
+		   int rtype2, float t2, const LVecBase4f &v2,
+		   int rtype3, float t3, const LVecBase4f &v3);
+
+  virtual bool stitch(const ParametricCurve *a, const ParametricCurve *b);
+
+  INLINE CubicCurveseg *get_curveseg(int ti);
+
+  virtual NurbsCurveInterface *get_nurbs_interface();
+  virtual bool convert_to_nurbs(ParametricCurve *nc) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+protected:
+  virtual int append_cv_impl(const LVecBase4f &v);
+  virtual bool format_egg(ostream &out, const string &name, 
+			  const string &curve_type, int indent_level) const;
+
+  int find_cv(float t);
+
+  int _order;
+
+  class CV {
+  public:
+    CV() {}
+    CV(const LVecBase4f &p, float t) : _p(p), _t(t) {}
+    LVecBase4f _p;
+    float _t;
+  };
+
+  vector<CV> _cvs;
+
+
+// TypedWriteable stuff
+public:
+  static void register_with_read_factory();
+
+protected:
+  static TypedWriteable *make_ClassicNurbsCurve(const FactoryParams &params);
+  virtual void write_datagram(BamWriter *manager, Datagram &me);  
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PiecewiseCurve::init_type();
+    NurbsCurveInterface::init_type();
+    register_type(_type_handle, "ClassicNurbsCurve",
+                  PiecewiseCurve::get_class_type(),
+		  NurbsCurveInterface::get_class_type());
+    register_type(_orig_type_handle, "NurbsCurve",
+                  PiecewiseCurve::get_class_type(),
+		  NurbsCurveInterface::get_class_type());
+ }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+ 
+private:
+  static TypeHandle _type_handle;
+  static TypeHandle _orig_type_handle;
+};
+
+#ifndef HAVE_NURBSPP
+typedef ClassicNurbsCurve NurbsCurve;
+#endif
+
+#include "classicNurbsCurve.I"
+
+#endif

+ 21 - 8
panda/src/parametrics/config_parametrics.cxx

@@ -3,13 +3,20 @@
 // 
 // 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#include "classicNurbsCurve.h"
 #include "config_parametrics.h"
 #include "config_parametrics.h"
-#include "curve.h"
-#include "curveDrawer.h"
+#include "cubicCurveseg.h"
 #include "curveFitter.h"
 #include "curveFitter.h"
 #include "hermiteCurve.h"
 #include "hermiteCurve.h"
-#include "nurbsCurve.h"
 #include "nurbsCurveDrawer.h"
 #include "nurbsCurveDrawer.h"
+#include "nurbsCurveInterface.h"
+#include "parametricCurve.h"
+#include "parametricCurveDrawer.h"
+#include "piecewiseCurve.h"
+
+#ifdef HAVE_NURBSPP
+#include "nurbsPPCurve.h"
+#endif
 
 
 #include "get_config_path.h"
 #include "get_config_path.h"
 
 
@@ -17,18 +24,24 @@ Configure(config_parametrics);
 NotifyCategoryDef(parametrics, "");
 NotifyCategoryDef(parametrics, "");
 
 
 ConfigureFn(config_parametrics) {
 ConfigureFn(config_parametrics) {
-  ParametricCurve::init_type();
-  PiecewiseCurve::init_type();
+  ClassicNurbsCurve::init_type();
   CubicCurveseg::init_type();
   CubicCurveseg::init_type();
-  ParametricCurveDrawer::init_type();
   CurveFitter::init_type();
   CurveFitter::init_type();
   HermiteCurve::init_type();
   HermiteCurve::init_type();
-  NurbsCurve::init_type();
   NurbsCurveDrawer::init_type();
   NurbsCurveDrawer::init_type();
+  NurbsCurveInterface::init_type();
+  NurbsPPCurve::init_type();
+  ParametricCurve::init_type();
+  ParametricCurveDrawer::init_type();
+  PiecewiseCurve::init_type();
+
+#ifdef HAVE_NURBSPP
+  NurbsPPCurve::init_type();
+#endif
 
 
+  ClassicNurbsCurve::register_with_read_factory();
   CubicCurveseg::register_with_read_factory();
   CubicCurveseg::register_with_read_factory();
   HermiteCurve::register_with_read_factory();
   HermiteCurve::register_with_read_factory();
-  NurbsCurve::register_with_read_factory();
 }
 }
 
 
 const DSearchPath &
 const DSearchPath &

+ 610 - 0
panda/src/parametrics/cubicCurveseg.cxx

@@ -0,0 +1,610 @@
+// Filename: cubicCurveseg.cxx
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "piecewiseCurve.h"
+
+#include "config_parametrics.h"
+#include "hermiteCurve.h"
+
+#include <datagram.h>
+#include <datagramIterator.h>
+#include <bamWriter.h>
+#include <bamReader.h>
+
+TypeHandle CubicCurveseg::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CubicCurveseg::
+CubicCurveseg() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::Constructor
+//       Access: Public
+//  Description: Creates the curveseg given the four basis vectors
+//               (the columns of the matrix) explicitly.
+////////////////////////////////////////////////////////////////////
+CubicCurveseg::
+CubicCurveseg(const LMatrix4f &basis) {
+  Bx = basis.get_col(0);
+  By = basis.get_col(1);
+  Bz = basis.get_col(2);
+  Bw = basis.get_col(3);
+  rational = true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::Constructor
+//       Access: Public
+//  Description: Creates the curveseg as a Bezier segment.
+////////////////////////////////////////////////////////////////////
+CubicCurveseg::
+CubicCurveseg(const BezierSeg &seg) {
+  bezier_basis(seg);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::Constructor
+//       Access: Public
+//  Description: Creates the curveseg as a NURBS segment.  See
+//               nurbs_basis for a description of the parameters.
+////////////////////////////////////////////////////////////////////
+CubicCurveseg::
+CubicCurveseg(int order, const float knots[], const LVecBase4f cvs[]) {
+  nurbs_basis(order, knots, cvs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+CubicCurveseg::
+~CubicCurveseg() {
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::get_point
+//       Access: Published, Virtual
+//  Description: Computes the surface point at a given parametric
+//               point t.
+////////////////////////////////////////////////////////////////////
+bool CubicCurveseg::
+get_point(float t, LVecBase3f &point) const {
+  evaluate_point(LVecBase4f(t*t*t, t*t, t, 1.0), point);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::get_tangent
+//       Access: Published, Virtual
+//  Description: Computes the surface tangent at a given parametric
+//               point t.
+////////////////////////////////////////////////////////////////////
+bool CubicCurveseg::
+get_tangent(float t, LVecBase3f &tangent) const {
+  evaluate_vector(LVecBase4f(3.0*t*t, 2.0*t, 1.0, 0.0), tangent);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::get_pt
+//       Access: Published, Virtual
+//  Description: Simultaneously computes the point and the tangent at
+//               the given parametric point.
+////////////////////////////////////////////////////////////////////
+bool CubicCurveseg::
+get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const {
+  evaluate_point(LVecBase4f(t*t*t, t*t, t, 1.0), point);
+  evaluate_vector(LVecBase4f(3.0*t*t, 2.0*t, 1.0, 0.0), tangent);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::get_2ndtangent
+//       Access: Published, Virtual
+//  Description: Computes the surface 2nd-order tangent at a given
+//               parametric point t.
+////////////////////////////////////////////////////////////////////
+bool CubicCurveseg::
+get_2ndtangent(float t, LVecBase3f &tangent2) const {
+  evaluate_vector(LVecBase4f(6.0*t, 2.0, 0.0, 0.0), tangent2);
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::hermite_basis
+//       Access: Public
+//  Description: Defines the curve segment as a Hermite.  This only
+//               sets up the basis vectors, so the curve will be
+//               computed correctly; it does not retain the CV's.
+////////////////////////////////////////////////////////////////////
+void CubicCurveseg::
+hermite_basis(const HermiteCurveCV &cv0,
+              const HermiteCurveCV &cv1,
+              float tlength) {
+  static LMatrix4f 
+    Mh(2, -3, 0, 1,
+       -2, 3, 0, 0,
+       1, -2, 1, 0,
+       1, -1, 0, 0);
+
+  LVecBase4f Gx(cv0._p[0], cv1._p[0],
+		cv0._out[0]*tlength, cv1._in[0]*tlength);
+  LVecBase4f Gy(cv0._p[1], cv1._p[1],
+		cv0._out[1]*tlength, cv1._in[1]*tlength);
+  LVecBase4f Gz(cv0._p[2], cv1._p[2], 
+		cv0._out[2]*tlength, cv1._in[2]*tlength);
+
+  Bx = Gx * Mh;
+  By = Gy * Mh;
+  Bz = Gz * Mh;
+  rational = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::bezier_basis
+//       Access: Public
+//  Description: Defines the curve segment as a Bezier.  This only
+//               sets up the basis vectors, so the curve will be
+//               computed correctly; it does not retain the CV's.
+////////////////////////////////////////////////////////////////////
+void CubicCurveseg::
+bezier_basis(const BezierSeg &seg) {
+  static LMatrix4f
+    Mb(-1, 3, -3, 1,
+       3, -6, 3, 0,
+       -3, 3, 0, 0,
+       1, 0, 0, 0);
+
+  LVecBase4f Gx(seg._v[0][0], seg._v[1][0], seg._v[2][0], seg._v[3][0]);
+  LVecBase4f Gy(seg._v[0][1], seg._v[1][1], seg._v[2][1], seg._v[3][1]);
+  LVecBase4f Gz(seg._v[0][2], seg._v[1][2], seg._v[2][2], seg._v[3][2]);
+
+  Bx = Gx * Mb;
+  By = Gy * Mb;
+  Bz = Gz * Mb;
+  rational = false;
+}
+
+static LVecBase4f
+nurbs_blending_function(int order, int i, int j,
+                        const float knots[]) {
+  // This is doubly recursive.  Ick.
+  LVecBase4f r;
+
+  if (j==1) {
+    if (i==order-1 && knots[i] < knots[i+1]) {
+      r.set(0.0, 0.0, 0.0, 1.0);
+    } else {
+      r.set(0.0, 0.0, 0.0, 0.0);
+    }
+
+  } else {
+    LVecBase4f bi0 = nurbs_blending_function(order, i, j-1, knots);
+    LVecBase4f bi1 = nurbs_blending_function(order, i+1, j-1, knots);
+
+    float d0 = knots[i+j-1] - knots[i];
+    float d1 = knots[i+j] - knots[i+1];
+
+    // First term.  Division by zero is defined to equal zero.
+    if (d0 != 0.0) {
+      if (d1 != 0.0) {
+        r = bi0 / d0 - bi1 / d1;
+      } else {
+        r = bi0 / d0;
+      }
+
+    } else if (d1 != 0.0) {
+      r = - bi1 / d1;
+
+    } else {
+      r.set(0.0, 0.0, 0.0, 0.0);
+    }
+
+    // scale by t.
+    r[0] = r[1];
+    r[1] = r[2];
+    r[2] = r[3];
+    r[3] = 0.0;
+
+    // Second term.
+    if (d0 != 0.0) {
+      if (d1 != 0.0) {
+        r += bi0 * (- knots[i] / d0) + bi1 * (knots[i+j] / d1);
+      } else {
+        r += bi0 * (- knots[i] / d0);
+      }
+
+    } else if (d1 != 0.0) {
+      r += bi1 * (knots[i+j] / d1);
+    }
+  }
+
+  return r;
+}
+
+void
+compute_nurbs_basis(int order,
+                    const float knots_in[],
+                    LMatrix4f &basis) {
+  int i;
+
+  // Scale the supplied knots to the range 0..1.
+  float knots[8];
+  float mink = knots_in[order-1];
+  float maxk = knots_in[order];
+
+  if (mink==maxk) {
+    // Huh.  What were you thinking?  This is a trivial NURBS.
+    parametrics_cat->warning()
+      << "Trivial NURBS curve specified." << endl;
+    memset((void *)&basis, 0, sizeof(LMatrix4f));
+    return;
+  }
+
+  for (i = 0; i<2*order; i++) {
+    knots[i] = (knots_in[i] - mink) / (maxk-mink);
+  }
+
+
+  LVecBase4f b[4];
+  for (i = 0; i<order; i++) {
+    b[i] = nurbs_blending_function(order, i, order, knots);
+  }
+
+  for (i = 0; i<order; i++) {
+    basis.set_row(i, b[i]);
+  }
+
+  for (i=order; i<4; i++) {
+    basis.set_row(i, LVecBase4f::zero());
+  }
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::nurbs_basis
+//       Access: Public
+//  Description: Defines the curve segment as a NURBS.  Order is one
+//               more than the degree, and must be 1, 2, 3, or 4;
+//               knots is an array of order*2 values, and cvs is an
+//               array of order values.
+////////////////////////////////////////////////////////////////////
+void CubicCurveseg::
+nurbs_basis(int order, const float knots[], const LVecBase4f cvs[]) {
+  assert(order>=1 && order<=4);
+
+  LMatrix4f B;
+  compute_nurbs_basis(order, knots, B);
+
+  // Create a local copy of our CV's, so we can zero out the unused
+  // elements.
+  LVecBase4f c[4];
+  for (int i = 0; i < 4; i++) {
+    c[i] = (i<order) ? cvs[i] : LVecBase4f(0.0, 0.0, 0.0, 0.0);
+  }
+
+  Bx = LVecBase4f(c[0][0], c[1][0], c[2][0], c[3][0]) * B;
+  By = LVecBase4f(c[0][1], c[1][1], c[2][1], c[3][1]) * B;
+  Bz = LVecBase4f(c[0][2], c[1][2], c[2][2], c[3][2]) * B;
+  Bw = LVecBase4f(c[0][3], c[1][3], c[2][3], c[3][3]) * B;
+
+  rational = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::get_bezier_seg
+//       Access: Public, Virtual
+//  Description: Fills the BezierSeg structure with a description of
+//               the curve segment as a Bezier, if possible, but does
+//               not change the _t member of the structure.  Returns
+//               true if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool CubicCurveseg::
+get_bezier_seg(BezierSeg &seg) const {
+  static LMatrix4f
+    Mbi(0.0, 0.0, 0.0, 1.0,
+        0.0, 0.0, 1.0/3.0, 1.0,
+        0.0, 1.0/3.0, 2.0/3.0, 1.0,
+        1.0, 1.0, 1.0, 1.0);
+
+  LVecBase4f Gx = Bx * Mbi;
+  LVecBase4f Gy = By * Mbi;
+  LVecBase4f Gz = Bz * Mbi;
+
+  if (rational) {
+    LVecBase4f Gw = Bw * Mbi;
+    seg._v[0].set(Gx[0]/Gw[0], Gy[0]/Gw[0], Gz[0]/Gw[0]);
+    seg._v[1].set(Gx[1]/Gw[1], Gy[1]/Gw[1], Gz[1]/Gw[1]);
+    seg._v[2].set(Gx[2]/Gw[2], Gy[2]/Gw[2], Gz[2]/Gw[2]);
+    seg._v[3].set(Gx[3]/Gw[3], Gy[3]/Gw[3], Gz[3]/Gw[3]);
+  } else {
+    seg._v[0].set(Gx[0], Gy[0], Gz[0]);
+    seg._v[1].set(Gx[1], Gy[1], Gz[1]);
+    seg._v[2].set(Gx[2], Gy[2], Gz[2]);
+    seg._v[3].set(Gx[3], Gy[3], Gz[3]);
+  }
+
+  return true;
+}
+
+
+// We need this operator since Performer didn't supply it.
+inline LVecBase4f
+col_mult(const LMatrix4f &M, const LVecBase4f &v) {
+  return LVecBase4f(M(0,0)*v[0] + M(0,1)*v[1] + M(0,2)*v[2] + M(0,3)*v[3],
+                M(1,0)*v[0] + M(1,1)*v[1] + M(1,2)*v[2] + M(1,3)*v[3],
+                M(2,0)*v[0] + M(2,1)*v[1] + M(2,2)*v[2] + M(2,3)*v[3],
+                M(3,0)*v[0] + M(3,1)*v[1] + M(3,2)*v[2] + M(3,3)*v[3]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: compute_seg_col
+//  Description: Interprets the parameters for a particular column of
+//               compute_seg.  Builds the indicated column of T
+//               and P.
+////////////////////////////////////////////////////////////////////
+static bool
+compute_seg_col(int c,
+                int rtype, float t, const LVecBase4f &v,
+                const LMatrix4f &B,
+                const LMatrix4f &Bi,
+                const LMatrix4f &G,
+                const LMatrix4f &GB,
+                LMatrix4f &T, LMatrix4f &P) {
+  bool keep_orig = ((rtype & RT_KEEP_ORIG) != 0);
+
+  if (parametrics_cat.is_debug()) {
+    parametrics_cat.debug()
+      << "Computing col " << c << " type " << (rtype & RT_BASE_TYPE)
+      << " at " << t << " keep_orig = " << keep_orig
+      << " v = " << v << "\n";
+  }
+
+  switch (rtype & RT_BASE_TYPE) {
+    // RT_point defines the point on the curve at t.  This is the vector
+    // [ t^3 t^2 t^1 t^0 ].
+  case RT_POINT:
+    T.set_col(c, LVecBase4f(t*t*t, t*t, t, 1.0));
+    if (keep_orig) {
+      LVecBase4f vec(t*t*t, t*t, t, 1.0);
+      LVecBase4f ov = col_mult(GB, vec);
+      if (parametrics_cat.is_debug()) {
+	parametrics_cat.debug()
+	  << "orig point = " << ov << "\n";
+      }
+      P.set_col(c, ov);
+    } else {
+      P.set_col(c, v);
+    }
+    break;
+
+    // RT_tangent defines the tangent to the curve at t.  This is
+    // the vector [ 3t^2 2t 1 0 ].
+  case RT_TANGENT:
+    T.set_col(c, LVecBase4f(3.0*t*t, 2.0*t, 1.0, 0.0));
+    if (keep_orig) {
+      LVecBase4f vec(3.0*t*t, 2.0*t, 1.0, 0.0);
+      LVecBase4f ov = col_mult(GB, vec);
+      if (parametrics_cat.is_debug()) {
+	parametrics_cat.debug()
+	  << "Matrix is:\n";
+	GB.write(parametrics_cat.debug(false), 2);
+	parametrics_cat.debug(false)
+	  << "vector is " << vec << "\n"
+	  << "orig tangent = " << ov << "\n";
+      }
+      P.set_col(c, ov);
+    } else {
+      P.set_col(c, v);
+    }
+    break;
+
+    // RT_cv defines the cth control point.  This is the cth column
+    // vector from Bi.
+  case RT_CV:
+    T.set_col(c, Bi.get_col(c));
+    if (keep_orig) {
+      if (parametrics_cat.is_debug()) {
+	parametrics_cat.debug()
+	  << "orig CV = " << G.get_col(c) << "\n";
+      }
+      P.set_col(c, G.get_col(c));
+    } else {
+      P.set_col(c, v);
+    }
+    break;
+
+  default:
+    cerr << "Invalid rebuild type in compute_seg\n";
+    return false;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::compute_seg
+//       Access: Public, Static
+//  Description: Given a set of four properties of a curve segment
+//               (e.g. four points, four tangent values, four control
+//               points, or any combination), and a basis matrix,
+//               computes the corresponding geometry matrix that
+//               (together with the basis matrix) represents the curve
+//               that satisfies the four properties.
+//
+//               The basis matrix is passed in as B, and its inverse
+//               must be precomputed and passed in as Bi.
+//
+//               The result is returned in the matrix G, each column
+//               of which represents the cth control vertex.  If any
+//               of the four properties has RT_KEEP_ORIG set (see
+//               below), G's input value is used to define the
+//               original shape of the curve; otherwise, G's input
+//               value is ignored.
+//
+//               Each property is defined by an rtype, which may be
+//               any of RT_POINT, RT_TANGENT, or RT_CV, and may or may
+//               not be or'ed with RT_KEEP_ORIG.  The meanings of the
+//               types are as follows:
+//
+//               RT_POINT defines a specific point which the curve
+//               segment must pass through.  t is in the range [0,1]
+//               and represents the parametric value at which the
+//               curve segment will intersect the given point.  If
+//               RT_KEEP_ORIG is not set, v defines the point;
+//               otherwise, v is ignored and the original curve at
+//               point t defines the point.
+//
+//               RT_TANGENT defines a specific tangent value which the
+//               curve segment must have at point t.  As with
+//               RT_POINT, if RT_KEEP_ORIG is not set, v defines the
+//               tangent; otherwise, v is ignored and the original
+//               curve defines the tangent.
+//
+//               RT_CV defines a specific control vertex which the
+//               curve segment must have.  In this case, t is ignored.
+//               The position within the argument list determines
+//               which control vertex is applicable; e.g. rtype0 =
+//               RT_CV defines control vertex 0, and rtype2 = RT_CV
+//               defines control vertex 2.  If RT_KEEP_ORIG is not
+//               set, v defines the new control vertex; otherwise, the
+//               control vertex is taken from G.
+//
+//               The return value is true if all the parameters are
+//               sensible, or false if there is some error.
+////////////////////////////////////////////////////////////////////
+bool CubicCurveseg::
+compute_seg(int rtype0, float t0, const LVecBase4f &v0,
+            int rtype1, float t1, const LVecBase4f &v1,
+            int rtype2, float t2, const LVecBase4f &v2,
+            int rtype3, float t3, const LVecBase4f &v3,
+            const LMatrix4f &B,
+            const LMatrix4f &Bi,
+            LMatrix4f &G) {
+
+  // We can define a cubic curve segment given four arbitrary
+  // properties of the segment: any point along the curve, any tangent
+  // along the curve, any control point.  Given any four such
+  // properties, a single cubic curve segment is defined.
+
+  // For a given cubic curve segment so defined, and given a basis
+  // matrix B, we can define the four control vertices that represent
+  // the segment with the basis matrix.  That is, we can define the
+  // matrix G such that G * B * tc, where tc is [ t^3 t^2 t^1 t^0 ]
+  // for t in [ 0..1 ], represents the point on the curve segment
+  // corresponding to t.
+
+  // First, we build a matrix T, such that each of the four columns of
+  // T contains the vector that would compute the corresponding
+  // property.  We also build a corresponding matrix P, such that each
+  // of its columns contains the vector that is the solution of the
+  // corresponding column in T.
+
+  LMatrix4f T, P, GB;
+
+  // GB is G * B, but we only need to compute this if any of the
+  // columns wants the value from the original G.
+  if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
+    GB = G * B;
+  }
+
+  if (! (compute_seg_col(0, rtype0, t0, v0, B, Bi, G, GB, T, P) &&
+         compute_seg_col(1, rtype1, t1, v1, B, Bi, G, GB, T, P) &&
+         compute_seg_col(2, rtype2, t2, v2, B, Bi, G, GB, T, P) &&
+         compute_seg_col(3, rtype3, t3, v3, B, Bi, G, GB, T, P))) {
+    return false;
+  }
+
+  LMatrix4f Ti;
+  Ti = invert(T);
+
+  // Now we have T and P such that P represents the solution of T,
+  // when T is applied to the geometry and basis matrices.  That is,
+  // each column of P represents the solution computed by the
+  // corresponding column of T.  P = G * B * T.
+
+  // We simply solve for G and get G = P * T^(-1) * B^(-1).
+
+  G = P * Ti * Bi;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::register_with_factory
+//       Access: Public, Static
+//  Description: Initializes the factory for reading these things from
+//               Bam files.
+////////////////////////////////////////////////////////////////////
+void CubicCurveseg::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_CubicCurveseg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::make_CubicCurveseg
+//       Access: Protected
+//  Description: Factory method to generate an object of this type.
+////////////////////////////////////////////////////////////////////
+TypedWriteable *CubicCurveseg::
+make_CubicCurveseg(const FactoryParams &params) {
+  CubicCurveseg *me = new CubicCurveseg;
+  BamReader *manager;
+  Datagram packet;
+
+  parse_params(params, manager, packet);
+  DatagramIterator scan(packet);
+
+  me->fillin(scan, manager);
+  return me;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::write_datagram
+//       Access: Protected, Virtual
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void CubicCurveseg::
+write_datagram(BamWriter *manager, Datagram &me) {
+  ParametricCurve::write_datagram(manager, me);
+
+  Bx.write_datagram(me);
+  By.write_datagram(me);
+  Bz.write_datagram(me);
+  Bw.write_datagram(me);
+  me.add_bool(rational);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CubicCurveseg::fillin
+//       Access: Protected
+//  Description: Function that reads out of the datagram (or asks
+//               manager to read) all of the data that is needed to
+//               re-create this object and stores it in the appropiate
+//               place
+////////////////////////////////////////////////////////////////////
+void CubicCurveseg::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  ParametricCurve::fillin(scan, manager);
+
+  Bx.read_datagram(scan);
+  By.read_datagram(scan);
+  Bz.read_datagram(scan);
+  Bw.read_datagram(scan);
+  rational = scan.get_bool();
+}

+ 142 - 0
panda/src/parametrics/cubicCurveseg.h

@@ -0,0 +1,142 @@
+// Filename: cubicCurveseg.h
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CUBICCURVESEG_H
+#define CUBICCURVESEG_H
+
+#include <pandabase.h>
+
+#include "parametricCurve.h"
+
+
+// These symbols are used to define the shape of the curve segment to
+// CubicCurveseg::compute_seg().
+
+#define RT_POINT       0x01
+#define RT_TANGENT     0x02
+#define RT_CV          0x03
+#define RT_BASE_TYPE   0xff
+
+#define RT_KEEP_ORIG  0x100
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : CubicCurveseg
+// Description : A CubicCurveseg is any curve that can be completely
+//               described by four 4-valued basis vectors, one for
+//               each dimension in three-space, and one for the
+//               homogeneous coordinate.  This includes Beziers,
+//               Hermites, and NURBS.
+//
+//               This class encapsulates a single curve segment of the
+//               cubic curve.  Normally, when we think of Bezier and
+//               Hermite curves, we think of a piecewise collection of
+//               such segments.
+//
+//               Although this class includes methods such as
+//               hermite_basis() and nurbs_basis(), to generate a
+//               Hermite and NURBS curve segment, respectively, only
+//               the final basis vectors are stored: the product of
+//               the basis matrix of the corresponding curve type, and
+//               its geometry vectors.  This is the minimum
+//               information needed to evaluate the curve.  However,
+//               the individual CV's that were used to compute these
+//               basis vectors are not retained; this might be handled
+//               in a subclass (for instance, HermiteCurve).
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA CubicCurveseg : public ParametricCurve {
+PUBLISHED:
+  virtual bool get_point(float t, LVecBase3f &point) const;
+  virtual bool get_tangent(float t, LVecBase3f &tangent) const;
+  virtual bool get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const;
+  virtual bool get_2ndtangent(float t, LVecBase3f &tangent2) const;
+
+public:
+  CubicCurveseg();
+  CubicCurveseg(const LMatrix4f &basis);
+  CubicCurveseg(const BezierSeg &seg);
+  CubicCurveseg(int order, const float knots[], const LVecBase4f cvs[]);
+
+  virtual ~CubicCurveseg();
+
+  void hermite_basis(const HermiteCurveCV &cv0,
+		     const HermiteCurveCV &cv1,
+		     float tlength = 1.0);
+  void bezier_basis(const BezierSeg &seg);
+  void nurbs_basis(int order, const float knots[], const LVecBase4f cvs[]);
+
+  // evaluate_point() and evaluate_vector() both evaluate the curve at
+  // a given point by applying the basis vector against the vector
+  // [t3 t2 t 1] (or some derivative).  The difference between the
+  // two is that evaluate_point() is called only with the vector
+  // [t3 t2 t 1] and computes a point in three-space and will scale by
+  // the homogeneous coordinate when the curve demands it (e.g. a
+  // NURBS), while evaluate_vector() is called with some derivative
+  // vector like [3t2 2t 1 0] and computes a vector difference between
+  // points, and will never scale by the homogeneous coordinate (which
+  // would be zero anyway).
+
+  void evaluate_point(const LVecBase4f &tv, LVecBase3f &result) const {
+    float h = (rational) ? tv.dot(Bw) : 1.0;
+    result.set(tv.dot(Bx) / h,
+               tv.dot(By) / h,
+               tv.dot(Bz) / h);
+  }
+
+  void evaluate_vector(const LVecBase4f &tv, LVecBase3f &result) const {
+    result.set(tv.dot(Bx),
+               tv.dot(By),
+               tv.dot(Bz));
+  }
+
+  virtual bool get_bezier_seg(BezierSeg &seg) const;
+
+  static bool compute_seg(int rtype0, float t0, const LVecBase4f &v0,
+			  int rtype1, float t1, const LVecBase4f &v1,
+			  int rtype2, float t2, const LVecBase4f &v2,
+			  int rtype3, float t3, const LVecBase4f &v3,
+			  const LMatrix4f &B,
+			  const LMatrix4f &Bi,
+			  LMatrix4f &G);
+
+  LVecBase4f Bx, By, Bz, Bw;
+  bool rational;
+
+
+// TypedWriteable stuff
+public:
+  static void register_with_read_factory();
+
+protected:
+  static TypedWriteable *make_CubicCurveseg(const FactoryParams &params);
+  virtual void write_datagram(BamWriter *manager, Datagram &me);  
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParametricCurve::init_type();
+    register_type(_type_handle, "CubicCurveseg",
+                  ParametricCurve::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+// This function is used internally to build the NURBS basis matrix
+// based on a given knot sequence.
+void compute_nurbs_basis(int order,
+                         const float knots_in[],
+                         LMatrix4f &basis);
+
+
+#endif

+ 0 - 1834
panda/src/parametrics/curve.cxx

@@ -1,1834 +0,0 @@
-// Filename: curve.C
-// Created by:  drose (14Mar97)
-//
-////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-//
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-
-#include "curve.h"
-#include "config_parametrics.h"
-#include "hermiteCurve.h"
-#include "nurbsCurve.h"
-#include "curveDrawer.h"
-
-#include <datagram.h>
-#include <datagramIterator.h>
-#include <bamWriter.h>
-#include <bamReader.h>
-
-////////////////////////////////////////////////////////////////////
-// Statics
-////////////////////////////////////////////////////////////////////
-
-TypeHandle ParametricCurve::_type_handle;
-TypeHandle PiecewiseCurve::_type_handle;
-TypeHandle CubicCurveseg::_type_handle;
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::Constructor
-//       Access: Public
-//  Description: This is a virtual base class.  Don't try to construct
-//               one from Scheme.
-////////////////////////////////////////////////////////////////////
-ParametricCurve::
-ParametricCurve() {
-  _curve_type = PCT_NONE;
-  _num_dimensions = 3;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::Destructor
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-ParametricCurve::
-~ParametricCurve() {
-  // We must disable each of the drawers that were registered to us,
-  // so they don't try to access our pointers any more.
-  DrawerList::iterator d;
-
-  for (d = _drawers.begin();
-       d != _drawers.end();
-       ++d) {
-    (*d)->disable(this);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::is_valid
-//       Access: Public, Scheme, Virtual
-//  Description: Returns true if the curve is defined.  This base
-//               class function always returns true; derived classes
-//               might override this to sometimes return false.
-////////////////////////////////////////////////////////////////////
-bool ParametricCurve::
-is_valid() const {
-  return true;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::get_max_t
-//       Access: Public, Scheme, Virtual
-//  Description: Returns the upper bound of t for the entire curve.
-//               The curve is defined in the range 0.0 <= t <=
-//               get_max_t().  This base class function always returns
-//               1.0; derived classes might override this to return
-//               something else.
-////////////////////////////////////////////////////////////////////
-double ParametricCurve::
-get_max_t() const {
-  return 1.0;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::set_curve_type
-//       Access: Public, Scheme
-//  Description: Sets the flag indicating the use to which the curve
-//               is intended to be put.  This flag is optional and
-//               only serves to provide a hint to the egg reader and
-//               writer code; it has no effect on the curve's
-//               behavior.
-//
-//               Setting the curve type also sets the num_dimensions
-//               to 3 or 1 according to the type.
-//
-//               THis flag may have one of the values PCT_XYZ,
-//               PCT_HPR, or PCT_T.
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-set_curve_type(int type) {
-  _curve_type = type;
-  switch (_curve_type) {
-  case PCT_XYZ:
-  case PCT_HPR:
-  case PCT_NONE:
-    _num_dimensions = 3;
-    break;
-
-  case PCT_T:
-    _num_dimensions = 1;
-    break;
-
-  default:
-    assert(0);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::get_curve_type
-//       Access: Public, Scheme
-//  Description: Returns the flag indicating the use to which the curve
-//               is intended to be put.
-////////////////////////////////////////////////////////////////////
-int ParametricCurve::
-get_curve_type() const {
-  return _curve_type;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::set_num_dimensions
-//       Access: Public, Scheme
-//  Description: Specifies the number of significant dimensions in the
-//               curve's vertices.  This should be one of 1, 2, or 3.
-//               Normally, XYZ and HPR curves have three dimensions;
-//               time curves should always have one dimension.  This
-//               only serves as a hint to the mopath editor, and also
-//               controls how the curve is written out.
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-set_num_dimensions(int num) {
-  _num_dimensions = num;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::get_num_dimensions
-//       Access: Public, Scheme
-//  Description: Returns the number of significant dimensions in the
-//               curve's vertices, as set by a previous call to
-//               set_num_dimensions().  This is only a hint as to how
-//               the curve is intended to be used; the actual number
-//               of dimensions of any curve is always three.
-////////////////////////////////////////////////////////////////////
-int ParametricCurve::
-get_num_dimensions() const {
-  return _num_dimensions;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::calc_length
-//       Access: Public, Scheme
-//  Description: Approximates the length of the entire curve to within
-//               a few decimal places.
-////////////////////////////////////////////////////////////////////
-float ParametricCurve::
-calc_length() const {
-  return calc_length(0.0, get_max_t());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::calc_length
-//       Access: Public, Scheme
-//  Description: Approximates the length of the curve segment from
-//               parametric time from to time to.
-////////////////////////////////////////////////////////////////////
-float ParametricCurve::
-calc_length(double from, double to) const {
-  double t1, t2;
-  LPoint3f p1, p2;
-
-  // Normally we expect from < to.  If they came in backwards, reverse
-  // them.
-  if (to < from) {
-    double temp = to;
-    to = from;
-    from = temp;
-  }
-
-  // Start with a segment for each unit of t.
-  int num_segs = (int)floor(to - from + 1);
-  t2 = from;
-  get_point(t2, p2);
-  float net = 0.0;
-
-  for (int i = 1; i <= num_segs; i++) {
-    t1 = t2;
-    p1 = p2;
-
-    t2 = (to - from) * (double)i / (double)num_segs + from;
-    get_point(t2, p2);
-
-    net += r_calc_length(t1, t2, p1, p2, (p1 - p2).length());
-  }
-  return net;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::compute_t
-//       Access: Public, Scheme
-//  Description: The inverse of calc_length: given a distance offset
-//               along the curve from an arbitrary point, compute the
-//               new value of t.  This function is quite expensive.
-////////////////////////////////////////////////////////////////////
-double ParametricCurve::
-compute_t(double start_t, double length_offset, double guess,
-          double threshold) const {
-  if (length_offset > 0.0) {
-    // If the length_offset is positive, we are looking forward.
-    // Enforce that the guess is greater than the start.
-    if (guess < start_t) {
-      guess = start_t + (start_t - guess);
-    } else if (guess == start_t) {
-      guess = start_t + 1.0;
-    }
-
-  } else if (length_offset < 0.0) {
-    // If the length offset is negative, we are looking backward.
-    // Enforce that the guess is less than the start.
-    if (guess > start_t) {
-      guess = start_t - (guess - start_t);
-    } else if (guess == start_t) {
-      guess = start_t - 1.0;
-    }
-
-  } else {
-    // If the length_offset is zero, we're just being silly.
-    return start_t;
-  }
-
-  // First, compute the length of the curve segment from start_t to
-  // guess.
-  double actual_length = calc_length(start_t, guess);
-  double max_t = get_max_t();
-  bool clamped = false;
-
-  // Are we close enough yet?
-  cerr << "Got " << actual_length << " wanted " << length_offset << "\n";
-  while (fabs(actual_length - length_offset) > threshold) {
-    // Not close enough: use the computed length to calculate where
-    // the t_offset should be if the curve were evenly distributed
-    // across its entire range.
-    guess = (guess - start_t) * length_offset / actual_length + start_t;
-
-    // Clamp it to the end of the curve.
-    if (guess > max_t) {
-      if (clamped) {
-        return max_t;
-      }
-      clamped = true;
-      guess = max_t;
-    } else {
-      clamped = false;
-    }
-
-    actual_length = calc_length(start_t, guess);
-    cerr << "Got " << actual_length << " wanted " << length_offset << "\n";
-  }
-  cerr << "The answer is " << guess << "\n\n";
-
-  return guess;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::convert_to_hermite
-//       Access: Public, Scheme
-//  Description: Stores an equivalent curve representation in the
-//               indicated Hermite curve, if possible.  Returns true
-//               if successful, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool ParametricCurve::
-convert_to_hermite(HermiteCurve &hc) const {
-  BezierSegs bz_segs;
-  if (!GetBezierSegs(bz_segs)) {
-    return false;
-  }
-
-  // Now convert the Bezier segments to a Hermite.  Normally, the
-  // Beziers will match up head-to-tail, but if they don't, that's a
-  // cut.
-  hc.remove_all_cvs();
-  hc.set_curve_type(_curve_type);
-
-  int i, n;
-  if (!bz_segs.empty()) {
-    double scale_in = 0.0;
-    double scale_out = bz_segs[0]._t;
-    n = hc.append_cv(HC_SMOOTH, bz_segs[0]._v[0]);
-    hc.set_cv_out(n, 3.0 * (bz_segs[0]._v[1] - bz_segs[0]._v[0]) / scale_out);
-
-    for (i = 0; i < (int)bz_segs.size()-1; i++) {
-      scale_in = scale_out;
-      scale_out = bz_segs[i+1]._t - bz_segs[i]._t;
-
-      if (!bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001)) {
-        // Oops, we have a cut.
-        hc.set_cv_type(n, HC_CUT);
-      }
-
-      n = hc.append_cv(HC_FREE, bz_segs[i+1]._v[0]);
-      hc.set_cv_in(n, 3.0 * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in);
-      hc.set_cv_tstart(n, bz_segs[i]._t);
-
-      hc.set_cv_out(n, 3.0 * (bz_segs[i+1]._v[1] - bz_segs[i+1]._v[0]) / scale_out);
-    }
-
-    // Now the last CV.
-    scale_in = scale_out;
-    i = bz_segs.size()-1;
-    n = hc.append_cv(HC_SMOOTH, bz_segs[i]._v[3]);
-    hc.set_cv_in(n, 3.0 * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in);
-    hc.set_cv_tstart(n, bz_segs[i]._t);
-  }
-
-  // Finally, go through and figure out which CV's are smooth or G1.
-  int num_cvs = hc.get_num_cvs();
-  for (n = 1; n < num_cvs-1; n++) {
-    if (hc.get_cv_type(n)!=HC_CUT) {
-      LVector3f in = hc.get_cv_in(n);
-      LVector3f out = hc.get_cv_out(n);
-      
-      if (in.almost_equal(out, 0.0001)) {
-        hc.set_cv_type(n, HC_SMOOTH);
-      } else {
-        in.normalize();
-        out.normalize();
-        if (in.almost_equal(out, 0.0001)) {
-          hc.set_cv_type(n, HC_G1);
-        }
-      }
-    }
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::convert_to_nurbs
-//       Access: Public
-//  Description: Stores in the indicated NurbsCurve a NURBS
-//               representation of an equivalent curve.  Returns true
-//               if successful, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool ParametricCurve::
-convert_to_nurbs(NurbsCurve &nc) const {
-  BezierSegs bz_segs;
-  if (!GetBezierSegs(bz_segs)) {
-    return false;
-  }
-
-  nc.remove_all_cvs();
-  nc.set_curve_type(_curve_type);
-  nc.set_order(4);
-  if (!bz_segs.empty()) {
-    int i;
-    for (i = 0; i < (int)bz_segs.size(); i++) {
-      nc.append_cv(bz_segs[i]._v[0]);
-      nc.append_cv(bz_segs[i]._v[1]);
-      nc.append_cv(bz_segs[i]._v[2]);
-      if (i == (int)bz_segs.size()-1 ||
-          !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001)) {
-        nc.append_cv(bz_segs[i]._v[3]);
-      }
-    }
-
-    double t;
-    int ki = 4;
-    nc.set_knot(0, 0.0);
-    nc.set_knot(1, 0.0);
-    nc.set_knot(2, 0.0);
-    nc.set_knot(3, 0.0);
-
-    for (i = 0; i < (int)bz_segs.size(); i++) {
-      t = bz_segs[i]._t;
-
-      nc.set_knot(ki, t);
-      nc.set_knot(ki+1, t);
-      nc.set_knot(ki+2, t);
-      ki += 3;
-      if (i == (int)bz_segs.size()-1 ||
-          !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001)) {
-        nc.set_knot(ki, t);
-        ki++;
-      }
-    }
-  }
-
-  nc.recompute();
-
-  return nc.is_valid();
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::ascii_draw
-//       Access: Public, Scheme
-//  Description: Draws a cheesy representation of the curve in the XZ
-//               plane using ASCII characters.  This is entirely so I
-//               can debug the curve from home.
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-ascii_draw() const {
-  // First, get an approximate bounds on the curve.
-
-  float minx, minz, maxx, maxz;
-
-  LVecBase3f p;
-  get_point(0.0, p);
-  minx = maxx = p[0];
-  minz = maxz = p[2];
-
-  double t;
-
-  for (t = 0.0; t <= get_max_t(); t += 0.0625) {
-    get_point(t, p);
-    minx = min(minx, p[0]);
-    maxx = max(maxx, p[0]);
-    minz = min(minz, p[2]);
-    maxz = max(maxz, p[2]);
-  }
-
-  // Set up the 2-d character buffer we will draw into.
-  static const int rows = 40;
-  static const int cols = 78;
-
-  char text[rows][cols+1];
-
-  int r, c;
-  for (r = 0; r<rows; r++) {
-    memset(text[r], ' ', cols);
-    text[r][cols] = '\0';
-  }
-
-  double xscale = cols / max(maxx - minx, (float)1.0);
-  double zscale = rows / max(maxz - minz, (float)1.0);
-
-
-  // Now draw into the buffer.
-
-  for (t = 0.0; t <= get_max_t(); t += 0.0625) {
-    if (get_point(t, p)) {
-      c = (int)((p[0] - minx) * xscale);
-      r = (int)((p[2] - minz) * zscale);
-
-      if (r>=0 && r<rows && c>=0 && c<cols) {
-        int digit = ((int)floor(t))%10;
-        text[rows-1-r][c] = digit + '0';
-      }
-    }
-  }
-
-  // And output the buffer.
-
-  for (r = 0; r<rows; r++) {
-    cout << text[r] << "\n";
-  }
-  cout << "\n" << flush;
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::register_drawer
-//       Access: Public
-//  Description: Registers a Drawer with this curve that will
-//               automatically be updated whenever the curve is
-//               modified, so that the visible representation of the
-//               curve is kept up to date.  This is called
-//               automatically by the ParametricCurveDrawer
-//               constructor.
-//
-//               Any number of Drawers may be registered with a
-//               particular curve.
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-register_drawer(ParametricCurveDrawer *drawer) {
-  _drawers.push_back(drawer);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::unregister_drawer
-//       Access: Public
-//  Description: Removes a previously registered drawer from the list
-//               of automatically-refreshed drawers.  This is called
-//               automatically by the ParametricCurveDrawer
-//               destructor.
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-unregister_drawer(ParametricCurveDrawer *drawer) {
-  _drawers.remove(drawer);
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::invalidate
-//       Access: Protected
-//  Description: Called from a base class to mark a section of the
-//               curve that has been modified and must be redrawn or
-//               recomputed in some way.
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-invalidate(double t1, double t2) {
-  if (t1 <= t2) {
-    DrawerList::iterator n;
-    for (n = _drawers.begin();
-         n != _drawers.end();
-         ++n) {
-      (*n)->recompute(max(t1, 0.0), min(t2, get_max_t()), this);
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::invalidate_all
-//       Access: Protected
-//  Description: Called from a base class to indicate that the curve
-//               has changed in some substantial way and must be
-//               entirely redrawn.
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-invalidate_all() {
-  DrawerList::iterator n;
-  for (n = _drawers.begin();
-       n != _drawers.end();
-       ++n) {
-    (*n)->draw();
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::r_calc_length
-//       Access: Public, Scheme
-//  Description: The recursive implementation of calc_length.  This
-//               function calculates the length of a segment of the
-//               curve between points t1 and t2, which presumably
-//               evaluate to the endpoints p1 and p2, and the segment
-//               has the length seglength.
-////////////////////////////////////////////////////////////////////
-float ParametricCurve::
-r_calc_length(double t1, double t2, const LPoint3f &p1, const LPoint3f &p2,
-              float seglength) const {
-  static const float length_tolerance = 0.0000001;
-  static const double t_tolerance = 0.000001;
-
-  if (t2 - t1 < t_tolerance) {
-    // Stop recursing--we've just walked off the limit for
-    // representing smaller values of t.
-    return 0.0;
-  } else {
-    double tmid;
-    LPoint3f pmid;
-    float left, right;
-
-    // Calculate the point on the curve midway between the two
-    // endpoints.
-    tmid = (t1+t2)/2.0;
-    get_point(tmid, pmid);
-
-    // Did we increase the length of the segment measurably?
-    left = (p1 - pmid).length();
-    right = (pmid - p2).length();
-
-    if ((left + right) - seglength < length_tolerance) {
-      // No.  We're done.
-      return seglength;
-    } else {
-      // Yes.  Keep going.
-      return r_calc_length(t1, tmid, p1, pmid, left) +
-             r_calc_length(tmid, t2, pmid, p2, right);
-    }
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::write_datagram
-//       Access: Protected, Virtual
-//  Description: Function to write the important information in
-//               the particular object to a Datagram
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-write_datagram(BamWriter *manager, Datagram &me) {
-  NamedNode::write_datagram(manager, me);
-
-  me.add_int8(_curve_type);
-  me.add_int8(_num_dimensions);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurve::fillin
-//       Access: Protected
-//  Description: Function that reads out of the datagram (or asks
-//               manager to read) all of the data that is needed to
-//               re-create this object and stores it in the appropiate
-//               place
-////////////////////////////////////////////////////////////////////
-void ParametricCurve::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  NamedNode::fillin(scan, manager);
-
-  _curve_type = scan.get_int8();
-  _num_dimensions = scan.get_int8();
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-PiecewiseCurve::
-PiecewiseCurve() {
-  _last_ti = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::Destructor
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-PiecewiseCurve::
-~PiecewiseCurve() {
-  remove_all_curvesegs();
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::is_valid
-//       Access: Public, Scheme, Virtual
-//  Description: Returns true if the curve is defined.  In the case of
-//               a PiecewiseCurve, this means we have at least one
-//               segment.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-is_valid() const {
-  return !_segs.empty();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_max_t
-//       Access: Public, Scheme, Virtual
-//  Description: Returns the upper bound of t for the entire curve.
-//               The curve is defined in the range 0.0 <= t <=
-//               get_max_t().
-////////////////////////////////////////////////////////////////////
-double PiecewiseCurve::
-get_max_t() const {
-  return _segs.empty() ? 0.0 : _segs.back()._tend;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_point
-//       Access: Public, Scheme, Virtual
-//  Description: Returns the point of the curve at a given parametric
-//               point t.  Returns true if t is in the valid range 0.0
-//               <= t <= get_max_t(); if t is outside this range, sets
-//               point to the value of the curve at the beginning or
-//               end (whichever is nearer) and returns false.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-get_point(double t, LVecBase3f &point) const {
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  // We use | instead of || so we won't short-circuit this calculation.
-  return result | curve->get_point(t, point);
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_tangent
-//       Access: Public, Scheme, Virtual
-//  Description: Returns the tangent of the curve at a given parametric
-//               point t.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-get_tangent(double t, LVecBase3f &tangent) const {
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  // We use | instead of || so we won't short-circuit this calculation.
-  return result | curve->get_tangent(t, tangent);
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_2ndtangent
-//       Access: Public, Scheme, Virtual
-//  Description: Returns the tangent of the first derivative of the
-//               curve at the point t.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-get_2ndtangent(double t, LVecBase3f &tangent2) const {
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  // We use | instead of || so we won't short-circuit this calculation.
-  return result | curve->get_2ndtangent(t, tangent2);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::adjust_point
-//       Access: Public, Scheme
-//  Description: Recomputes the curve such that it passes through the
-//               point (px, py, pz) at time t, but keeps the same
-//               tangent value at that point.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-adjust_point(double t,
-             float px, float py, float pz) {
-  if (parametrics_cat.is_debug()) {
-    parametrics_cat.debug()
-      << "Adjusting point at " << t << " to " << px << " " << py << " "
-      << pz << "\n";
-  }
-      
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  if (!result) {
-    cerr << "No curve segment at t = " << t << "\n";
-    return false;
-  }
-
-  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f(),
-                   RT_POINT, t, LVecBase4f(px, py, pz, 1.0),
-                   RT_TANGENT | RT_KEEP_ORIG, t, LVecBase4f(),
-                   RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f());
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::adjust_tangent
-//       Access: Public, Scheme
-//  Description: Recomputes the curve such that it has the tangent
-//               (tx, ty, tz) at time t, but keeps the same position
-//               at the point.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-adjust_tangent(double t,
-               float tx, float ty, float tz) {
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  if (!result) {
-    cerr << "No curve segment at t = " << t << "\n";
-    return false;
-  }
-
-  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f(),
-                   RT_POINT | RT_KEEP_ORIG, t, LVecBase4f(),
-                   RT_TANGENT, t, LVecBase4f(tx, ty, tz, 0.0),
-                   RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f());
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::adjust_pt
-//       Access: Public, Scheme
-//  Description: Recomputes the curve such that it passes through the
-//               point (px, py, pz) with the tangent (tx, ty, tz).
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-adjust_pt(double t,
-          float px, float py, float pz,
-          float tx, float ty, float tz) {
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  if (!result) {
-    cerr << "No curve segment at t = " << t << "\n";
-    return false;
-  }
-
-  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f(),
-                   RT_POINT, t, LVecBase4f(px, py, pz, 1.0),
-                   RT_TANGENT, t, LVecBase4f(tx, ty, tz, 0.0),
-                   RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f());
-  return true;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_pt
-//       Access: Public, Scheme, Virtual
-//  Description: Simultaneously returns the point and tangent of the
-//               curve at a given parametric point t.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-get_pt(double t, LVecBase3f &point, LVecBase3f &tangent) const {
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  // We use | instead of || so we won't short-circuit this calculation.
-  return result | curve->get_pt(t, point, tangent);
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_num_segs
-//       Access: Public
-//  Description: Returns the number of curve segments that make up the
-//               Piecewise curve.
-////////////////////////////////////////////////////////////////////
-int PiecewiseCurve::
-get_num_segs() const {
-  return _segs.size();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_curveseg
-//       Access: Public
-//  Description: Returns the curve segment corresponding to the given
-//               index.
-////////////////////////////////////////////////////////////////////
-ParametricCurve *PiecewiseCurve::
-get_curveseg(int ti) {
-  assert(ti >= 0 && ti < (int)_segs.size());
-  return _segs[ti]._curve;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::insert_curveseg
-//       Access: Public
-//  Description: Inserts a new curve segment at the indicated index.
-//               The curve segment must have been allocated via
-//               new; it will be freed using delete when it is removed
-//               or the PiecewiseCurve destructs.
-//
-//               If the curve segment is not inserted at the end, its
-//               tlength is subtracted from that of the following
-//               segment, so that the overall length of the curve is
-//               not changed.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-insert_curveseg(int ti, ParametricCurve *seg, double tlength) {
-  if (ti < 0 || ti > (int)_segs.size()) {
-    return false;
-  }
-
-  if (ti == (int)_segs.size()) {
-    _segs.push_back(Curveseg(seg, get_max_t() + tlength));
-
-  } else if (ti==0) {
-    _segs.insert(_segs.begin(),
-                 Curveseg(seg, tlength));
-
-  } else {
-    _segs.insert(_segs.begin() + ti,
-                 Curveseg(seg, _segs[ti-1]._tend + tlength));
-  }
-
-  return true;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::remove_curveseg
-//       Access: Public
-//  Description: Removes the given curve segment from the curve and
-//               frees it.  Returns true if the segment was defined,
-//               false otherwise.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-remove_curveseg(int ti) {
-  if (ti < 0 || ti >= (int)_segs.size()) {
-    return false;
-  }
-
-  double tlength = get_tlength(ti);
-  _segs.erase(_segs.begin() + ti);
-
-  // Now update the _tend figures for everything after the one we
-  // removed.
-  while (ti < (int)_segs.size()) {
-    _segs[ti]._tend -= tlength;
-    ti++;
-  }
-
-  _last_ti = 0;
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::remove_all_curvesegs
-//       Access: Public
-//  Description: Removes all curve segments from the curve.
-////////////////////////////////////////////////////////////////////
-void PiecewiseCurve::
-remove_all_curvesegs() {
-  _segs.erase(_segs.begin(), _segs.end());
-  _last_ti = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_tlength
-//       Access: Public
-//  Description: Returns the parametric length of the given segment of
-//               the curve.
-////////////////////////////////////////////////////////////////////
-double PiecewiseCurve::
-get_tlength(int ti) const {
-  assert(ti >= 0 && ti < (int)_segs.size());
-  return (ti==0) ? _segs[ti]._tend : _segs[ti]._tend - _segs[ti-1]._tend;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_tstart
-//       Access: Public
-//  Description: Returns the parametric start of the given segment of
-//               the curve.
-////////////////////////////////////////////////////////////////////
-double PiecewiseCurve::
-get_tstart(int ti) const {
-  assert(ti >= 0 && ti <= (int)_segs.size());
-  return (ti==0) ? 0.0 : _segs[ti-1]._tend;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::get_tend
-//       Access: Public
-//  Description: Returns the parametric end of the given segment of
-//               the curve.
-////////////////////////////////////////////////////////////////////
-double PiecewiseCurve::
-get_tend(int ti) const {
-  assert(ti >= 0 && ti < (int)_segs.size());
-  return _segs[ti]._tend;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::set_tlength
-//       Access: Public
-//  Description: Sets the parametric length of the given segment of
-//               the curve.  The length of the following segment is
-//               lengthened by the corresponding amount to keep the
-//               overall length of the curve the same.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-set_tlength(int ti, double tlength) {
-  if (ti < 0 || ti >= (int)_segs.size()) {
-    return false;
-  }
-
-  _segs[ti]._tend += tlength - get_tlength(ti);
-  return true;
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::make_nurbs
-//       Access: Public
-//  Description: Defines the curve as a general NURBS curve.  The
-//               order is the degree plus one and must be 1, 2, 3, or
-//               4; cvs is an array of num_cvs points each with a
-//               homogeneous coordinate; knots is an array of
-//               num_cvs+order knot values.
-//
-//               This creates the individual curve segments and sets
-//               up the basis matrices, but does not store the CV's or
-//               knot values so the curve shape is not later
-//               modifiable.
-////////////////////////////////////////////////////////////////////
-void PiecewiseCurve::
-make_nurbs(int order, int num_cvs,
-           const double knots[], const LVecBase4f cvs[]) {
-  remove_all_curvesegs();
-
-  for (int i=0; i<num_cvs - order + 1; i++) {
-    if (knots[i+order] > knots[i+order-1]) {
-      int ti = get_num_segs();
-      bool result =
-        insert_curveseg(ti, new CubicCurveseg(order, knots+i, cvs+i),
-                        knots[i+order] - knots[i+order-1]);
-      assert(result);
-    }
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::GetBezierSegs
-//       Access: Public, Virtual
-//  Description: Fills up the indicated vector with a list of
-//               BezierSeg structs that describe the curve.  This
-//               assumes the curve is a PiecewiseCurve of
-//               CubicCurvesegs.  Returns true if successful, false
-//               otherwise.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-GetBezierSegs(BezierSegs &bz_segs) const {
-  bz_segs.erase(bz_segs.begin(), bz_segs.end());
-  int i;
-  BezierSeg seg;
-  for (i = 0; i < (int)_segs.size(); i++) {
-    if (!_segs[i]._curve->GetBezierSeg(seg)) {
-      return false;
-    }
-    seg._t = _segs[i]._tend;
-    bz_segs.push_back(seg);
-  }
-
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::rebuild_curveseg
-//       Access: Public, Virtual
-//  Description: Rebuilds the current curve segment (as selected by
-//               the most recent call to find_curve()) according to
-//               the specified properties (see
-//               CubicCurveseg::compute_seg).  Returns true if
-//               possible, false if something goes horribly wrong.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-rebuild_curveseg(int, double, const LVecBase4f &,
-                 int, double, const LVecBase4f &,
-                 int, double, const LVecBase4f &,
-                 int, double, const LVecBase4f &) {
-  cerr << "rebuild_curveseg not implemented for this curve type.\n";
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::find_curve
-//       Access: Protected
-//  Description: Finds the curve corresponding to the given value of
-//               t.  If t is inside the curve's defined range, sets
-//               curve to the appropriate segment, translates t to
-//               [0,1] to index into the segment's coordinate system,
-//               and returns true.  If t is outside the curve's
-//               defined range, sets curve to the nearest segment and
-//               t to the nearest point on this segment, and returns
-//               false.
-////////////////////////////////////////////////////////////////////
-bool PiecewiseCurve::
-find_curve(const ParametricCurve *&curve, double &t) const {
-  // Check the index computed by the last call to find_curve().  If
-  // it's still a reasonable starting value, start searching from
-  // there.  This way, we take advantage of locality of reference: the
-  // search is trivial it is the same segment as last time, or the
-  // next segment after the last one.
-  if (_last_ti>0 && _segs[_last_ti-1]._tend>=t) {
-    // However, if the new t value precedes that of last time, we'll
-    // have to start over.
-
-    // We do some messy casting so we can get away with assigning a
-    // value to a member within a const function.  This assignment
-    // doesn't really count as a const violation since we're just
-    // updating a cached value, not changing any real data of the
-    // class.
-    ((PiecewiseCurve *)this)->_last_ti = 0;
-  }
-
-  int ti;
-  for (ti = _last_ti; ti < (int)_segs.size(); ti++) {
-    if (_segs[ti]._tend+0.00001 > t) {
-      break;
-    }
-  }
-
-  if (ti < (int)_segs.size()) {
-    // Adjust t to the range [0,1).
-    if (ti > 0) {
-      t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend);
-    } else {
-      t /= _segs[0]._tend;
-    }
-  }
-
-  if (t < 0) {
-    // Oops.
-    curve = _segs[0]._curve;
-    t = 0.0;
-    return false;
-  }
-
-  if (ti >= (int)_segs.size() || !_segs[ti]._curve->is_valid()) {
-    assert(ti <= (int)_segs.size());
-
-    // If we're out of bounds, or the curve is undefined, we're probably
-    // screwed.  There's one exception: if we were right on a border between
-    // curves, try the curve before.
-
-    if (ti > 0 && t < _segs[ti-1]._tend+0.0001) {
-      ti--;
-      t = 1.0;
-    }
-
-    if (ti >= (int)_segs.size()) {
-      if (_segs.empty()) {
-        curve = NULL;
-        t = 0.0;
-        return false;
-      } else {
-        curve = _segs.back()._curve;
-        t = 1.0;
-        return false;
-      }
-    } else if (!_segs[ti]._curve->is_valid()) {
-      curve = _segs[ti]._curve;
-      return false;
-    }
-  }
-
-  // Again, some messy casting so we can get away with updating the
-  // cached index value for next time.
-  ((PiecewiseCurve *)this)->_last_ti = ti;
-
-  // Now scale t back into the curve's own valid range.
-  t *= _segs[ti]._curve->get_max_t();
-  curve = _segs[ti]._curve;
-  return true;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::current_seg_range
-//       Access: Protected
-//  Description: Returns a number in the range [0,1], representing the
-//               conversion of t into the current segment's coordinate
-//               system (the segment last returned by find_curve).
-//               This operation is already performed automatically on
-//               the t passed into find_seg; this function is useful
-//               only to adjust a different value into the same range.
-//
-//               It is an error to call this function if find_curve()
-//               has not yet been called, or if find_curve() returned
-//               false from its previous call.
-////////////////////////////////////////////////////////////////////
-double PiecewiseCurve::
-current_seg_range(double t) const {
-  int ti = _last_ti;
-
-  assert(ti < (int)_segs.size());
-
-  // Adjust t to the range [0,1).
-  if (ti > 0) {
-    t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend);
-  } else {
-    t /= _segs[0]._tend;
-  }
-
-  return t;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::write_datagram
-//       Access: Protected, Virtual
-//  Description: Function to write the important information in
-//               the particular object to a Datagram
-////////////////////////////////////////////////////////////////////
-void PiecewiseCurve::
-write_datagram(BamWriter *manager, Datagram &me) {
-  ParametricCurve::write_datagram(manager, me);
-
-  me.add_uint32(_segs.size());
-  size_t i;
-  for (i = 0; i < _segs.size(); i++) {
-    const Curveseg &seg = _segs[i];
-    manager->write_pointer(me, seg._curve);
-    me.add_float64(seg._tend);
-  }
-
-  _last_ti = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::fillin
-//       Access: Protected
-//  Description: Function that reads out of the datagram (or asks
-//               manager to read) all of the data that is needed to
-//               re-create this object and stores it in the appropiate
-//               place
-////////////////////////////////////////////////////////////////////
-void PiecewiseCurve::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  ParametricCurve::fillin(scan, manager);
-
-  size_t num_segs = scan.get_uint32();
-  _segs.reserve(num_segs);
-  size_t i;
-  for (i = 0; i < num_segs; i++) {
-    Curveseg seg;
-    manager->read_pointer(scan, this);
-    seg._curve = (ParametricCurve *)NULL;
-    seg._tend = scan.get_float64();
-    _segs.push_back(seg);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PiecewiseCurve::complete_pointers
-//       Access: Protected, Virtual
-//  Description: Takes in a vector of pointes to TypedWriteable
-//               objects that correspond to all the requests for 
-//               pointers that this object made to BamReader.
-////////////////////////////////////////////////////////////////////
-int PiecewiseCurve::
-complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
-  int used = ParametricCurve::complete_pointers(plist, manager);
-
-  nassertr(used + _segs.size() <= plist.size(), used);
-
-  size_t i;
-  for (i = 0; i < _segs.size(); i++) {
-    DCAST_INTO_R(_segs[i]._curve, plist[used + i], used);
-  }
-  
-  return used + _segs.size();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-CubicCurveseg::
-CubicCurveseg() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::Constructor
-//       Access: Public
-//  Description: Creates the curveseg given the four basis vectors
-//               (the columns of the matrix) explicitly.
-////////////////////////////////////////////////////////////////////
-CubicCurveseg::
-CubicCurveseg(const LMatrix4f &basis) {
-  Bx = basis.get_col(0);
-  By = basis.get_col(1);
-  Bz = basis.get_col(2);
-  Bw = basis.get_col(3);
-  rational = true;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::Constructor
-//       Access: Public
-//  Description: Creates the curveseg as a Bezier segment.
-////////////////////////////////////////////////////////////////////
-CubicCurveseg::
-CubicCurveseg(const BezierSeg &seg) {
-  bezier_basis(seg);
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::Constructor
-//       Access: Public
-//  Description: Creates the curveseg as a NURBS segment.  See
-//               nurbs_basis for a description of the parameters.
-////////////////////////////////////////////////////////////////////
-CubicCurveseg::
-CubicCurveseg(int order, const double knots[], const LVecBase4f cvs[]) {
-  nurbs_basis(order, knots, cvs);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::Destructor
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-CubicCurveseg::
-~CubicCurveseg() {
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::get_point
-//       Access: Public, Scheme, Virtual
-//  Description: Computes the surface point at a given parametric
-//               point t.
-////////////////////////////////////////////////////////////////////
-bool CubicCurveseg::
-get_point(double t, LVecBase3f &point) const {
-  evaluate_point(LVecBase4f(t*t*t, t*t, t, 1.0), point);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::get_tangent
-//       Access: Public, Scheme, Virtual
-//  Description: Computes the surface tangent at a given parametric
-//               point t.
-////////////////////////////////////////////////////////////////////
-bool CubicCurveseg::
-get_tangent(double t, LVecBase3f &tangent) const {
-  evaluate_vector(LVecBase4f(3.0*t*t, 2.0*t, 1.0, 0.0), tangent);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::get_pt
-//       Access: Public, Scheme, Virtual
-//  Description: Simultaneously computes the point and the tangent at
-//               the given parametric point.
-////////////////////////////////////////////////////////////////////
-bool CubicCurveseg::
-get_pt(double t, LVecBase3f &point, LVecBase3f &tangent) const {
-  evaluate_point(LVecBase4f(t*t*t, t*t, t, 1.0), point);
-  evaluate_vector(LVecBase4f(3.0*t*t, 2.0*t, 1.0, 0.0), tangent);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::get_2ndtangent
-//       Access: Public, Scheme, Virtual
-//  Description: Computes the surface 2nd-order tangent at a given
-//               parametric point t.
-////////////////////////////////////////////////////////////////////
-bool CubicCurveseg::
-get_2ndtangent(double t, LVecBase3f &tangent2) const {
-  evaluate_vector(LVecBase4f(6.0*t, 2.0, 0.0, 0.0), tangent2);
-  return true;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::hermite_basis
-//       Access: Public
-//  Description: Defines the curve segment as a Hermite.  This only
-//               sets up the basis vectors, so the curve will be
-//               computed correctly; it does not retain the CV's.
-////////////////////////////////////////////////////////////////////
-void CubicCurveseg::
-hermite_basis(const HermiteCurveCV &cv0,
-              const HermiteCurveCV &cv1,
-              double tlength) {
-  static LMatrix4f 
-    Mh(2, -3, 0, 1,
-       -2, 3, 0, 0,
-       1, -2, 1, 0,
-       1, -1, 0, 0);
-
-  LVecBase4f Gx(cv0._p[0], cv1._p[0],
-            cv0._out[0]*tlength, cv1._in[0]*tlength);
-  LVecBase4f Gy(cv0._p[1], cv1._p[1],
-            cv0._out[1]*tlength, cv1._in[1]*tlength);
-  LVecBase4f Gz(cv0._p[2], cv1._p[2], 
-            cv0._out[2]*tlength, cv1._in[2]*tlength);
-
-  Bx = Gx * Mh;
-  By = Gy * Mh;
-  Bz = Gz * Mh;
-  rational = false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::bezier_basis
-//       Access: Public
-//  Description: Defines the curve segment as a Bezier.  This only
-//               sets up the basis vectors, so the curve will be
-//               computed correctly; it does not retain the CV's.
-////////////////////////////////////////////////////////////////////
-void CubicCurveseg::
-bezier_basis(const BezierSeg &seg) {
-  static LMatrix4f
-    Mb(-1, 3, -3, 1,
-       3, -6, 3, 0,
-       -3, 3, 0, 0,
-       1, 0, 0, 0);
-
-  LVecBase4f Gx(seg._v[0][0], seg._v[1][0], seg._v[2][0], seg._v[3][0]);
-  LVecBase4f Gy(seg._v[0][1], seg._v[1][1], seg._v[2][1], seg._v[3][1]);
-  LVecBase4f Gz(seg._v[0][2], seg._v[1][2], seg._v[2][2], seg._v[3][2]);
-
-  Bx = Gx * Mb;
-  By = Gy * Mb;
-  Bz = Gz * Mb;
-  rational = false;
-}
-
-static LVecBase4f
-nurbs_blending_function(int order, int i, int j,
-                        const double knots[]) {
-  // This is doubly recursive.  Ick.
-  LVecBase4f r;
-
-  if (j==1) {
-    if (i==order-1 && knots[i] < knots[i+1]) {
-      r.set(0.0, 0.0, 0.0, 1.0);
-    } else {
-      r.set(0.0, 0.0, 0.0, 0.0);
-    }
-
-  } else {
-    LVecBase4f bi0 = nurbs_blending_function(order, i, j-1, knots);
-    LVecBase4f bi1 = nurbs_blending_function(order, i+1, j-1, knots);
-
-    float d0 = knots[i+j-1] - knots[i];
-    float d1 = knots[i+j] - knots[i+1];
-
-    // First term.  Division by zero is defined to equal zero.
-    if (d0 != 0.0) {
-      if (d1 != 0.0) {
-        r = bi0 / d0 - bi1 / d1;
-      } else {
-        r = bi0 / d0;
-      }
-
-    } else if (d1 != 0.0) {
-      r = - bi1 / d1;
-
-    } else {
-      r.set(0.0, 0.0, 0.0, 0.0);
-    }
-
-    // scale by t.
-    r[0] = r[1];
-    r[1] = r[2];
-    r[2] = r[3];
-    r[3] = 0.0;
-
-    // Second term.
-    if (d0 != 0.0) {
-      if (d1 != 0.0) {
-        r += bi0 * (- knots[i] / d0) + bi1 * (knots[i+j] / d1);
-      } else {
-        r += bi0 * (- knots[i] / d0);
-      }
-
-    } else if (d1 != 0.0) {
-      r += bi1 * (knots[i+j] / d1);
-    }
-  }
-
-  return r;
-}
-
-void
-compute_nurbs_basis(int order,
-                    const double knots_in[],
-                    LMatrix4f &basis) {
-  int i;
-
-  // Scale the supplied knots to the range 0..1.
-  double knots[8];
-  double mink = knots_in[order-1];
-  double maxk = knots_in[order];
-
-  if (mink==maxk) {
-    // Huh.  What were you thinking?  This is a trivial NURBS.
-    parametrics_cat->warning()
-      << "Trivial NURBS curve specified." << endl;
-    memset((void *)&basis, 0, sizeof(LMatrix4f));
-    return;
-  }
-
-  for (i = 0; i<2*order; i++) {
-    knots[i] = (knots_in[i] - mink) / (maxk-mink);
-  }
-
-
-  LVecBase4f b[4];
-  for (i = 0; i<order; i++) {
-    b[i] = nurbs_blending_function(order, i, order, knots);
-  }
-
-  for (i = 0; i<order; i++) {
-    basis.set_row(i, b[i]);
-  }
-
-  for (i=order; i<4; i++) {
-    basis.set_row(i, LVecBase4f::zero());
-  }
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::nurbs_basis
-//       Access: Public
-//  Description: Defines the curve segment as a NURBS.  Order is one
-//               more than the degree, and must be 1, 2, 3, or 4;
-//               knots is an array of order*2 values, and cvs is an
-//               array of order values.
-////////////////////////////////////////////////////////////////////
-void CubicCurveseg::
-nurbs_basis(int order, const double knots[], const LVecBase4f cvs[]) {
-  assert(order>=1 && order<=4);
-
-  LMatrix4f B;
-  compute_nurbs_basis(order, knots, B);
-
-  // Create a local copy of our CV's, so we can zero out the unused
-  // elements.
-  LVecBase4f c[4];
-  for (int i = 0; i < 4; i++) {
-    c[i] = (i<order) ? cvs[i] : LVecBase4f(0.0, 0.0, 0.0, 0.0);
-  }
-
-  Bx = LVecBase4f(c[0][0], c[1][0], c[2][0], c[3][0]) * B;
-  By = LVecBase4f(c[0][1], c[1][1], c[2][1], c[3][1]) * B;
-  Bz = LVecBase4f(c[0][2], c[1][2], c[2][2], c[3][2]) * B;
-  Bw = LVecBase4f(c[0][3], c[1][3], c[2][3], c[3][3]) * B;
-
-  rational = true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::GetBezierSeg
-//       Access: Public, Virtual
-//  Description: Fills the BezierSeg structure with a description of
-//               the curve segment as a Bezier, if possible, but does
-//               not change the _t member of the structure.  Returns
-//               true if successful, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool CubicCurveseg::
-GetBezierSeg(BezierSeg &seg) const {
-  static LMatrix4f
-    Mbi(0.0, 0.0, 0.0, 1.0,
-        0.0, 0.0, 1.0/3.0, 1.0,
-        0.0, 1.0/3.0, 2.0/3.0, 1.0,
-        1.0, 1.0, 1.0, 1.0);
-
-  LVecBase4f Gx = Bx * Mbi;
-  LVecBase4f Gy = By * Mbi;
-  LVecBase4f Gz = Bz * Mbi;
-
-  if (rational) {
-    LVecBase4f Gw = Bw * Mbi;
-    seg._v[0].set(Gx[0]/Gw[0], Gy[0]/Gw[0], Gz[0]/Gw[0]);
-    seg._v[1].set(Gx[1]/Gw[1], Gy[1]/Gw[1], Gz[1]/Gw[1]);
-    seg._v[2].set(Gx[2]/Gw[2], Gy[2]/Gw[2], Gz[2]/Gw[2]);
-    seg._v[3].set(Gx[3]/Gw[3], Gy[3]/Gw[3], Gz[3]/Gw[3]);
-  } else {
-    seg._v[0].set(Gx[0], Gy[0], Gz[0]);
-    seg._v[1].set(Gx[1], Gy[1], Gz[1]);
-    seg._v[2].set(Gx[2], Gy[2], Gz[2]);
-    seg._v[3].set(Gx[3], Gy[3], Gz[3]);
-  }
-
-  return true;
-}
-
-
-// We need this operator since Performer didn't supply it.
-inline LVecBase4f
-col_mult(const LMatrix4f &M, const LVecBase4f &v) {
-  return LVecBase4f(M(0,0)*v[0] + M(0,1)*v[1] + M(0,2)*v[2] + M(0,3)*v[3],
-                M(1,0)*v[0] + M(1,1)*v[1] + M(1,2)*v[2] + M(1,3)*v[3],
-                M(2,0)*v[0] + M(2,1)*v[1] + M(2,2)*v[2] + M(2,3)*v[3],
-                M(3,0)*v[0] + M(3,1)*v[1] + M(3,2)*v[2] + M(3,3)*v[3]);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: compute_seg_col
-//  Description: Interprets the parameters for a particular column of
-//               compute_seg.  Builds the indicated column of T
-//               and P.
-////////////////////////////////////////////////////////////////////
-static bool
-compute_seg_col(int c,
-                int rtype, double t, const LVecBase4f &v,
-                const LMatrix4f &B,
-                const LMatrix4f &Bi,
-                const LMatrix4f &G,
-                const LMatrix4f &GB,
-                LMatrix4f &T, LMatrix4f &P) {
-  bool keep_orig = ((rtype & RT_KEEP_ORIG) != 0);
-
-  if (parametrics_cat.is_debug()) {
-    parametrics_cat.debug()
-      << "Computing col " << c << " type " << (rtype & RT_BASE_TYPE)
-      << " at " << t << " keep_orig = " << keep_orig
-      << " v = " << v << "\n";
-  }
-
-  switch (rtype & RT_BASE_TYPE) {
-    // RT_point defines the point on the curve at t.  This is the vector
-    // [ t^3 t^2 t^1 t^0 ].
-  case RT_POINT:
-    T.set_col(c, LVecBase4f(t*t*t, t*t, t, 1.0));
-    if (keep_orig) {
-      LVecBase4f vec(t*t*t, t*t, t, 1.0);
-      LVecBase4f ov = col_mult(GB, vec);
-      if (parametrics_cat.is_debug()) {
-	parametrics_cat.debug()
-	  << "orig point = " << ov << "\n";
-      }
-      P.set_col(c, ov);
-    } else {
-      P.set_col(c, v);
-    }
-    break;
-
-    // RT_tangent defines the tangent to the curve at t.  This is
-    // the vector [ 3t^2 2t 1 0 ].
-  case RT_TANGENT:
-    T.set_col(c, LVecBase4f(3.0*t*t, 2.0*t, 1.0, 0.0));
-    if (keep_orig) {
-      LVecBase4f vec(3.0*t*t, 2.0*t, 1.0, 0.0);
-      LVecBase4f ov = col_mult(GB, vec);
-      if (parametrics_cat.is_debug()) {
-	parametrics_cat.debug()
-	  << "Matrix is:\n";
-	GB.write(parametrics_cat.debug(false), 2);
-	parametrics_cat.debug(false)
-	  << "vector is " << vec << "\n"
-	  << "orig tangent = " << ov << "\n";
-      }
-      P.set_col(c, ov);
-    } else {
-      P.set_col(c, v);
-    }
-    break;
-
-    // RT_cv defines the cth control point.  This is the cth column
-    // vector from Bi.
-  case RT_CV:
-    T.set_col(c, Bi.get_col(c));
-    if (keep_orig) {
-      if (parametrics_cat.is_debug()) {
-	parametrics_cat.debug()
-	  << "orig CV = " << G.get_col(c) << "\n";
-      }
-      P.set_col(c, G.get_col(c));
-    } else {
-      P.set_col(c, v);
-    }
-    break;
-
-  default:
-    cerr << "Invalid rebuild type in compute_seg\n";
-    return false;
-  }
-
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::compute_seg
-//       Access: Public, Static
-//  Description: Given a set of four properties of a curve segment
-//               (e.g. four points, four tangent values, four control
-//               points, or any combination), and a basis matrix,
-//               computes the corresponding geometry matrix that
-//               (together with the basis matrix) represents the curve
-//               that satisfies the four properties.
-//
-//               The basis matrix is passed in as B, and its inverse
-//               must be precomputed and passed in as Bi.
-//
-//               The result is returned in the matrix G, each column
-//               of which represents the cth control vertex.  If any
-//               of the four properties has RT_KEEP_ORIG set (see
-//               below), G's input value is used to define the
-//               original shape of the curve; otherwise, G's input
-//               value is ignored.
-//
-//               Each property is defined by an rtype, which may be
-//               any of RT_POINT, RT_TANGENT, or RT_CV, and may or may
-//               not be or'ed with RT_KEEP_ORIG.  The meanings of the
-//               types are as follows:
-//
-//               RT_POINT defines a specific point which the curve
-//               segment must pass through.  t is in the range [0,1]
-//               and represents the parametric value at which the
-//               curve segment will intersect the given point.  If
-//               RT_KEEP_ORIG is not set, v defines the point;
-//               otherwise, v is ignored and the original curve at
-//               point t defines the point.
-//
-//               RT_TANGENT defines a specific tangent value which the
-//               curve segment must have at point t.  As with
-//               RT_POINT, if RT_KEEP_ORIG is not set, v defines the
-//               tangent; otherwise, v is ignored and the original
-//               curve defines the tangent.
-//
-//               RT_CV defines a specific control vertex which the
-//               curve segment must have.  In this case, t is ignored.
-//               The position within the argument list determines
-//               which control vertex is applicable; e.g. rtype0 =
-//               RT_CV defines control vertex 0, and rtype2 = RT_CV
-//               defines control vertex 2.  If RT_KEEP_ORIG is not
-//               set, v defines the new control vertex; otherwise, the
-//               control vertex is taken from G.
-//
-//               The return value is true if all the parameters are
-//               sensible, or false if there is some error.
-////////////////////////////////////////////////////////////////////
-bool CubicCurveseg::
-compute_seg(int rtype0, double t0, const LVecBase4f &v0,
-            int rtype1, double t1, const LVecBase4f &v1,
-            int rtype2, double t2, const LVecBase4f &v2,
-            int rtype3, double t3, const LVecBase4f &v3,
-            const LMatrix4f &B,
-            const LMatrix4f &Bi,
-            LMatrix4f &G) {
-
-  // We can define a cubic curve segment given four arbitrary
-  // properties of the segment: any point along the curve, any tangent
-  // along the curve, any control point.  Given any four such
-  // properties, a single cubic curve segment is defined.
-
-  // For a given cubic curve segment so defined, and given a basis
-  // matrix B, we can define the four control vertices that represent
-  // the segment with the basis matrix.  That is, we can define the
-  // matrix G such that G * B * tc, where tc is [ t^3 t^2 t^1 t^0 ]
-  // for t in [ 0..1 ], represents the point on the curve segment
-  // corresponding to t.
-
-  // First, we build a matrix T, such that each of the four columns of
-  // T contains the vector that would compute the corresponding
-  // property.  We also build a corresponding matrix P, such that each
-  // of its columns contains the vector that is the solution of the
-  // corresponding column in T.
-
-  LMatrix4f T, P, GB;
-
-  // GB is G * B, but we only need to compute this if any of the
-  // columns wants the value from the original G.
-  if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
-    GB = G * B;
-  }
-
-  if (! (compute_seg_col(0, rtype0, t0, v0, B, Bi, G, GB, T, P) &&
-         compute_seg_col(1, rtype1, t1, v1, B, Bi, G, GB, T, P) &&
-         compute_seg_col(2, rtype2, t2, v2, B, Bi, G, GB, T, P) &&
-         compute_seg_col(3, rtype3, t3, v3, B, Bi, G, GB, T, P))) {
-    return false;
-  }
-
-  LMatrix4f Ti;
-  Ti = invert(T);
-
-  // Now we have T and P such that P represents the solution of T,
-  // when T is applied to the geometry and basis matrices.  That is,
-  // each column of P represents the solution computed by the
-  // corresponding column of T.  P = G * B * T.
-
-  // We simply solve for G and get G = P * T^(-1) * B^(-1).
-
-  G = P * Ti * Bi;
-
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::register_with_factory
-//       Access: Public, Static
-//  Description: Initializes the factory for reading these things from
-//               Bam files.
-////////////////////////////////////////////////////////////////////
-void CubicCurveseg::
-register_with_read_factory() {
-  BamReader::get_factory()->register_factory(get_class_type(), make_CubicCurveseg);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::make_CubicCurveseg
-//       Access: Protected
-//  Description: Factory method to generate an object of this type.
-////////////////////////////////////////////////////////////////////
-TypedWriteable *CubicCurveseg::
-make_CubicCurveseg(const FactoryParams &params) {
-  CubicCurveseg *me = new CubicCurveseg;
-  BamReader *manager;
-  Datagram packet;
-
-  parse_params(params, manager, packet);
-  DatagramIterator scan(packet);
-
-  me->fillin(scan, manager);
-  return me;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::write_datagram
-//       Access: Protected, Virtual
-//  Description: Function to write the important information in
-//               the particular object to a Datagram
-////////////////////////////////////////////////////////////////////
-void CubicCurveseg::
-write_datagram(BamWriter *manager, Datagram &me) {
-  ParametricCurve::write_datagram(manager, me);
-
-  Bx.write_datagram(me);
-  By.write_datagram(me);
-  Bz.write_datagram(me);
-  Bw.write_datagram(me);
-  me.add_bool(rational);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CubicCurveseg::fillin
-//       Access: Protected
-//  Description: Function that reads out of the datagram (or asks
-//               manager to read) all of the data that is needed to
-//               re-create this object and stores it in the appropiate
-//               place
-////////////////////////////////////////////////////////////////////
-void CubicCurveseg::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  ParametricCurve::fillin(scan, manager);
-
-  Bx.read_datagram(scan);
-  By.read_datagram(scan);
-  Bz.read_datagram(scan);
-  Bw.read_datagram(scan);
-  rational = scan.get_bool();
-}

+ 0 - 385
panda/src/parametrics/curve.h

@@ -1,385 +0,0 @@
-// Filename: curve.h
-// Created by:  drose (14Mar97)
-//
-////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-//
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
-#ifndef CURVE_H
-#define CURVE_H
-
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-
-#include "pandabase.h"
-
-#include <typedef.h>
-#include <list>
-#include <vector>
-
-#include "namedNode.h"
-#include "luse.h"
-
-
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
-
-// Parametric curve semantic types.  A parametric curve may have one
-// of these types specified.  These serve as hints to the egg reader
-// and writer code about the intention of this curve, and have no
-// other effect on the curve.
-
-BEGIN_PUBLISH //[
-#define PCT_NONE        0
-// Unspecified type.
-
-#define PCT_XYZ         1
-// A three-dimensional curve in space.
-
-#define PCT_HPR         2
-// The curve represents Euler rotation angles.
-
-#define PCT_T           3
-// A one-dimensional timewarp curve.
-END_PUBLISH //]
-
-
-//#define LVecBase3f LVecBase3f
-//typedef LVecBase3f LVecBase3f;
-
-
-// These symbols are used to define the shape of the curve segment to
-// CubicCurveseg::compute_seg().
-
-#define RT_POINT       0x01
-#define RT_TANGENT     0x02
-#define RT_CV          0x03
-#define RT_BASE_TYPE   0xff
-
-#define RT_KEEP_ORIG  0x100
-
-
-class ParametricCurveDrawer;
-class HermiteCurveCV;
-class HermiteCurve;
-class NurbsCurve;
-
-
-////////////////////////////////////////////////////////////////////
-//       Class : ParametricCurve
-// Description : A virtual base class for parametric curves.
-//               This encapsulates all curves in 3-d space defined
-//               for a single parameter t in the range [0,get_max_t()].
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA ParametricCurve : public NamedNode {
-PUBLISHED:
-  ParametricCurve();
-  virtual ~ParametricCurve();
-
-  virtual bool is_valid() const;
-
-  virtual double get_max_t() const;
-
-  void set_curve_type(int type);
-  int get_curve_type() const;
-
-  void set_num_dimensions(int num);
-  int get_num_dimensions() const;
-
-  float calc_length() const;
-  float calc_length(double from, double to) const;
-  double compute_t(double start_t, double length_offset, double guess,
-                   double threshold) const;
-
-  bool convert_to_hermite(HermiteCurve &hc) const;
-  bool convert_to_nurbs(NurbsCurve &nc) const;
-
-  void ascii_draw() const;
-
-  virtual bool get_point(double t, LVecBase3f &point) const=0;
-  virtual bool get_tangent(double t, LVecBase3f &tangent) const=0;
-  virtual bool get_pt(double t, LVecBase3f &point, LVecBase3f &tangent) const=0;
-  virtual bool get_2ndtangent(double t, LVecBase3f &tangent2) const=0;
-
-public:
-
-  struct BezierSeg {
-  public:
-    LVecBase3f _v[4];
-    double _t;
-  };
-  typedef vector<BezierSeg> BezierSegs;
-
-  virtual bool GetBezierSegs(BezierSegs &) const {
-    return false;
-  }
-
-  virtual bool GetBezierSeg(BezierSeg &) const {
-    return false;
-  }
-
-  void register_drawer(ParametricCurveDrawer *drawer);
-  void unregister_drawer(ParametricCurveDrawer *drawer);
-
-protected:
-  void invalidate(double t1, double t2);
-  void invalidate_all();
-
-  float r_calc_length(double t1, double t2,
-                      const LPoint3f &p1, const LPoint3f &p2,
-                      float seglength) const;
-
-  typedef list< ParametricCurveDrawer * > DrawerList;
-  DrawerList _drawers;
-  int _curve_type;
-  int _num_dimensions;
-
-// TypedWriteable stuff
-protected:
-  virtual void write_datagram(BamWriter *manager, Datagram &me);  
-  void fillin(DatagramIterator &scan, BamReader *manager);
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    NamedNode::init_type();
-    register_type(_type_handle, "ParametricCurve",
-		  NamedNode::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-};
-
-
-////////////////////////////////////////////////////////////////////
-//          Class : PiecewiseCurve
-// Description : A PiecewiseCurve is a curve made up of several curve
-//               segments, connected in a head-to-tail fashion.  The
-//               length of each curve segment in parametric space is
-//               definable.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA PiecewiseCurve : public ParametricCurve {
-PUBLISHED:
-  PiecewiseCurve();
-  ~PiecewiseCurve();
-
-  virtual bool is_valid() const;
-  virtual double get_max_t() const;
-
-  virtual bool get_point(double t, LVecBase3f &point) const;
-  virtual bool get_tangent(double t, LVecBase3f &tangent) const;
-  virtual bool get_pt(double t, LVecBase3f &point, LVecBase3f &tangent) const;
-  virtual bool get_2ndtangent(double t, LVecBase3f &tangent2) const;
-
-  bool adjust_point(double t,
-                       float px, float py, float pz);
-  bool adjust_tangent(double t,
-                         float tx, float ty, float tz);
-  bool adjust_pt(double t,
-                    float px, float py, float pz,
-                    float tx, float ty, float tz);
-
-public:
-  int get_num_segs() const;
-
-  ParametricCurve *get_curveseg(int ti);
-  bool insert_curveseg(int ti, ParametricCurve *seg, double tlength);
-
-  bool remove_curveseg(int ti);
-  void remove_all_curvesegs();
-
-  double get_tlength(int ti) const;
-  double get_tstart(int ti) const;
-  double get_tend(int ti) const;
-  bool set_tlength(int ti, double tlength);
-
-  void make_nurbs(int order, int num_cvs,
-                  const double knots[], const LVecBase4f cvs[]);
-
-  virtual bool GetBezierSegs(BezierSegs &bz_segs) const;
-
-  virtual bool
-  rebuild_curveseg(int rtype0, double t0, const LVecBase4f &v0,
-                   int rtype1, double t1, const LVecBase4f &v1,
-                   int rtype2, double t2, const LVecBase4f &v2,
-                   int rtype3, double t3, const LVecBase4f &v3);
-
-protected:
-  bool find_curve(const ParametricCurve *&curve, double &t) const;
-  double current_seg_range(double t) const;
-
-  class Curveseg {
-  public:
-    Curveseg() {}
-    Curveseg(ParametricCurve *c, double t) : _curve(c), _tend(t) {}
-
-    ParametricCurve *_curve;
-    double _tend;
-  };
-
-  vector<Curveseg> _segs;
-  int _last_ti;
-
-
-// TypedWriteable stuff
-protected:
-  virtual void write_datagram(BamWriter *manager, Datagram &me);  
-  void fillin(DatagramIterator &scan, BamReader *manager);
-  virtual int complete_pointers(vector_typedWriteable &plist, 
-				BamReader *manager);
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    ParametricCurve::init_type();
-    register_type(_type_handle, "PiecewiseCurve",
-                  ParametricCurve::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-};
-
-
-
-
-////////////////////////////////////////////////////////////////////
-//          Class : CubicCurveseg
-// Description : A CubicCurveseg is any curve that can be completely
-//               described by four 4-valued basis vectors, one for
-//               each dimension in three-space, and one for the
-//               homogeneous coordinate.  This includes Beziers,
-//               Hermites, and NURBS.
-//
-//               This class encapsulates a single curve segment of the
-//               cubic curve.  Normally, when we think of Bezier and
-//               Hermite curves, we think of a piecewise collection of
-//               such segments.
-//
-//               Although this class includes methods such as
-//               hermite_basis() and nurbs_basis(), to generate a
-//               Hermite and NURBS curve segment, respectively, only
-//               the final basis vectors are stored: the product of
-//               the basis matrix of the corresponding curve type, and
-//               its geometry vectors.  This is the minimum
-//               information needed to evaluate the curve.  However,
-//               the individual CV's that were used to compute these
-//               basis vectors are not retained; this might be handled
-//               in a subclass (for instance, HermiteCurve).
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA CubicCurveseg : public ParametricCurve {
-PUBLISHED:
-  virtual bool get_point(double t, LVecBase3f &point) const;
-  virtual bool get_tangent(double t, LVecBase3f &tangent) const;
-  virtual bool get_pt(double t, LVecBase3f &point, LVecBase3f &tangent) const;
-  virtual bool get_2ndtangent(double t, LVecBase3f &tangent2) const;
-
-public:
-  CubicCurveseg();
-  CubicCurveseg(const LMatrix4f &basis);
-  CubicCurveseg(const BezierSeg &seg);
-  CubicCurveseg(int order, const double knots[], const LVecBase4f cvs[]);
-
-  virtual ~CubicCurveseg();
-
-  void hermite_basis(const HermiteCurveCV &cv0,
-          const HermiteCurveCV &cv1,
-          double tlength = 1.0);
-  void bezier_basis(const BezierSeg &seg);
-  void nurbs_basis(int order, const double knots[], const LVecBase4f cvs[]);
-
-  // evaluate_point() and evaluate_vector() both evaluate the curve at
-  // a given point by applying the basis vector against the vector
-  // [t3 t2 t 1] (or some derivative).  The difference between the
-  // two is that evaluate_point() is called only with the vector
-  // [t3 t2 t 1] and computes a point in three-space and will scale by
-  // the homogeneous coordinate when the curve demands it (e.g. a
-  // NURBS), while evaluate_vector() is called with some derivative
-  // vector like [3t2 2t 1 0] and computes a vector difference between
-  // points, and will never scale by the homogeneous coordinate (which
-  // would be zero anyway).
-
-  void evaluate_point(const LVecBase4f &tv, LVecBase3f &result) const {
-    double h = (rational) ? tv.dot(Bw) : 1.0;
-    result.set(tv.dot(Bx) / h,
-               tv.dot(By) / h,
-               tv.dot(Bz) / h);
-  }
-
-  void evaluate_vector(const LVecBase4f &tv, LVecBase3f &result) const {
-    result.set(tv.dot(Bx),
-               tv.dot(By),
-               tv.dot(Bz));
-  }
-
-  virtual bool GetBezierSeg(BezierSeg &seg) const;
-
-  static bool compute_seg(int rtype0, double t0, const LVecBase4f &v0,
-                             int rtype1, double t1, const LVecBase4f &v1,
-                             int rtype2, double t2, const LVecBase4f &v2,
-                             int rtype3, double t3, const LVecBase4f &v3,
-                             const LMatrix4f &B,
-                             const LMatrix4f &Bi,
-                             LMatrix4f &G);
-
-  LVecBase4f Bx, By, Bz, Bw;
-  bool rational;
-
-
-// TypedWriteable stuff
-public:
-  static void register_with_read_factory();
-
-protected:
-  static TypedWriteable *make_CubicCurveseg(const FactoryParams &params);
-  virtual void write_datagram(BamWriter *manager, Datagram &me);  
-  void fillin(DatagramIterator &scan, BamReader *manager);
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    ParametricCurve::init_type();
-    register_type(_type_handle, "CubicCurveseg",
-                  ParametricCurve::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-};
-
-// This function is used internally to build the NURBS basis matrix
-// based on a given knot sequence.
-void compute_nurbs_basis(int order,
-                         const double knots_in[],
-                         LMatrix4f &basis);
-
-
-#endif

+ 0 - 727
panda/src/parametrics/curveDrawer.cxx

@@ -1,727 +0,0 @@
-// Filename: curveDrawer.C
-// Created by:  drose (14Mar97)
-// 
-////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-
-#include "curveDrawer.h"
-#include "config_parametrics.h"
-
-
-TypeHandle ParametricCurveDrawer::_type_handle;
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::Constructor
-//       Access: Public, Scheme
-//  Description:
-////////////////////////////////////////////////////////////////////
-ParametricCurveDrawer::
-ParametricCurveDrawer(ParametricCurve *curve) {
-  _curve = curve;
-  _time_curve = NULL;
-  _lines.set_color(1.0, 1.0, 1.0);
-  _ticks.set_color(1.0, 0.0, 0.0);
-  _tick_scale = 0.1;
-  _num_segs = 100.0;
-  _num_ticks = 0.0;
-  _frame_accurate = false;
-  _geom_node = new GeomNode;
-  _drawn = false;
-  _mapper = DefaultMap;
-  _curve->register_drawer(this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::Destructor
-//       Access: Public, Scheme, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-ParametricCurveDrawer::
-~ParametricCurveDrawer() {
-  hide();
-  if (_curve!=NULL) {
-    _curve->unregister_drawer(this);
-  }
-  if (_time_curve!=NULL) {
-    _time_curve->unregister_drawer(this);
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_curve
-//       Access: Public, Scheme
-//  Description:
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_curve(ParametricCurve *curve) {
-  if (_curve!=NULL) {
-    _curve->unregister_drawer(this);
-  }
-  _curve = curve;
-  _curve->register_drawer(this);
-  if (_drawn) {
-    draw();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_curve
-//       Access: Public, Scheme
-//  Description:
-////////////////////////////////////////////////////////////////////
-ParametricCurve *ParametricCurveDrawer::
-get_curve() {
-  return _curve;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_time_curve
-//       Access: Public, Scheme
-//  Description:
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_time_curve(ParametricCurve *curve) {
-  if (_time_curve!=NULL) {
-    _time_curve->unregister_drawer(this);
-  }
-  _time_curve = curve;
-  if (_time_curve!=NULL) {
-    _time_curve->register_drawer(this);
-  }
-  if (_drawn) {
-    draw();
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_time_curve
-//       Access: Public, Scheme
-//  Description:
-////////////////////////////////////////////////////////////////////
-ParametricCurve *ParametricCurveDrawer::
-get_time_curve() {
-  return _time_curve;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_geom_node
-//       Access: Public, Scheme
-//  Description: Returns a pointer to the drawer's GeomNode.  This is
-//               where the drawer will build the visible
-//               representation of the curve.  This GeomNode must be
-//               inserted into the scene graph to make the curve
-//               visible.  The GeomNode remains connected to the drawer,
-//               so that future updates to the drawer will reflect in
-//               the GeomNode, and the GeomNode will be emptied when the
-//               drawer destructs.  Also see detach_geom_node().
-////////////////////////////////////////////////////////////////////
-GeomNode *ParametricCurveDrawer::
-get_geom_node() {
-  return _geom_node;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::detach_geom_node
-//       Access: Public, Scheme
-//  Description: Detaches the GeomNode from the drawer so that the
-//               drawing will remain after the death of the drawer.
-//               Returns the now-static GeomNode.  A new, dynamic GeomNode
-//               is created for the drawer's future use; get_geom_node()
-//               will return this new GeomNode which will be empty until
-//               the next call to draw().
-////////////////////////////////////////////////////////////////////
-GeomNode *ParametricCurveDrawer::
-detach_geom_node() {
-  if (!_drawn) {
-    draw();
-  }
-  PT(GeomNode) g = _geom_node;
-  _geom_node = new GeomNode;
-  _drawn = false;
-  return g;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_num_segs
-//       Access: Public, Scheme
-//  Description: Specifies the number of line segments used to
-//               approximate the curve for each parametric unit.  This
-//               just affects the visual appearance of the curve as it
-//               is drawn.  The total number of segments drawn for the
-//               curve will be get_max_t() * get_num_segs().
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_num_segs(double num_segs) {
-  _num_segs = num_segs;
-  if (_drawn) {
-    draw();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_num_segs
-//       Access: Public, Scheme
-//  Description: Returns the number of line segments used to
-//               approximate the curve for each parametric unit.  This
-//               just affects the visual appearance of the curve as it
-//               is drawn.  The total number of segments drawn for the
-//               curve will be get_max_t() * get_num_segs().
-////////////////////////////////////////////////////////////////////
-double ParametricCurveDrawer::
-get_num_segs() const {
-  return _num_segs;
-}
-
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_num_ticks
-//       Access: Public, Scheme
-//  Description: Specifies the number of time tick marks drawn
-//               for each unit of time.  These tick marks are drawn at
-//               equal increments in time to give a visual
-//               approximation of speed.  Specify 0 to disable drawing
-//               of tick marks.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_num_ticks(double num_ticks) {
-  _num_ticks = num_ticks;
-  if (_drawn) {
-    draw();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_num_ticks
-//       Access: Public, Scheme
-//  Description: Returns the number of time tick marks per unit of
-//               time drawn.
-////////////////////////////////////////////////////////////////////
-double ParametricCurveDrawer::
-get_num_ticks() const {
-  return _num_ticks;
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_color
-//       Access: Public, Scheme
-//  Description: Specifies the color of the curve when it is drawn.
-//               The default is white.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_color(float r, float g, float b) {
-  _lines.set_color(r, g, b);
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_color
-//       Access: Public, Scheme
-//  Description: Specifies the color of the time tick marks drawn on
-//               the curve.  The default is red.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_tick_color(float r, float g, float b) {
-  _ticks.set_color(r, g, b);
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_frame_accurate
-//       Access: Public, Scheme
-//  Description: Specifies whether the curve drawn is to be
-//               frame-accurate.  If true, then changes made to the
-//               curve dynamically after it has been drawn will be
-//               reflected correctly in the render window.  If false,
-//               dynamic updates may be drawn before the rest of the
-//               scene has updated.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_frame_accurate(bool frame_accurate) {
-  _frame_accurate = frame_accurate;
-  if (_drawn) {
-    draw();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_frame_accurate
-//       Access: Public, Scheme
-//  Description: Returns whether the curve is drawn in frame-accurate
-//               mode.
-////////////////////////////////////////////////////////////////////
-bool ParametricCurveDrawer::
-get_frame_accurate() const {
-  return _frame_accurate;
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::draw
-//       Access: Public, Scheme, Virtual
-//  Description: Creates a series of line segments that approximates
-//               the curve.  These line segments may be made visible
-//               by adding the GeomNode returned by get_geom_node() into the
-//               scene graph.
-////////////////////////////////////////////////////////////////////
-bool ParametricCurveDrawer::
-draw() {
-  // First, remove the old drawing, if any.
-  hide();
-
-  // If there's no curve, draw nothing and return false.
-  if (_curve==NULL || !_curve->is_valid()) {
-    return false;
-  }
-
-  // Otherwise, let's go to town!
-  int total_segs = (int)floor(_curve->get_max_t() * _num_segs + 0.5);
-
-  double scale = _curve->get_max_t() / (double)(total_segs-1);
-  double t;
-  LVecBase3f point, tangent;
-  bool last_in, next_in;
-
-  last_in = false;
-  int i;
-
-  for (i = 0; i<total_segs; i++) {
-    t = (double)i * scale;
-
-    // Since we're just drawing the geometric shape of the curve, we
-    // don't care at this point about the time curve.
-
-    next_in = _curve->get_pt(t, point, tangent);
-
-    LVecBase3f p = _mapper(point, tangent, t);
-
-    if (!next_in || !last_in) {
-      _lines.move_to(p);
-    } else {
-      _lines.draw_to(p);
-    }
-    last_in = next_in;
-  }
-
-  _lines.create(_geom_node, _frame_accurate);
-  _drawn = true;
-
-  // Now draw the time tick marks.
-  if (_num_ticks > 0.0) {
-    LVecBase3f tangent2;
-    int total_ticks = (int)floor(_curve->get_max_t() * _num_ticks + 0.5);
-
-    scale = get_max_t() / (double)(total_ticks-1);
-    for (i = 0; i<total_ticks; i++) {
-      t = (double)i * scale;
-      if (_time_curve!=NULL) {
-	_time_curve->get_point(t, point);
-	t = point[0];
-      }
-
-      _curve->get_pt(t, point, tangent);
-      _curve->get_2ndtangent(t, tangent2);
-      
-      LVecBase3f pt = _mapper(point, tangent, t);
-      LVecBase3f t1, t2;
-      get_tick_marks(_mapper(tangent, tangent2, t + 1.0), t1, t2);
-      
-      _ticks.move_to(pt - t1 * _tick_scale);
-      _ticks.draw_to(pt + t1 * _tick_scale);
-      _ticks.move_to(pt - t2 * _tick_scale);
-      _ticks.draw_to(pt + t2 * _tick_scale);
-    }
-    _ticks.create(_geom_node, _frame_accurate);
-  }
-	
-
-  return true;
-}
-
-    
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::recompute
-//       Access: Public, Scheme, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-bool ParametricCurveDrawer::
-recompute(double t1, double t2, ParametricCurve *curve) {
-  if (!_drawn || _curve==NULL || !_curve->is_valid()) {
-    return false;
-  }
-
-  bool redraw_curve = true;
-
-  if (_time_curve!=NULL) {
-    if (curve != _time_curve) {
-      // If the recompute call came from the basic curve, and not from
-      // the time curve, the t1 and t2 it gave us aren't the t1 and t2
-      // we need.  To be safe, we'll run over the whole range.
-      t1 = 0.0;
-      t2 = get_max_t();
-    } else {
-      // On the other hand, if the recompute call came from the time
-      // curve, we won't be changing the curve's geometric shape at
-      // all--we only need to move the tick marks.
-      redraw_curve = false;
-    }
-  }
-
-  // Scale t1 and t2 to [0, 1].
-  t1 = min(max(t1 / get_max_t(), 0.0), 1.0);
-  t2 = min(max(t2 / get_max_t(), 0.0), 1.0);
-
-  int n1, n2, i;
-  double scale, t;
-  LVecBase3f point, tangent;
-
-  if (redraw_curve) {
-    // Compute the number of total segments we will draw.  This is based
-    // on the parametric length of the curve, _curve->get_max_t().
-    
-    int total_segs = (int)floor(_curve->get_max_t() * _num_segs + 0.5);
-    
-    n1 = (int)floor(t1 * (total_segs-1));
-    n2 = (int)ceil(t2 * (total_segs-1));
-    
-    // This should be implied by the above t1, t2 bounds check.
-    nassertr(n1>=0 && n1<total_segs, false);
-    nassertr(n2>=0 && n2<total_segs, false);
-    
-    scale = _curve->get_max_t() / (double)(total_segs-1);
-    
-    for (i = n1; i<=n2; i++) {
-      t = (double)i * scale;
-      
-      _curve->get_pt(t, point, tangent);
-
-      LVecBase3f p = _mapper(point, tangent, t);
-      _lines.set_vertex(i, p);
-    }
-  }
-    
-  if (_num_ticks > 0.0) {
-    LVecBase3f tangent2;
-    int total_ticks = (int)floor(_curve->get_max_t() * _num_ticks + 0.5);
-
-    n1 = (int)floor(t1 * (total_ticks-1));
-    n2 = (int)ceil(t2 * (total_ticks-1));
-
-    scale = get_max_t() / (double)(total_ticks-1);
-
-    for (i = n1; i<=n2; i++) {
-      t = (double)i * scale;
-      if (_time_curve!=NULL) {
-	_time_curve->get_point(t, point);
-	t = point[0];
-      }
-      _curve->get_pt(t, point, tangent);
-      _curve->get_2ndtangent(t, tangent2);
-      
-      LVecBase3f pt = _mapper(point, tangent, t);
-      LVecBase3f t1, t2;
-      get_tick_marks(_mapper(tangent, tangent2, t + 1.0), 
-		     t1, t2);
-      
-      int ti = i * 4;
-      _ticks.set_vertex(ti, pt - t1 * _tick_scale);
-      _ticks.set_vertex(ti+1, pt + t1 * _tick_scale);
-      _ticks.set_vertex(ti+2, pt - t2 * _tick_scale);
-      _ticks.set_vertex(ti+3, pt + t2 * _tick_scale);
-    }
-  }
-  return true;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::hide
-//       Access: Public, Scheme
-//  Description: Removes the lines that were created by a previous
-//               call to draw().
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-hide() {
-  _geom_node->clear();
-  _drawn = false;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_tick_scale
-//       Access: Public, Scheme
-//  Description: Sets the visible size of the time tick marks.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_tick_scale(double scale) {
-  _tick_scale = scale;
-  if (_drawn) {
-    draw();
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_tick_scale
-//       Access: Public, Scheme
-//  Description: Returns the size of the time tick marks.
-////////////////////////////////////////////////////////////////////
-double ParametricCurveDrawer::
-get_tick_scale() const {
-  return _tick_scale;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_graph_type
-//       Access: Public, Scheme
-//  Description: Selects one of a handful of pre-canned graph types
-//               the drawer can represent.  The default, PCD_DEFAULT,
-//               draws the curve's shape in three-dimensional space;
-//               other possibilites like PCD_XVST draw a graph of X(t)
-//               vs. t in the Z and X axes, respectively.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_graph_type(int graph_type) {
-  switch (graph_type) {
-  case PCD_DEFAULT:
-    set_mapper(DefaultMap);
-    break;
-
-  case PCD_XVST:
-    set_mapper(XvsT);
-    break;
-
-  case PCD_YVST:
-    set_mapper(YvsT);
-    break;
-
-  case PCD_ZVST:
-    set_mapper(ZvsT);
-    break;
-
-  case PCD_DXVST:
-    set_mapper(dXvsT);
-    break;
-
-  case PCD_DYVST:
-    set_mapper(dYvsT);
-    break;
-
-  case PCD_DZVST:
-    set_mapper(dZvsT);
-    break;
-
-  case PCD_IXVST:
-    set_mapper(iXvsT);
-    break;
-
-  case PCD_IYVST:
-    set_mapper(iYvsT);
-    break;
-
-  default:
-    parametrics_cat->warning() << "Invalid graph_type " << graph_type << endl;
-  }
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::disable
-//       Access: Public
-//  Description: Called by the ParametricCurve destructor to indicate
-//               that a curve we are depending on has just been
-//               deleted.  We must no longer attempt to access this
-//               curve.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-disable(ParametricCurve *curve) {
-  if (curve==_time_curve) {
-    _time_curve = NULL;
-  } else if (curve==_curve) {
-    // Hmm, the primary curve has destructed.  We're useless now.
-    _curve = NULL;
-    hide();
-  } else {
-    parametrics_cat->warning()
-      << "ParametricCurveDrawer::disable() called on nonsensible curve"
-      << endl;
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::set_mapper
-//       Access: Public
-//  Description: This establishes the function that will be applied to
-//               each point of the four-dimensional curve to translate
-//               it to a three-dimensional representation.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-set_mapper(LVecBase3fMapper *mapper) {
-  // If the mapper hasn't changed, don't force a redraw.
-  if (_mapper != mapper) {
-    _mapper = mapper;
-    if (_drawn) {
-      draw();
-    }
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::DefaultMap
-//       Access: Public, Static
-//  Description: This mapping function returns the X,Y,Z component of
-//               each point, showing the line's three-dimensional
-//               shape.
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-DefaultMap(const LVecBase3f &point, const LVecBase3f &, double) {
-  return LVecBase3f(point[0], point[1], point[2]);
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::XvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of X(t), with the
-//               x along the Y axis and t along the X axis.
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-XvsT(const LVecBase3f &point, const LVecBase3f &, double t) {
-  return LVecBase3f(t, point[0], 0.0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::iXvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of X(t), with the
-//               x along the X axis and t along the Y axis.
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-iXvsT(const LVecBase3f &point, const LVecBase3f &, double t) {
-  return LVecBase3f(point[0], t, 0.0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::YvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of Y(t), with the
-//               y along the Y axis and t along the X axis.
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-YvsT(const LVecBase3f &point, const LVecBase3f &, double t) {
-  return LVecBase3f(t, point[1], 0.0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::YvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of Y(t), with the
-//               y along the X axis and t along the Y axis.
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-iYvsT(const LVecBase3f &point, const LVecBase3f &, double t) {
-  return LVecBase3f(point[1], t, 0.0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::ZvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of Z(t), with the
-//               z along the Y axis and t along the X axis.
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-ZvsT(const LVecBase3f &point, const LVecBase3f &, double t) {
-  return LVecBase3f(t, point[2], 0.0);
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::dXvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of dX(t), the
-//               derivative of X(t).
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-dXvsT(const LVecBase3f &, const LVecBase3f &tangent, double t) {
-  return LVecBase3f(t, tangent[0], 0.0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::dYvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of dY(t), the
-//               derivative of Y(t).
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-dYvsT(const LVecBase3f &, const LVecBase3f &tangent, double t) {
-  return LVecBase3f(t, tangent[1], 0.0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::dZvsT
-//       Access: Public, Static
-//  Description: This mapping function shows a graph of dZ(t), the
-//               derivative of Z(t).
-////////////////////////////////////////////////////////////////////
-LVecBase3f ParametricCurveDrawer::
-dZvsT(const LVecBase3f &, const LVecBase3f &tangent, double t) {
-  return LVecBase3f(t, tangent[2], 0.0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ParametricCurveDrawer::get_tick_marks
-//       Access: Protected, Static
-//  Description: Given a tangent vector, computes two vectors at right
-//               angles to the tangent and to each other, suitable for
-//               drawing as tick marks.
-////////////////////////////////////////////////////////////////////
-void ParametricCurveDrawer::
-get_tick_marks(const LVecBase3f &tangent, LVecBase3f &t1, LVecBase3f &t2) {
-  LVector3f tn = tangent;
-  tn.normalize();
-
-  // Decide the smallest axis of tn and cross with the corresponding
-  // unit vector.
-  if (fabs(tn[0]) <= fabs(tn[1]) && fabs(tn[0]) <= fabs(tn[2])) {
-    // X is smallest.
-    t1 = tn.cross(LVector3f(1.0, 0.0, 0.0));
-
-  } else if (fabs(tn[1]) <= fabs(tn[2])) {
-    // Y is smallest.
-    t1 = tn.cross(LVector3f(0.0, 1.0, 0.0));
-
-  } else {
-    // Z is smallest.
-    t1 = tn.cross(LVector3f(0.0, 0.0, 1.0));
-  }
-
-  t2 = tn.cross(t1);
-}

+ 0 - 149
panda/src/parametrics/curveDrawer.h

@@ -1,149 +0,0 @@
-// Filename: curveDrawer.h
-// Created by:  drose (14Mar97)
-// 
-////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
-#ifndef CURVEDRAWER_H
-#define CURVEDRAWER_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes 
-////////////////////////////////////////////////////////////////////
-
-#include "curve.h"
-#include "lineSegs.h"
-
-#include <typeHandle.h>
-
-////////////////////////////////////////////////////////////////////
-// Defines 
-////////////////////////////////////////////////////////////////////
-
-typedef LVecBase3f LVecBase3fMapper(const LVecBase3f &point, 
-			     const LVecBase3f &tangent, 
-			     double t);
-
-BEGIN_PUBLISH //[
-// The different kinds of ParametricCurveDrawer graph types
-#define PCD_DEFAULT 1
-#define PCD_XVST    2
-#define PCD_YVST    3
-#define PCD_ZVST    4
-#define PCD_DXVST   6
-#define PCD_DYVST   7
-#define PCD_DZVST   8
-#define PCD_IXVST   9
-#define PCD_IYVST   10
-END_PUBLISH //]
-
-class ParametricSurface;
-
-////////////////////////////////////////////////////////////////////
-// 	 Class : ParametricCurveDrawer
-// Description : Draws a 3-d parametric curve in the scene by creating
-//               a series of line segments to approximate the curve.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA ParametricCurveDrawer : public TypedObject {
-
-////////////////////////////////////////////////////////////////////
-// Member functions visible to Scheme
-////////////////////////////////////////////////////////////////////
-
-PUBLISHED:
-  ParametricCurveDrawer(ParametricCurve *curve);
-  virtual ~ParametricCurveDrawer();
-
-  void set_curve(ParametricCurve *curve);
-  ParametricCurve *get_curve();
-
-  void set_time_curve(ParametricCurve *curve);
-  ParametricCurve *get_time_curve();
-
-  GeomNode *get_geom_node();
-  GeomNode *detach_geom_node();
-
-  void set_num_segs(double num_segs);
-  double get_num_segs() const;
-
-  void set_num_ticks(double num_ticks);
-  double get_num_ticks() const;
-
-  void set_color(float r, float g, float b);
-  void set_tick_color(float r, float g, float b);
-
-  void set_frame_accurate(bool frame_accurate);
-  bool get_frame_accurate() const;
-
-  virtual bool draw();
-  virtual bool recompute(double t1, double t2, ParametricCurve *curve=NULL);
-  void hide();
-
-  void set_tick_scale(double scale);
-  double get_tick_scale() const;
-
-  void set_graph_type(int graph_type);
-
-////////////////////////////////////////////////////////////////////
-// Member functions not visible to Scheme
-////////////////////////////////////////////////////////////////////
-
-public:
-  double get_max_t() const {
-    return _time_curve==NULL ? _curve->get_max_t() : _time_curve->get_max_t();
-  }
-
-  void disable(ParametricCurve *curve);
-
-  void set_mapper(LVecBase3fMapper *mapper);
-
-  static LVecBase3f DefaultMap(const LVecBase3f &point, const LVecBase3f &, double);
-  static LVecBase3f XvsT(const LVecBase3f &point, const LVecBase3f &, double t);
-  static LVecBase3f iXvsT(const LVecBase3f &point, const LVecBase3f &, double t);
-  static LVecBase3f YvsT(const LVecBase3f &point, const LVecBase3f &, double t);
-  static LVecBase3f iYvsT(const LVecBase3f &point, const LVecBase3f &, double t);
-  static LVecBase3f ZvsT(const LVecBase3f &point, const LVecBase3f &, double t);
-  static LVecBase3f dXvsT(const LVecBase3f &, const LVecBase3f &tangent, double t);
-  static LVecBase3f dYvsT(const LVecBase3f &, const LVecBase3f &tangent, double t);
-  static LVecBase3f dZvsT(const LVecBase3f &, const LVecBase3f &tangent, double t);
-
-protected:
-  static void get_tick_marks(const LVecBase3f &tangent, LVecBase3f &t1, LVecBase3f &t2);
-
-  PT(GeomNode) _geom_node;
-  double _num_segs;
-  ParametricCurve *_curve, *_time_curve;
-  LineSegs _lines, _ticks;
-  bool _drawn;
-  double _num_ticks;
-  double _tick_scale;
-  bool _frame_accurate;
-  LVecBase3fMapper *_mapper;
-
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    register_type(_type_handle, "ParametricCurveDrawer",
-		  TypedObject::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
- 
-private:
-  static TypeHandle _type_handle;
-};
-  
-#endif

+ 42 - 0
panda/src/parametrics/curveFitter.I

@@ -0,0 +1,42 @@
+// Filename: curveFitter.I
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CurveFitter::DataPoint::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CurveFitter::DataPoint::
+DataPoint() : 
+  _t(0.0),
+  _xyz(0.0, 0.0, 0.0),
+  _hpr(0.0, 0.0, 0.0),
+  _tangent(0.0, 0.0, 0.0),
+  _hpr_tangent(0.0, 0.0, 0.0) 
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CurveFitter::DataPoint::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void CurveFitter::DataPoint::
+output(ostream &out) const {
+  out << "Time " << _t << " xyz " << _xyz << " hpr " << _hpr
+      << " tan " << _tangent;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CurveFitter::DataPoint::operator <
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool CurveFitter::DataPoint::
+operator < (const DataPoint &other) const {
+  return _t < other._t;
+}
+

+ 203 - 286
panda/src/parametrics/curveFitter.cxx

@@ -2,22 +2,14 @@
 // Created by:  drose (17Sep98)
 // Created by:  drose (17Sep98)
 // 
 // 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
+
 #include "pandabase.h"
 #include "pandabase.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 
 
 #include "curveFitter.h"
 #include "curveFitter.h"
 #include "config_parametrics.h"
 #include "config_parametrics.h"
-#include "curve.h"
-#include "nurbsCurve.h"
+#include "parametricCurve.h"
+#include "classicNurbsCurve.h"
 #include "hermiteCurve.h"
 #include "hermiteCurve.h"
 #include <algorithm>
 #include <algorithm>
 
 
@@ -30,6 +22,8 @@ TypeHandle CurveFitter::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CurveFitter::
 CurveFitter::
 CurveFitter() {
 CurveFitter() {
+  _got_xyz = false;
+  _got_hpr = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -53,16 +47,47 @@ reset() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CurveFitter::add_point
+//     Function: CurveFitter::add_xyz
 //       Access: Public
 //       Access: Public
-//  Description: Adds a single sample point.
+//  Description: Adds a single sample xyz.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CurveFitter::
 void CurveFitter::
-add_point(double t, const LVecBase3f &point) {
+add_xyz(float t, const LVecBase3f &xyz) {
   DataPoint dp;
   DataPoint dp;
   dp._t = t;
   dp._t = t;
-  dp._point = point;
+  dp._xyz = xyz;
   _data.push_back(dp);
   _data.push_back(dp);
+  _got_xyz = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CurveFitter::add_hpr
+//       Access: Public
+//  Description: Adds a single sample hpr.
+////////////////////////////////////////////////////////////////////
+void CurveFitter::
+add_hpr(float t, const LVecBase3f &hpr) {
+  DataPoint dp;
+  dp._t = t;
+  dp._hpr = hpr;
+  _data.push_back(dp);
+  _got_hpr = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CurveFitter::add_xyz_hpr
+//       Access: Public
+//  Description: Adds a single sample xyz & hpr simultaneously.
+////////////////////////////////////////////////////////////////////
+void CurveFitter::
+add_xyz_hpr(float t, const LVecBase3f &xyz, const LVecBase3f &hpr) {
+  DataPoint dp;
+  dp._t = t;
+  dp._xyz = xyz;
+  dp._hpr = hpr;
+  _data.push_back(dp);
+  _got_xyz = true;
+  _got_hpr = true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -81,24 +106,32 @@ get_num_samples() const {
 //       Access: Public
 //       Access: Public
 //  Description: Returns the parametric value of the nth sample added.
 //  Description: Returns the parametric value of the nth sample added.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-double CurveFitter::
+float CurveFitter::
 get_sample_t(int n) const {
 get_sample_t(int n) const {
   nassertr(n >= 0 && n < (int)_data.size(), 0.0);
   nassertr(n >= 0 && n < (int)_data.size(), 0.0);
   return _data[n]._t;
   return _data[n]._t;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CurveFitter::get_sample_point
+//     Function: CurveFitter::get_sample_xyz
 //       Access: Public
 //       Access: Public
 //  Description: Returns the point in space of the nth sample added.
 //  Description: Returns the point in space of the nth sample added.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-const LVecBase3f &CurveFitter::
-get_sample_point(int n) const {
-#ifndef NDEBUG
-  static const LVecBase3f zero(0.0, 0.0, 0.0);
-  nassertr(n >= 0 && n < (int)_data.size(), zero);
-#endif
-  return _data[n]._point;
+LVecBase3f CurveFitter::
+get_sample_xyz(int n) const {
+  nassertr(n >= 0 && n < (int)_data.size(), LVecBase3f::zero());
+  return _data[n]._xyz;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CurveFitter::get_sample_hpr
+//       Access: Public
+//  Description: Returns the orientation of the nth sample added.
+////////////////////////////////////////////////////////////////////
+LVecBase3f CurveFitter::
+get_sample_hpr(int n) const {
+  nassertr(n >= 0 && n < (int)_data.size(), LVecBase3f::zero());
+  return _data[n]._hpr;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -108,12 +141,9 @@ get_sample_point(int n) const {
 //               added.  This is only meaningful if compute_tangents()
 //               added.  This is only meaningful if compute_tangents()
 //               has already been called.
 //               has already been called.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-const LVecBase3f &CurveFitter::
+LVecBase3f CurveFitter::
 get_sample_tangent(int n) const {
 get_sample_tangent(int n) const {
-#ifndef NDEBUG
-  static const LVecBase3f zero(0.0, 0.0, 0.0);
-  nassertr(n >= 0 && n < (int)_data.size(), zero);
-#endif
+  nassertr(n >= 0 && n < (int)_data.size(), LVecBase3f::zero());
   return _data[n]._tangent;
   return _data[n]._tangent;
 }
 }
 
 
@@ -137,66 +167,33 @@ remove_samples(int begin, int end) {
 //     Function: CurveFitter::sample
 //     Function: CurveFitter::sample
 //       Access: Public
 //       Access: Public
 //  Description: Generates a series of data points by sampling the
 //  Description: Generates a series of data points by sampling the
-//               given curve the indicated number of times.  If even
-//               is true, the sampled t value is taken from the actual
-//               curve length, as opposed to the parametric length.
+//               given curve (or xyz/hpr curves) the indicated number
+//               of times.  The sampling is made evenly in parametric
+//               time, and then the timewarps, if any, are applied.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CurveFitter::
 void CurveFitter::
-sample(ParametricCurve *curve, int count, bool even) {
-  double max_t = curve->get_max_t();
-  double t, last_t, d;
+sample(ParametricCurveCollection *curves, int count) {
+  nassertv(curves != (ParametricCurveCollection *)NULL);
+  float max_t = curves->get_max_t();
+  float t, last_t, d;
   DataPoint dp;
   DataPoint dp;
 
 
-
-  size_t n = _data.size();
-
   last_t = 0.0;
   last_t = 0.0;
   d = 0.0;
   d = 0.0;
   int i;
   int i;
   for (i = 0; i < count; i++) {
   for (i = 0; i < count; i++) {
-    t = max_t * (double)i / (double)(count-1);
-    curve->get_point(t, dp._point);
-
-    if (even) {
-      d += curve->calc_length(last_t, t);
-      dp._t = d;
-    } else {
+    t = max_t * (float)i / (float)(count-1);
+    if (curves->evaluate(t, dp._xyz, dp._hpr)) {
       dp._t = t;
       dp._t = t;
+      _data.push_back(dp);
     }
     }
-
-    _data.push_back(dp);
-    last_t = t;
   }
   }
 
 
-  if (even) {
-    double scale = max_t / d;
-    while (n < _data.size()) {
-      _data[n]._t *= scale;
-      n++;
-    }
+  if (curves->get_xyz_curve() != (ParametricCurve *)NULL) {
+    _got_xyz = true;
   }
   }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CurveFitter::generate_even
-//       Access: Public
-//  Description: Generates a set of data points whose x coordinate is
-//               evenly distributed across the indicated distance and
-//               parametric time.  Useful before a call to
-//               compute_timewarp().
-////////////////////////////////////////////////////////////////////
-void CurveFitter::
-generate_even(int count, double net_distance, double net_time) {
-  double t, d;
-  DataPoint dp;
-  int i;
-  for (i = 0; i < count; i++) {
-    t = net_time * (double)i / (double)(count-1);
-    d = net_distance * (double)i / (double)(count-1);
-    
-    dp._point.set(d, 0.0, 0.0);
-    dp._t = t;
-    _data.push_back(dp);
+  if (curves->get_hpr_curve() != (ParametricCurve *)NULL) {
+    _got_hpr = true;
   }
   }
 }
 }
 
 
@@ -205,11 +202,9 @@ generate_even(int count, double net_distance, double net_time) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CurveFitter::wrap_hpr
 //     Function: CurveFitter::wrap_hpr
 //       Access: Public
 //       Access: Public
-//  Description: Assumes the data points collected represent a set of
-//               HPR coordinates.  Resets each data point so that the
-//               maximum delta between any two consecutive points is
-//               180 degrees, which should prevent incorrect HPR
-//               wrapping.
+//  Description: Resets each HPR data point so that the maximum delta
+//               between any two consecutive points is 180 degrees,
+//               which should prevent incorrect HPR wrapping.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CurveFitter::
 void CurveFitter::
 wrap_hpr() {
 wrap_hpr() {
@@ -220,87 +215,20 @@ wrap_hpr() {
   for (di = _data.begin(); di != _data.end(); ++di) {
   for (di = _data.begin(); di != _data.end(); ++di) {
     int i;
     int i;
     for (i = 0; i < 3; i++) {
     for (i = 0; i < 3; i++) {
-      (*di)._point[i] += net[i];
+      (*di)._hpr[i] += net[i];
   
   
-      while (((*di)._point[i] - last[i]) > 180.0) {
-        (*di)._point[i] -= 360.0;
+      while (((*di)._hpr[i] - last[i]) > 180.0) {
+        (*di)._hpr[i] -= 360.0;
         net[i] -= 360.0;
         net[i] -= 360.0;
       }
       }
       
       
-      while (((*di)._point[i] - last[i]) < -180.0) {
-        (*di)._point[i] += 360.0;
+      while (((*di)._hpr[i] - last[i]) < -180.0) {
+        (*di)._hpr[i] += 360.0;
         net[i] += 360.0;
         net[i] += 360.0;
       }
       }
       
       
-      last[i] = (*di)._point[i];
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CurveFitter::compute_timewarp
-//       Access: Public
-//  Description: Assumes the data points already collected represent
-//               the distance along a given curve at each unit of
-//               parametric time (only the X coordinate is used).
-//               Computes a new set of data points based on the
-//               indicated curve that will serve as a timewarp to
-//               produce this effect.
-////////////////////////////////////////////////////////////////////
-void CurveFitter::
-compute_timewarp(const ParametricCurve *xyz) {
-  Data::iterator di;
-  double last_t = 0.0;
-  double last_d = 0.0;
-  double last_ratio = 1.0;
-
-  for (di = _data.begin(); di != _data.end(); ++di) {
-    double d = (*di)._point[0];
-    double t = xyz->compute_t(last_t, d - last_d, 
-                              last_t + last_ratio * (d - last_d),
-                              0.001);
-    (*di)._point.set(t, 0.0, 0.0);
-
-    /*
-    // Special HPR computation
-    {
-      LVecBase3f tangent;
-      LMatrix4f mat;
-      pfCoord c;
-      static double last_h = 0.0;
-      static double h_net = 0.0;
-
-      xyz->get_tangent(t, tangent);
-      look_at(mat, tangent, LVecBase3f(0.0, 0.0, 1.0));
-      mat.getOrthoCoord(&c);
-      cerr << "Replacing R " << c.hpr[2] << " with " << (*di)._point[1] << "\n";
-      c.hpr[2] = (*di)._point[1];
-      c.hpr[0] += h_net;
-
-      // Check the wrap on the heading
-      if ((c.hpr[0] - last_h) > 180.0) {
-        c.hpr[0] -= 360.0;
-        h_net -= 360.0;
-      }
-
-      if ((c.hpr[0] - last_h) < -180.0) {
-        c.hpr[0] += 360.0;
-        h_net += 360.0;
-      }
-
-      cerr << "H is " << c.hpr[0] << " h_net is " << h_net << "\n";
-      last_h = c.hpr[0];
-      
-      (*di)._point = c.hpr;
-      (*di)._t = t;
+      last[i] = (*di)._hpr[i];
     }
     }
-    */
-
-    if (d != last_d) {
-      last_ratio = (t - last_t) / (d - last_d);
-    }
-    last_t = t;
-    last_d = d;
   }
   }
 }
 }
 
 
@@ -324,9 +252,9 @@ sort_points() {
 //               last samples.
 //               last samples.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CurveFitter::
 void CurveFitter::
-desample(double factor) {
+desample(float factor) {
   int in, out;
   int in, out;
-  double count = factor;
+  float count = factor;
 
 
   out = 0;
   out = 0;
   for (in = 0; in < (int)_data.size()-1; in++) {
   for (in = 0; in < (int)_data.size()-1; in++) {
@@ -354,10 +282,18 @@ desample(double factor) {
 //               as the points were added).
 //               as the points were added).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CurveFitter::
 void CurveFitter::
-compute_tangents(double scale) {
+compute_tangents(float scale) {
   // If the head and tail points match up, close the curve.
   // If the head and tail points match up, close the curve.
-  bool closed =
-    (_data.front()._point.almost_equal(_data.back()._point, 0.001));
+  bool closed = false;
+
+  if (_got_xyz) {
+    closed =
+      (_data.front()._xyz.almost_equal(_data.back()._xyz, 0.001));
+
+  } else if (_got_hpr) {
+    closed =
+      (_data.front()._hpr.almost_equal(_data.back()._hpr, 0.001));
+  }
 
 
   int i;
   int i;
   int len = _data.size();
   int len = _data.size();
@@ -365,25 +301,51 @@ compute_tangents(double scale) {
   // First, get all the points in the middle, excluding endpoints.
   // First, get all the points in the middle, excluding endpoints.
   // These are handled the same whether we are closing the curve or
   // These are handled the same whether we are closing the curve or
   // not.
   // not.
-  for (i = 1; i < len-1; i++) {
-    _data[i]._tangent = 
-      (_data[i+1]._point - _data[i-1]._point) * scale /
-      (_data[i+1]._t - _data[i-1]._t);
+  if (_got_xyz) {
+    for (i = 1; i < len-1; i++) {
+      _data[i]._tangent = 
+	(_data[i+1]._xyz - _data[i-1]._xyz) * scale /
+	(_data[i+1]._t - _data[i-1]._t);
+    }
+  }
+  if (_got_hpr) {
+    for (i = 1; i < len-1; i++) {
+      _data[i]._hpr_tangent = 
+	(_data[i+1]._hpr - _data[i-1]._hpr) * scale /
+	(_data[i+1]._t - _data[i-1]._t);
+    }
   }
   }
 
 
   // Now handle the endpoints.
   // Now handle the endpoints.
   if (closed) {
   if (closed) {
-    _data[0]._tangent = _data[len-1]._tangent =
-      (_data[1]._point - _data[len-2]._point) * scale /
-      ((_data[1]._t - _data[0]._t) + (_data[len-1]._t - _data[len-2]._t));
-
+    if (_got_xyz) {
+      _data[0]._tangent = _data[len-1]._tangent =
+	(_data[1]._xyz - _data[len-2]._xyz) * scale /
+	((_data[1]._t - _data[0]._t) + (_data[len-1]._t - _data[len-2]._t));
+    }
+    if (_got_hpr) {
+      _data[0]._tangent = _data[len-1]._tangent =
+	(_data[1]._hpr - _data[len-2]._hpr) * scale /
+	((_data[1]._t - _data[0]._t) + (_data[len-1]._t - _data[len-2]._t));
+    }
+      
   } else {
   } else {
-    _data[0]._tangent =
-      (_data[1]._point - _data[0]._point) * scale /
-      ((_data[1]._t - _data[0]._t) * 2.0);
-    _data[len-1]._tangent =
-      (_data[len-1]._point - _data[len-2]._point) * scale /
-      ((_data[len-1]._t - _data[len-2]._t) * 2.0);
+    if (_got_xyz) {
+      _data[0]._tangent =
+	(_data[1]._xyz - _data[0]._xyz) * scale /
+	((_data[1]._t - _data[0]._t) * 2.0);
+      _data[len-1]._tangent =
+	(_data[len-1]._xyz - _data[len-2]._xyz) * scale /
+	((_data[len-1]._t - _data[len-2]._t) * 2.0);
+    }
+    if (_got_hpr) {
+      _data[0]._tangent =
+	(_data[1]._hpr - _data[0]._hpr) * scale /
+	((_data[1]._t - _data[0]._t) * 2.0);
+      _data[len-1]._tangent =
+	(_data[len-1]._hpr - _data[len-2]._hpr) * scale /
+	((_data[len-1]._t - _data[len-2]._t) * 2.0);
+    }
   }
   }
 }
 }
 
 
@@ -393,20 +355,41 @@ compute_tangents(double scale) {
 //  Description: Converts the current set of data points into a
 //  Description: Converts the current set of data points into a
 //               Hermite curve.
 //               Hermite curve.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(HermiteCurve) CurveFitter::
+PT(ParametricCurveCollection) CurveFitter::
 make_hermite() const {
 make_hermite() const {
-  PT(HermiteCurve) hc = new HermiteCurve;
+  PT(ParametricCurveCollection) result = new ParametricCurveCollection;
 
 
-  Data::const_iterator di;
-  for (di = _data.begin(); di != _data.end(); ++di) {
-    int n = hc->insert_cv((*di)._t);
-    hc->set_cv_type(n, HC_SMOOTH);
-    hc->set_cv_point(n, (*di)._point);
-    hc->set_cv_in(n, (*di)._tangent);
-    hc->set_cv_out(n, (*di)._tangent);
+  if (_got_xyz) {
+    HermiteCurve *hc = new HermiteCurve;
+    result->add_curve(hc);
+    hc->set_curve_type(PCT_XYZ);
+    
+    Data::const_iterator di;
+    for (di = _data.begin(); di != _data.end(); ++di) {
+      int n = hc->insert_cv((*di)._t);
+      hc->set_cv_type(n, HC_SMOOTH);
+      hc->set_cv_point(n, (*di)._xyz);
+      hc->set_cv_in(n, (*di)._tangent);
+      hc->set_cv_out(n, (*di)._tangent);
+    }
   }
   }
 
 
-  return hc;
+  if (_got_hpr) {
+    HermiteCurve *hc = new HermiteCurve;
+    result->add_curve(hc);
+    hc->set_curve_type(PCT_HPR);
+    
+    Data::const_iterator di;
+    for (di = _data.begin(); di != _data.end(); ++di) {
+      int n = hc->insert_cv((*di)._t);
+      hc->set_cv_type(n, HC_SMOOTH);
+      hc->set_cv_point(n, (*di)._hpr);
+      hc->set_cv_in(n, (*di)._hpr_tangent);
+      hc->set_cv_out(n, (*di)._hpr_tangent);
+    }
+  }
+
+  return result;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -416,118 +399,52 @@ make_hermite() const {
 //               NURBS curve.  This gives a smoother curve than
 //               NURBS curve.  This gives a smoother curve than
 //               produced by MakeHermite().
 //               produced by MakeHermite().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(NurbsCurve) CurveFitter::
+PT(ParametricCurveCollection) CurveFitter::
 make_nurbs() const {
 make_nurbs() const {
-  if (_data.size() < 2) {
-    return NULL;
-  }
+  // We start with the HermiteCurves produced above, then convert them
+  // to NURBS form.
+  PT(ParametricCurveCollection) hermites = make_hermite();
+  PT(ParametricCurveCollection) result = new ParametricCurveCollection;
 
 
-#if 0
-  PT(NurbsCurve) nc = new NurbsCurve;
-  nc->set_order(4);
-
-  // First, we need four CV's to get started.
-  nc->append_cv(LVecBase3f(0.0, 0.0, 0.0));
-  nc->append_cv(LVecBase3f(0.0, 0.0, 0.0));
-  nc->append_cv(LVecBase3f(0.0, 0.0, 0.0));
-  nc->append_cv(LVecBase3f(0.0, 0.0, 0.0));
-  nc->set_knot(4, _data[1]._t);
-
-  nc->recompute();
-  LVecBase3f junk;
-  nc->get_point(nc->get_max_t(), junk);  // Reference the last segment.
-  const LVecBase3f &p0 = _data[0]._point;
-  LVecBase3f t0 = _data[0]._tangent * 2.0;
-  LVecBase3f t1 = _data[1]._tangent * 2.0;
-  const LVecBase3f &p1 = _data[1]._point;
-
-  nc->rebuild_curveseg(RT_POINT, 0.0, pfVec4(p0[0], p0[1], p0[2], 1.0),
-                       RT_TANGENT, 0.0, pfVec4(t0[0], t0[1], t0[2], 0.0),
-                       RT_TANGENT, 1.0, pfVec4(t1[0], t1[1], t1[2], 0.0),
-                       RT_POINT, 1.0, pfVec4(p1[0], p1[1], p1[2], 1.0));
+  int num_curves = hermites->get_num_curves();
+  for (int c = 0; c < num_curves; c++) {
+    ClassicNurbsCurve *nc = new ClassicNurbsCurve(*hermites->get_curve(c));
+    result->add_curve(nc);
 
 
-  int i;
-  for (i = 2; i < _data.size(); i++) {
-    cerr << "Adding point " << i << "\n";
-    nc->append_cv(LVecBase3f(0.0, 0.0, 0.0));
-    nc->set_knot(i + 3, _data[i]._t);
-    nc->recompute();
-    nc->get_point(nc->get_max_t(), junk);
+    // Now we even out the knots to smooth out the curve and make
+    // everything c2 continuous.
 
 
-    /*
-    const LVecBase3f &p0 = _data[i-1]._point;
-    const LVecBase3f &t0 = _data[i-1]._tangent;
-    const LVecBase3f &p1 = _data[i]._point;
-    const LVecBase3f &t1 = _data[i]._tangent;
+    int num_knots = nc->get_num_knots();
     
     
-    nc->rebuild_curveseg(RT_POINT, 0.0, pfVec4(p0[0], p0[1], p0[2], 1.0),
-                         RT_TANGENT, 0.0, pfVec4(t0[0], t0[1], t0[2], 0.0),
-                         RT_TANGENT, 1.0, pfVec4(t1[0], t1[1], t1[2], 0.0),
-                         RT_POINT, 1.0, pfVec4(p1[0], p1[1], p1[2], 1.0));
-                         */
-
-    const LVecBase3f &pi = _data[i]._point;
-    nc->rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(),
-                         RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(),
-                         RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(),
-                         RT_POINT, 1.0, pfVec4(pi[0], pi[1], pi[2], 1.0));
-  }
-
-  /*
-  nc->append_cv(LVecBase3f(0.0, 0.0, 0.0));
-  nc->recompute();
-  nc->get_point(nc->get_max_t(), junk);
-  const LVecBase3f &pi = _data[_data.size()-1]._point;
-  nc->rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(),
-                       RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(),
-                       RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(),
-                       RT_CV, 0.0, pfVec4(pi[0], pi[1], pi[2], 1.0));
-                       */
-  
-  nc->recompute();
-  return nc;
-
-#else
-
-  // We start with the HermiteCurve produced above, then convert it to
-  // NURBS form.
-  PT(HermiteCurve) hc = make_hermite();
-  PT(NurbsCurve) nc = new NurbsCurve(*hc);
-
-  // Now we even out the knots to smooth out the curve and make
-  // everything c2 continuous.
+    // We expect this to be a 4th order curve, since we just converted
+    // it from a Hermite.
+    assert(nc->get_order() == 4);
+    assert(num_knots > 0);
 
 
-  int num_knots = nc->get_num_knots();
-
-  // We expect this to be a 4th order curve, since we just converted
-  // it from a Hermite.
-  assert(nc->get_order() == 4);
-  assert(num_knots > 0);
-
-  // Now the knot sequence goes something like this:
-  //    0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 4
-
-  // We'll consider pairs of knot values beginning at position 3 and
-  // every third position thereafter.  We just even out these values
-  // between their two neighbors.
+    // Now the knot sequence goes something like this:
+    //    0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 4
+    
+    // We'll consider pairs of knot values beginning at position 3 and
+    // every third position thereafter.  We just even out these values
+    // between their two neighbors.
+    
+    int i;
+    float k1, k2 = nc->get_knot(num_knots-1);
+    for (i = 3; i < num_knots - 4; i += 3) {
+      k1 = nc->get_knot(i-1);
+      k2 = nc->get_knot(i+2);
+      nc->set_knot(i, (k1 + k1 + k2) / 3.0);
+      nc->set_knot(i+1, (k1 + k2 + k2) / 3.0);
+    }
 
 
-  int i;
-  double k1, k2 = nc->get_knot(num_knots-1);
-  for (i = 3; i < num_knots - 4; i += 3) {
-    k1 = nc->get_knot(i-1);
-    k2 = nc->get_knot(i+2);
-    nc->set_knot(i, (k1 + k1 + k2) / 3.0);
-    nc->set_knot(i+1, (k1 + k2 + k2) / 3.0);
+    // The last knot must have the terminal value.
+    nc->set_knot(num_knots-4, k2);
+    
+    // Finally, recompute the curve.
+    nc->recompute();
   }
   }
 
 
-  // The last knot must have the terminal value.
-  nc->set_knot(num_knots-4, k2);
-
-  // Finally, recompute the curve.
-  nc->recompute();
-
-  return nc;
-#endif
+  return result;
 }
 }
   
   
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 32 - 35
panda/src/parametrics/curveFitter.h

@@ -2,27 +2,22 @@
 // Created by:  drose (17Sep98)
 // Created by:  drose (17Sep98)
 // 
 // 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
 
 
 #ifndef CURVEFITTER_H
 #ifndef CURVEFITTER_H
 #define CURVEFITTER_H
 #define CURVEFITTER_H
 
 
 #include <pandabase.h>
 #include <pandabase.h>
-#include "luse.h"
+
+#include <luse.h>
+
+#include "parametricCurveCollection.h"
+
 #include <typedef.h>
 #include <typedef.h>
 #include <vector>
 #include <vector>
 
 
 class HermiteCurve;
 class HermiteCurve;
-class NurbsCurve;
 class ParametricCurve;
 class ParametricCurve;
+class ClassicNurbsCurve;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CurveFitter
 //       Class : CurveFitter
@@ -34,25 +29,25 @@ PUBLISHED:
   ~CurveFitter();
   ~CurveFitter();
 
 
   void reset();
   void reset();
-  void add_point(double t, const LVecBase3f &point);
+  void add_xyz(float t, const LVecBase3f &xyz);
+  void add_hpr(float t, const LVecBase3f &hpr);
+  void add_xyz_hpr(float t, const LVecBase3f &xyz, const LVecBase3f &hpr);
 
 
   int get_num_samples() const;
   int get_num_samples() const;
-  double get_sample_t(int n) const;
-  const LVecBase3f &get_sample_point(int n) const;
-  const LVecBase3f &get_sample_tangent(int n) const;
+  float get_sample_t(int n) const;
+  LVecBase3f get_sample_xyz(int n) const;
+  LVecBase3f get_sample_hpr(int n) const;
+  LVecBase3f get_sample_tangent(int n) const;
   void remove_samples(int begin, int end);
   void remove_samples(int begin, int end);
 
 
-  void sample(ParametricCurve *curve, int count, bool even);
-  void generate_even(int count, double net_distance, double net_time);
-
+  void sample(ParametricCurveCollection *curves, int count);
   void wrap_hpr();
   void wrap_hpr();
-  void compute_timewarp(const ParametricCurve *xyz);
   void sort_points();
   void sort_points();
-  void desample(double factor);
+  void desample(float factor);
 
 
-  void compute_tangents(double scale);
-  PT(HermiteCurve) make_hermite() const;
-  PT(NurbsCurve) make_nurbs() const;
+  void compute_tangents(float scale);
+  PT(ParametricCurveCollection) make_hermite() const;
+  PT(ParametricCurveCollection) make_nurbs() const;
   
   
   void output(ostream &out) const;
   void output(ostream &out) const;
   void write(ostream &out) const;
   void write(ostream &out) const;
@@ -60,23 +55,23 @@ PUBLISHED:
 public:
 public:
   class DataPoint {
   class DataPoint {
   public:
   public:
-    DataPoint() : _t(0.0), _point(0.0, 0.0, 0.0), _tangent(0.0, 0.0, 0.0) { }
-    void output(ostream &out) const {
-      out << "Time " << _t << " point " << _point << " tan " << _tangent;
-    }
-
-    int operator < (const DataPoint &other) const {
-      return _t < other._t;
-    }
+    INLINE DataPoint();
+    INLINE void output(ostream &out) const;
+    INLINE bool operator < (const DataPoint &other) const;
     
     
-    double _t;
-    LVecBase3f _point;
+    float _t;
+    LVecBase3f _xyz;
+    LVecBase3f _hpr;
     LVecBase3f _tangent;
     LVecBase3f _tangent;
+    LVecBase3f _hpr_tangent;
   };
   };
   
   
   typedef vector<DataPoint> Data;
   typedef vector<DataPoint> Data;
   Data _data;
   Data _data;
 
 
+  bool _got_xyz;
+  bool _got_hpr;
+
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;
@@ -89,14 +84,16 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 };
 };
 
 
-inline ostream &operator << (ostream &out, const CurveFitter::DataPoint &dp) {
+INLINE ostream &operator << (ostream &out, const CurveFitter::DataPoint &dp) {
   dp.output(out);
   dp.output(out);
   return out;
   return out;
 }
 }
 
 
-inline ostream &operator << (ostream &out, const CurveFitter &cf) {
+INLINE ostream &operator << (ostream &out, const CurveFitter &cf) {
   cf.output(out);
   cf.output(out);
   return out;
   return out;
 }
 }
 
 
+#include "curveFitter.I"
+
 #endif
 #endif

+ 46 - 136
panda/src/parametrics/hermiteCurve.cxx

@@ -2,19 +2,6 @@
 // Created by:  drose (27Feb98)
 // Created by:  drose (27Feb98)
 // 
 // 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 #include "hermiteCurve.h"
 #include "hermiteCurve.h"
@@ -29,9 +16,6 @@
 
 
 #include <math.h>
 #include <math.h>
 
 
-////////////////////////////////////////////////////////////////////
-// Statics
-////////////////////////////////////////////////////////////////////
 TypeHandle HermiteCurve::_type_handle;
 TypeHandle HermiteCurve::_type_handle;
 
 
 static const LVecBase3f zero = LVecBase3f(0.0, 0.0, 0.0);
 static const LVecBase3f zero = LVecBase3f(0.0, 0.0, 0.0);
@@ -97,7 +81,7 @@ void HermiteCurveCV::
 set_in(const LVecBase3f &in) {
 set_in(const LVecBase3f &in) {
   _in = in;
   _in = in;
   /*
   /*
-  double l;
+  float l;
   switch (_type) {
   switch (_type) {
   case HC_G1:
   case HC_G1:
     l = _in.length();
     l = _in.length();
@@ -123,7 +107,7 @@ void HermiteCurveCV::
 set_out(const LVecBase3f &out) {
 set_out(const LVecBase3f &out) {
   _out = out;
   _out = out;
   /*
   /*
-  double l;
+  float l;
   switch (_type) {
   switch (_type) {
   case HC_G1:
   case HC_G1:
     l = _out.length();
     l = _out.length();
@@ -189,7 +173,7 @@ set_name(const string &name) {
 void HermiteCurveCV::
 void HermiteCurveCV::
 format_egg(ostream &out, int indent_level, int num_dimensions,
 format_egg(ostream &out, int indent_level, int num_dimensions,
        bool show_in, bool show_out,
        bool show_in, bool show_out,
-       double scale_in, double scale_out) const {
+       float scale_in, float scale_out) const {
   if (show_in) {
   if (show_in) {
     indent(out, indent_level) << "<Vertex> {\n";
     indent(out, indent_level) << "<Vertex> {\n";
     show_vec3(out, indent_level + 2, _p - scale_in * _in / 3.0, 
     show_vec3(out, indent_level + 2, _p - scale_in * _in / 3.0, 
@@ -265,7 +249,7 @@ fillin(DatagramIterator &scan, BamReader *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::Constructor
 //     Function: HermiteCurve::Constructor
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 HermiteCurve::
 HermiteCurve::
@@ -273,14 +257,14 @@ HermiteCurve() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: HermiteCurve::Constructor
-//       Access: Public, Scheme
+//     Function: HermiteCurve::Copy Constructor
+//       Access: Published
 //  Description: Constructs a Hermite from the indicated (possibly
 //  Description: Constructs a Hermite from the indicated (possibly
 //               non-hermite) curve.
 //               non-hermite) curve.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 HermiteCurve::
 HermiteCurve::
 HermiteCurve(const ParametricCurve &nc) {
 HermiteCurve(const ParametricCurve &nc) {
-  if (!nc.convert_to_hermite(*this)) {
+  if (!nc.convert_to_hermite(this)) {
     parametrics_cat->warning()
     parametrics_cat->warning()
       << "Cannot make a Hermite from the indicated curve."
       << "Cannot make a Hermite from the indicated curve."
       << endl;
       << endl;
@@ -304,7 +288,7 @@ HermiteCurve::
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::get_num_cvs
 //     Function: HermiteCurve::get_num_cvs
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Returns the number of CV's in the curve.
 //  Description: Returns the number of CV's in the curve.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int HermiteCurve::
 int HermiteCurve::
@@ -315,7 +299,7 @@ get_num_cvs() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::insert_cv
 //     Function: HermiteCurve::insert_cv
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Inserts a new CV at the given parametric point along
 //  Description: Inserts a new CV at the given parametric point along
 //               the curve.  If this parametric point is already on
 //               the curve.  If this parametric point is already on
 //               the curve, the CV is assigned an index between its
 //               the curve, the CV is assigned an index between its
@@ -330,14 +314,14 @@ get_num_cvs() const {
 //               The index number of the new CV is returned.
 //               The index number of the new CV is returned.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int HermiteCurve::
 int HermiteCurve::
-insert_cv(double t) {
+insert_cv(float t) {
   if (!is_valid() || t >= get_max_t()) {
   if (!is_valid() || t >= get_max_t()) {
     int n = append_cv(HC_SMOOTH, 0.0, 0.0, 0.0);
     int n = append_cv(HC_SMOOTH, 0.0, 0.0, 0.0);
     set_cv_tstart(n, t);
     set_cv_tstart(n, t);
     return n;
     return n;
   }
   }
 
 
-  t = min(max(t, 0.0), get_max_t());
+  t = min(max(t, 0.0f), get_max_t());
 
 
   int n = find_cv(t);
   int n = find_cv(t);
   nassertr(n+1<get_num_cvs(), 0);
   nassertr(n+1<get_num_cvs(), 0);
@@ -362,7 +346,7 @@ insert_cv(double t) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::append_cv
 //     Function: HermiteCurve::append_cv
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Adds a new CV to the end of the curve.  The new CV is
 //  Description: Adds a new CV to the end of the curve.  The new CV is
 //               given initial in/out tangents of 0.  The return value
 //               given initial in/out tangents of 0.  The return value
 //               is the index of the new CV.
 //               is the index of the new CV.
@@ -390,7 +374,7 @@ append_cv(int type, float x, float y, float z) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::remove_cv
 //     Function: HermiteCurve::remove_cv
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Removes the given CV from the curve.  Returns true if
 //  Description: Removes the given CV from the curve.  Returns true if
 //               the CV existed, false otherwise.
 //               the CV existed, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -413,7 +397,7 @@ remove_cv(int n) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::remove_all_cvs
 //     Function: HermiteCurve::remove_all_cvs
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Removes all CV's from the curve.
 //  Description: Removes all CV's from the curve.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void HermiteCurve::
 void HermiteCurve::
@@ -429,7 +413,7 @@ remove_all_cvs() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::set_cv_type
 //     Function: HermiteCurve::set_cv_type
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Changes the given CV's continuity type.  Legal values
 //  Description: Changes the given CV's continuity type.  Legal values
 //               are HC_CUT, HC_FREE, HC_G1, or HC_SMOOTH.
 //               are HC_CUT, HC_FREE, HC_G1, or HC_SMOOTH.
 //
 //
@@ -464,7 +448,7 @@ set_cv_type(int n, int type) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::set_cv_point
 //     Function: HermiteCurve::set_cv_point
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Changes the given CV's position.
 //  Description: Changes the given CV's position.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool HermiteCurve::
 bool HermiteCurve::
@@ -479,7 +463,7 @@ set_cv_point(int n, float x, float y, float z) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::set_cv_in
 //     Function: HermiteCurve::set_cv_in
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Changes the given CV's in tangent.  Depending on the
 //  Description: Changes the given CV's in tangent.  Depending on the
 //               continuity type, this may also adjust the out
 //               continuity type, this may also adjust the out
 //               tangent.
 //               tangent.
@@ -496,7 +480,7 @@ set_cv_in(int n, float x, float y, float z) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::set_cv_out
 //     Function: HermiteCurve::set_cv_out
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Changes the given CV's out tangent.  Depending on the
 //  Description: Changes the given CV's out tangent.  Depending on the
 //               continuity type, this may also adjust the in
 //               continuity type, this may also adjust the in
 //               tangent.
 //               tangent.
@@ -513,12 +497,12 @@ set_cv_out(int n, float x, float y, float z) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::set_cv_tstart
 //     Function: HermiteCurve::set_cv_tstart
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Changes the given CV's parametric starting time.
 //  Description: Changes the given CV's parametric starting time.
 //               This may affect the shape of the curve.
 //               This may affect the shape of the curve.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool HermiteCurve::
 bool HermiteCurve::
-set_cv_tstart(int n, double tstart) {
+set_cv_tstart(int n, float tstart) {
   if (n <= 0 || n >= (int)_points.size()) {
   if (n <= 0 || n >= (int)_points.size()) {
     return false;
     return false;
   }
   }
@@ -533,7 +517,7 @@ set_cv_tstart(int n, double tstart) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::set_cv_name
 //     Function: HermiteCurve::set_cv_name
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Changes the name associated with a particular CV.
 //  Description: Changes the name associated with a particular CV.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool HermiteCurve::
 bool HermiteCurve::
@@ -549,7 +533,7 @@ set_cv_name(int n, const char *name) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::get_cv_type
 //     Function: HermiteCurve::get_cv_type
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Returns the given CV's continuity type, HC_CUT,
 //  Description: Returns the given CV's continuity type, HC_CUT,
 //               HC_FREE, HC_G1, or HC_SMOOTH, or 0 if there is
 //               HC_FREE, HC_G1, or HC_SMOOTH, or 0 if there is
 //               no such CV.
 //               no such CV.
@@ -566,7 +550,7 @@ get_cv_type(int n) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::get_cv_point
 //     Function: HermiteCurve::get_cv_point
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Returns the position of the given CV.
 //  Description: Returns the position of the given CV.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 const LVecBase3f &HermiteCurve::
 const LVecBase3f &HermiteCurve::
@@ -585,7 +569,7 @@ get_cv_point(int n, LVecBase3f &v) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::get_cv_in
 //     Function: HermiteCurve::get_cv_in
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Returns the in tangent of the given CV.
 //  Description: Returns the in tangent of the given CV.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 const LVecBase3f &HermiteCurve::
 const LVecBase3f &HermiteCurve::
@@ -604,7 +588,7 @@ get_cv_in(int n, LVecBase3f &v) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::get_cv_out
 //     Function: HermiteCurve::get_cv_out
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Returns the out tangent of the given CV.
 //  Description: Returns the out tangent of the given CV.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 const LVecBase3f &HermiteCurve::
 const LVecBase3f &HermiteCurve::
@@ -623,11 +607,11 @@ get_cv_out(int n, LVecBase3f &v) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::get_cv_tstart
 //     Function: HermiteCurve::get_cv_tstart
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Returns the starting point in parametric space of the
 //  Description: Returns the starting point in parametric space of the
 //               given CV.
 //               given CV.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-double HermiteCurve::
+float HermiteCurve::
 get_cv_tstart(int n) const {
 get_cv_tstart(int n) const {
   if (n<0) {
   if (n<0) {
     return 0.0;
     return 0.0;
@@ -640,7 +624,7 @@ get_cv_tstart(int n) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::get_cv_name
 //     Function: HermiteCurve::get_cv_name
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: Returns the name of the given CV, or NULL.
 //  Description: Returns the name of the given CV, or NULL.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string HermiteCurve::
 string HermiteCurve::
@@ -685,7 +669,7 @@ output(ostream &out) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::write_cv
 //     Function: HermiteCurve::write_cv
-//       Access: Public, Scheme
+//       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void HermiteCurve::
 void HermiteCurve::
@@ -725,70 +709,6 @@ write_cv(ostream &out, int n) const {
 }
 }
 
 
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: HermiteCurve::write_egg
-//       Access: Public, Scheme
-//  Description: Writes an egg description of the hermite curve to the
-//               specified output file.  Creates the file if it does
-//               not exist; appends to the end of it if it does.
-//               Returns true if the file is successfully written.
-////////////////////////////////////////////////////////////////////
-bool HermiteCurve::
-write_egg(const char *filename) {
-  const char *basename = strrchr(filename, '/');
-  basename = (basename==NULL) ? filename : basename+1;
-
-  ofstream out(filename, ios::app);
-  return write_egg(out, basename);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: HermiteCurve::write_egg
-//       Access: Public, Scheme
-//  Description: Writes an egg description of the hermite curve to the
-//               specified output stream.  Returns true if the file is
-//               successfully written.
-////////////////////////////////////////////////////////////////////
-bool HermiteCurve::
-write_egg(ostream &out, const char *basename) {
-  if (!has_name()) {
-    // If we don't have a name, come up with one.
-    int len = strlen(basename);
-    if (len>4 && strcmp(basename+len-4, ".egg")==0) {
-      len -= 4;
-    }
-
-    char *name = (char *)alloca(len + 5);
-    strncpy(name, basename, len);
-    switch (_curve_type) {
-    case PCT_XYZ:
-      strcpy(name+len, "_xyz");
-      break;
-
-    case PCT_HPR:
-      strcpy(name+len, "_hpr");
-      break;
-
-    case PCT_T:
-      strcpy(name+len, "_t");
-      break;
-      
-    default:
-      name[len] = '\0';
-    };
-
-    set_name(name);
-  }
-
-  format_egg(out);
-
-  if (out) {
-    return true;
-  } else {
-    return false;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HermiteCurve::rebuild_curveseg
 //     Function: HermiteCurve::rebuild_curveseg
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -799,10 +719,10 @@ write_egg(ostream &out, const char *basename) {
 //               possible, false if something goes horribly wrong.
 //               possible, false if something goes horribly wrong.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool HermiteCurve::
 bool HermiteCurve::
-rebuild_curveseg(int, double, const LVecBase4f &,
-		 int, double, const LVecBase4f &,
-		 int, double, const LVecBase4f &,
-		 int, double, const LVecBase4f &) {
+rebuild_curveseg(int, float, const LVecBase4f &,
+		 int, float, const LVecBase4f &,
+		 int, float, const LVecBase4f &,
+		 int, float, const LVecBase4f &) {
   cerr << "rebuild_curveseg not implemented for this curve type.\n";
   cerr << "rebuild_curveseg not implemented for this curve type.\n";
   return false;
   return false;
 }
 }
@@ -812,10 +732,11 @@ rebuild_curveseg(int, double, const LVecBase4f &,
 //       Access: Public
 //       Access: Public
 //  Description: Formats the Hermite curve for output to an Egg file.
 //  Description: Formats the Hermite curve for output to an Egg file.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void HermiteCurve::
-format_egg(ostream &out, int indent_level) const {
+bool HermiteCurve::
+format_egg(ostream &out, const string &name, const string &curve_type,
+	   int indent_level) const {
   indent(out, indent_level)
   indent(out, indent_level)
-    << "<VertexPool> " << get_name() << ".pool {\n";
+    << "<VertexPool> " << name << ".pool {\n";
 
 
   int i;
   int i;
   for (i = 0; i < (int)_points.size(); i++) {
   for (i = 0; i < (int)_points.size(); i++) {
@@ -828,24 +749,11 @@ format_egg(ostream &out, int indent_level) const {
   }
   }
   indent(out, indent_level) << "}\n";
   indent(out, indent_level) << "}\n";
     
     
-  indent(out, indent_level) << "<BezierCurve> " << get_name() << " {\n";
-
-  if (_curve_type!=PCT_NONE) {
-    indent(out, indent_level+2) << "<Scalar> type { ";
-    switch (_curve_type) {
-    case PCT_XYZ:
-      out << "XYZ";
-      break;
+  indent(out, indent_level) << "<BezierCurve> " << name << " {\n";
 
 
-    case PCT_HPR:
-      out << "HPR";
-      break;
-
-    case PCT_T:
-      out << "T";
-      break;
-    };
-    out << " }\n";
+  if (!curve_type.empty()) {
+    indent(out, indent_level+2)
+      << "<Scalar> type { " << curve_type << " }\n";
   }
   }
 
 
   indent(out, indent_level+2) << "<TLengths> {";
   indent(out, indent_level+2) << "<TLengths> {";
@@ -870,10 +778,12 @@ format_egg(ostream &out, int indent_level) const {
     out << " " << i;
     out << " " << i;
   }
   }
   out << "\n";
   out << "\n";
-  indent(out, indent_level+4) << "<Ref> { " << get_name() << ".pool }\n";
+  indent(out, indent_level+4) << "<Ref> { " << name << ".pool }\n";
   indent(out, indent_level+2) << "}\n";
   indent(out, indent_level+2) << "}\n";
 
 
   indent(out, indent_level) << "}\n";
   indent(out, indent_level) << "}\n";
+
+  return true;
 }
 }
 
 
 
 
@@ -898,7 +808,7 @@ wrap_hpr(const LVecBase3f &hpr1, LVecBase3f &hpr2) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void HermiteCurve::
 void HermiteCurve::
 invalidate_cv(int n, bool redo_all) {
 invalidate_cv(int n, bool redo_all) {
-  double t1 = 0.0, t2 = get_max_t();
+  float t1 = 0.0, t2 = get_max_t();
   if (n>0 && _points[n-1]._type!=HC_CUT) {
   if (n>0 && _points[n-1]._type!=HC_CUT) {
     const HermiteCurveCV &p1 = _points[n-1];
     const HermiteCurveCV &p1 = _points[n-1];
     HermiteCurveCV p2(_points[n]);
     HermiteCurveCV p2(_points[n]);
@@ -943,7 +853,7 @@ invalidate_cv(int n, bool redo_all) {
 //               t.
 //               t.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int HermiteCurve::
 int HermiteCurve::
-find_cv(double t) {
+find_cv(float t) {
   nassertr(is_valid(), 0);
   nassertr(is_valid(), 0);
 
 
   int n;
   int n;

+ 14 - 35
panda/src/parametrics/hermiteCurve.h

@@ -2,28 +2,12 @@
 // Created by:  drose (27Feb98)
 // Created by:  drose (27Feb98)
 // 
 // 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
+
 #ifndef HERMITECURVE_H
 #ifndef HERMITECURVE_H
 #define HERMITECURVE_H
 #define HERMITECURVE_H
 
 
-////////////////////////////////////////////////////////////////////
-// Includes 
-////////////////////////////////////////////////////////////////////
-
-#include "curve.h"
-
-////////////////////////////////////////////////////////////////////
-// Defines 
-////////////////////////////////////////////////////////////////////
+#include "piecewiseCurve.h"
+#include "cubicCurveseg.h"
 
 
 
 
 BEGIN_PUBLISH //[
 BEGIN_PUBLISH //[
@@ -51,8 +35,6 @@ BEGIN_PUBLISH //[
 // change continuously.  This is C1 parametric continuity.
 // change continuously.  This is C1 parametric continuity.
 END_PUBLISH //]
 END_PUBLISH //]
 
 
-class NurbsCurve;
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 // 	 Class : HermiteCurveCV
 // 	 Class : HermiteCurveCV
 // Description : A single CV of a Hermite curve.  Hermite curve CV's
 // Description : A single CV of a Hermite curve.  Hermite curve CV's
@@ -72,7 +54,7 @@ public:
 
 
   void format_egg(ostream &out, int indent, int num_dimensions,
   void format_egg(ostream &out, int indent, int num_dimensions,
 	      bool show_in, bool show_out,
 	      bool show_in, bool show_out,
-	      double scale_in, double scale_out) const;
+	      float scale_in, float scale_out) const;
 
 
   void write_datagram(BamWriter *manager, Datagram &me) const;
   void write_datagram(BamWriter *manager, Datagram &me) const;
   void fillin(DatagramIterator &scan, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
@@ -102,7 +84,7 @@ PUBLISHED:
 
 
   int get_num_cvs() const;
   int get_num_cvs() const;
 
 
-  int insert_cv(double t);
+  int insert_cv(float t);
   int append_cv(int type, float x, float y, float z);
   int append_cv(int type, float x, float y, float z);
   inline int append_cv(int type, const LVecBase3f &v) {
   inline int append_cv(int type, const LVecBase3f &v) {
     return append_cv(type, v[0], v[1], v[2]);
     return append_cv(type, v[0], v[1], v[2]);
@@ -124,7 +106,7 @@ PUBLISHED:
   inline bool set_cv_out(int n, const LVecBase3f &v) {
   inline bool set_cv_out(int n, const LVecBase3f &v) {
     return set_cv_out(n, v[0], v[1], v[2]);
     return set_cv_out(n, v[0], v[1], v[2]);
   }
   }
-  bool set_cv_tstart(int n, double tstart);
+  bool set_cv_tstart(int n, float tstart);
   bool set_cv_name(int n, const char *name);
   bool set_cv_name(int n, const char *name);
 
 
 
 
@@ -135,14 +117,11 @@ PUBLISHED:
   void get_cv_in(int n, LVecBase3f &v) const;
   void get_cv_in(int n, LVecBase3f &v) const;
   const LVecBase3f &get_cv_out(int n) const;
   const LVecBase3f &get_cv_out(int n) const;
   void get_cv_out(int n, LVecBase3f &v) const;
   void get_cv_out(int n, LVecBase3f &v) const;
-  double get_cv_tstart(int n) const;
+  float get_cv_tstart(int n) const;
   string get_cv_name(int n) const;
   string get_cv_name(int n) const;
 
 
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
   void write_cv(ostream &out, int n) const;
   void write_cv(ostream &out, int n) const;
-
-  bool write_egg(const char *filename);
-  bool write_egg(ostream &out, const char *basename);
   
   
 public:
 public:
 
 
@@ -151,17 +130,17 @@ public:
   }
   }
 
 
   virtual bool
   virtual bool
-  rebuild_curveseg(int rtype0, double t0, const LVecBase4f &v0,
-		   int rtype1, double t1, const LVecBase4f &v1,
-		   int rtype2, double t2, const LVecBase4f &v2,
-		   int rtype3, double t3, const LVecBase4f &v3);
-
-  void format_egg(ostream &out, int indent=0) const;
+  rebuild_curveseg(int rtype0, float t0, const LVecBase4f &v0,
+		   int rtype1, float t1, const LVecBase4f &v1,
+		   int rtype2, float t2, const LVecBase4f &v2,
+		   int rtype3, float t3, const LVecBase4f &v3);
 
 
 protected:
 protected:
+  virtual bool format_egg(ostream &out, const string &name, 
+			  const string &curve_type, int indent_level) const;
 
 
   void invalidate_cv(int n, bool redo_all);
   void invalidate_cv(int n, bool redo_all);
-  int find_cv(double t);
+  int find_cv(float t);
   void recompute_basis();
   void recompute_basis();
 
 
   vector<HermiteCurveCV> _points;
   vector<HermiteCurveCV> _points;

+ 0 - 985
panda/src/parametrics/nurbsCurve.cxx

@@ -1,985 +0,0 @@
-// Filename: nurbsCurve.C
-// Created by:  drose (27Feb98)
-// 
-////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-
-#include "nurbsCurve.h"
-#include "config_parametrics.h"
-
-#include <indent.h>
-#include <datagram.h>
-#include <datagramIterator.h>
-#include <bamWriter.h>
-#include <bamReader.h>
-
-////////////////////////////////////////////////////////////////////
-// Statics
-////////////////////////////////////////////////////////////////////
-
-TypeHandle NurbsCurve::_type_handle;
-
-static const LVecBase3f zero = LVecBase3f(0.0, 0.0, 0.0);
-// This is returned occasionally from some of the functions, and is
-// used from time to time as an initializer.
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::Constructor
-//       Access: Public, Scheme
-//  Description: 
-////////////////////////////////////////////////////////////////////
-NurbsCurve::
-NurbsCurve() {
-  _order = 4;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::Constructor
-//       Access: Public, Scheme
-//  Description: Constructs a NURBS curve equivalent to the indicated
-//               (possibly non-NURBS) curve.
-////////////////////////////////////////////////////////////////////
-NurbsCurve::
-NurbsCurve(const ParametricCurve &pc) {
-  _order = 4;
-  
-  if (!pc.convert_to_nurbs(*this)) {
-    parametrics_cat->warning() 
-      << "Cannot make a NURBS from the indicated curve."
-      << endl;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::Constructor
-//       Access: Public
-//  Description: Constructs a NURBS curve according to the indicated
-//               NURBS parameters.
-////////////////////////////////////////////////////////////////////
-NurbsCurve::
-NurbsCurve(int order, int num_cvs,
-	   const double knots[], const LVecBase4f cvs[]) {
-  _order = order;
-
-  int i;
-  _cvs.reserve(num_cvs);
-  for (i = 0; i < num_cvs; i++) {
-    append_cv(cvs[i]);
-  }
-
-  int num_knots = num_cvs + order;
-  for (i = 0; i < num_knots; i++) {
-    set_knot(i, knots[i]);
-  }
-
-  recompute();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::Destructor
-//       Access: Protected
-//  Description: 
-////////////////////////////////////////////////////////////////////
-NurbsCurve::
-~NurbsCurve() {
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::set_order
-//       Access: Public, Scheme
-//  Description: Changes the order of the curve.  Must be a value from
-//               1 to 4.  Can only be done when there are no cv's.
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-set_order(int order) {
-  if (order < 1 || order > 4) {
-    parametrics_cat->warning()
-      << "Invalid NURBS curve order: " << order << endl;
-    return;
-  }
-  if (!_cvs.empty()) {
-    parametrics_cat->warning()
-      << "Cannot change NURBS curve order on a nonempty curve." << endl;
-    return;
-  }
-  _order = order;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::get_order
-//       Access: Public, Scheme
-//  Description: 
-////////////////////////////////////////////////////////////////////
-int NurbsCurve::
-get_order() const {
-  return _order;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::get_num_cvs
-//       Access: Public, Scheme
-//  Description: 
-////////////////////////////////////////////////////////////////////
-int NurbsCurve::
-get_num_cvs() const {
-  return _cvs.size();
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::insert_cv
-//       Access: Public, Scheme
-//  Description: Inserts a new CV into the middle of the curve at the
-//               indicated parametric value.  This doesn't change the
-//               shape or timing of the curve; however, it is
-//               irreversible: if the new CV is immediately removed,
-//               the curve will be changed.  Returns the index of the
-//               newly created CV.
-////////////////////////////////////////////////////////////////////
-int NurbsCurve::
-insert_cv(double t) {
-  if (_cvs.empty()) {
-    return append_cv(0.0, 0.0, 0.0);
-  }
-
-  if (t <= 0) {
-    t = 0.0;
-  }
-
-  int k = find_cv(t);
-  if (k < 0) {
-    return append_cv(_cvs.back()._p);
-  }
-
-  // Now we are inserting a knot between k-1 and k.  We'll adjust the
-  // CV's according to Bohm's rule.
-
-  // First, get the new values of all the CV's that will change.
-  // These are the CV's in the range [k - (_order-1), k-1].
-
-  LVecBase4f new_cvs[3];
-  int i;
-  for (i = 0; i < _order-1; i++) {
-    int nk = i + k - (_order-1);
-    double ti = get_knot(nk);
-    double d = get_knot(nk + _order-1) - ti;
-    if (d == 0.0) {
-      new_cvs[i] = _cvs[nk-1]._p;
-    } else {
-      double a = (t - ti) / d;
-      new_cvs[i] = (1.0-a)*_cvs[nk-1]._p + a*_cvs[nk]._p;
-    }
-  }
-
-  // Now insert the new CV
-  _cvs.insert(_cvs.begin() + k-1, CV());
-
-  // Set all the new position values
-  for (i = 0; i < _order-1; i++) {
-    int nk = i + k - (_order-1);
-    _cvs[nk]._p = new_cvs[i];
-  }
-
-  // And set the new knot value.
-  _cvs[k-1]._t = t;
-
-  return k-1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::append_cv
-//       Access: Public, Scheme
-//  Description: Adds a new CV to the end of the curve.  Creates a new
-//               knot value by adding 1 to the last knot value.
-//               Returns the index of the new CV.
-////////////////////////////////////////////////////////////////////
-int NurbsCurve::
-append_cv(float x, float y, float z) {
-  return append_cv(LVecBase4f(x, y, z, 1.0));
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::remove_cv
-//       Access: Public, Scheme
-//  Description: Removes the indicated CV from the curve.  Returns
-//               true if the CV index was valid, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-remove_cv(int n) {
-  if (n < 0 || n >= (int)_cvs.size()) {
-    return false;
-  }
-
-  _cvs.erase(_cvs.begin() + n);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::remove_all_cvs
-//       Access: Public, Scheme
-//  Description: Removes all CV's from the curve.
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-remove_all_cvs() {
-  _cvs.erase(_cvs.begin(), _cvs.end());
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::set_cv_point
-//       Access: Public, Scheme
-//  Description: Repositions the indicated CV.  Returns true if there
-//               is such a CV, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-set_cv_point(int n, float x, float y, float z) {
-  if (n < 0 || n >= (int)_cvs.size()) {
-    return false;
-  }
-
-  float w = _cvs[n]._p[3];
-  _cvs[n]._p.set(x*w, y*w, z*w, w);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::get_cv_point
-//       Access: Public, Scheme
-//  Description: Returns the position of the indicated CV.
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-get_cv_point(int n, LVecBase3f &v) const {
-  if (n < 0 || n >= (int)_cvs.size()) {
-    v = zero;
-  } else {
-    v = (const LVecBase3f &)_cvs[n]._p / _cvs[n]._p[3];
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::get_cv_point
-//       Access: Public, Scheme
-//  Description: Returns the position of the indicated CV.
-////////////////////////////////////////////////////////////////////
-const LVecBase3f &NurbsCurve::
-get_cv_point(int n) const {
-  if (n < 0 || n >= (int)_cvs.size()) {
-    return zero;
-  } else {
-    static LVecBase3f result;
-    result = (LVecBase3f &)_cvs[n]._p / _cvs[n]._p[3];
-    return result;
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::set_cv_weight
-//       Access: Public, Scheme
-//  Description: Sets the weight of the indicated CV.          
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-set_cv_weight(int n, float w) {
-  if (n < 0 || n >= (int)_cvs.size()) {
-    return false;
-  }
-
-  _cvs[n]._p *= (w / _cvs[n]._p[3]);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::get_cv_weight
-//       Access: Public, Scheme
-//  Description: Returns the weight of the indicated CV.
-////////////////////////////////////////////////////////////////////
-float NurbsCurve::
-get_cv_weight(int n) const {
-  if (n < 0 || n >= (int)_cvs.size()) {
-    return 0.0;
-  }
-
-  return _cvs[n]._p[3];
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::set_knot
-//       Access: Public, Scheme
-//  Description: Sets the value of the indicated knot.  There are
-//               get_num_cvs()+_order-1 knot values, but the first
-//               _order-1 and the last _order-1 knot values cannot be
-//               changed.  It is also an error to set a knot value
-//               outside the range of its neighbors.
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-set_knot(int n, double t) {
-  if (n < _order || n-1 >= (int)_cvs.size()) {
-    return false;
-  }
-
-  _cvs[n-1]._t = t;
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::write
-//       Access: Public, Scheme
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-write(ostream &out, int indent_level) const {
-  indent(out, indent_level);
-
-  switch (get_curve_type()) {
-  case PCT_T:
-    out << "Time-warping ";
-    break;
-
-  case PCT_XYZ:
-    out << "XYZ ";
-    break;
-
-  case PCT_HPR:
-    out << "HPR ";
-    break;
-
-  default:
-    break;
-  }
-
-  out << "NurbsCurve, order " << _order << ", " << get_num_cvs()
-      << " CV's.  t ranges from 0 to " << get_max_t() << ".\n";
-
-  indent(out, indent_level)
-    << "CV's:\n";
-  int i;
-  for (i = 0; i < (int)_cvs.size(); i++) {
-    LVecBase3f p = (const LVecBase3f &)_cvs[i]._p / _cvs[i]._p[3];
-    indent(out, indent_level)
-      << i << ") " << p << ", weight " << _cvs[i]._p[3] << "\n";
-  }
-
-  indent(out, indent_level)
-    << "Knots: ";
-  for (i = 0; i < (int)_cvs.size()+_order; i++) {
-    out << " " << get_knot(i);
-  }
-  out << "\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::write_cv
-//       Access: Public, Scheme
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-write_cv(ostream &out, int n) const {
-  if (n < 0 || n >= (int)_cvs.size()) {
-    out << "No such CV: " << n << "\n";
-  } else {
-    LVecBase3f p = (const LVecBase3f &)_cvs[n]._p / _cvs[n]._p[3];
-    out << "CV " << n << ": " << p << ", weight " 
-	<< _cvs[n]._p[3] << "\n";
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::recompute
-//       Access: Public, Scheme
-//  Description: Recalculates the curve basis according to the latest
-//               position of the CV's, knots, etc.  Until this
-//               function is called, adjusting the NURBS parameters
-//               will have no visible effect on the curve.  Returns
-//               true if the resulting curve is valid, false
-//               otherwise.
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-recompute() {
-  _segs.erase(_segs.begin(), _segs.end());
-
-  double knots[8];
-  LVecBase4f cvs[4];
-
-  if ((int)_cvs.size() > _order-1) {
-    for (int cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
-      if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
-	// There are _order consecutive CV's that define each segment,
-	// beginning at cv.  Collect the CV's and knot values that define
-	// this segment.
-	int c;
-	for (c = 0; c < _order; c++) {
-	  cvs[c] = _cvs[c+cv]._p;
-	}
-	for (c = 0; c < _order+_order; c++) {
-	  knots[c] = get_knot(c+cv);
-	}
-	
-	insert_curveseg(_segs.size(), new CubicCurveseg(_order, knots, cvs),
-			knots[_order] - knots[_order-1]);
-      }
-    }
-  }
-
-  return !_segs.empty();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::normalize_tlength
-//       Access: Public, Scheme
-//  Description: Recomputes the knot vector so that the curve is
-//               nearly uniformly spaced in parametric time.  That is,
-//               calc_length(0, t) == t (approximately) for all t in
-//               [0, get_max_t()].  This is only an approximation; its
-//               precision depends on the placement of the knots.
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-normalize_tlength() {
-  int num_knots = _cvs.size() + _order;
-  double *new_t = (double *)alloca(sizeof(double) * num_knots);
-
-  int i;
-  double last_t = 0.0;
-  double last_new_t = 0.0;
-  for (i = 0; i < num_knots; i++) {
-    double this_t = get_knot(i);
-
-    if (this_t == last_t) {
-
-      // Keep the same knot value.
-      new_t[i] = last_new_t;
-
-    } else {
-      // Compute a new knot value that represents the distance on the
-      // curve since the last knot.
-      last_new_t += calc_length(last_t, this_t);
-      new_t[i] = last_new_t;
-      last_t = this_t;
-    }
-  }
-
-  // Now set all the knot values at once.
-  for (i = 0; i < num_knots; i++) {
-    set_knot(i, new_t[i]);
-  }
-}
-
-
-#if 0
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::adjust_pt
-//       Access: Public, Scheme
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-adjust_pt(double t, 
-	  float px, float py, float pz,
-	  float tx, float ty, float tz) {
-  const ParametricCurve *curve;
-  bool result = find_curve(curve, t);
-
-  if (!result) {
-    cerr << "  no curve segment at t\n";
-    return;
-  }
-
-  // Figure out which CV's contributed to this segment.
-  int seg = 0;
-
-  dnassert(_cvs.size() > _order-1);
-
-  int cv = 0;
-  for (cv = 0; cv < _cvs.size()-(_order-1); cv++) {
-    if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
-      if (seg == _last_ti) {
-	break;
-      }
-      seg++;
-    }
-  }
-
-  // Now copy the cvs and knots in question.
-  double knots[8];
-  LVecBase4f cvs[4];
-
-  int c;
-  for (c = 0; c < 4; c++) {
-    cvs[c] = (c < _order) ? _cvs[c+cv]._p : LVecBase4f(0.0, 0.0, 0.0, 0.0);
-  }
-  for (c = 0; c < _order+_order; c++) {
-    knots[c] = get_knot(c+cv);
-  }
-
-  dnassert(_order>=1 && _order<=4);
-
-  LMatrix4f B;
-  compute_nurbs_basis(_order, knots, B);
-
-  LMatrix4f Bi;
-  if (!Bi.invertFull(B)) {
-    cerr << "Cannot invert B!\n";
-    return;
-  }
-
-  // We can rebuild a curve segment given four arbitrary properties of
-  // the segment: any point along the curve, any tangent along the
-  // curve, any control point.  Given any four such properties, a
-  // single cubic curve segment is defined.
-
-  // We now want to rebuild the curve segment with the following four
-  // properties: the same first control point, the same last control
-  // point, and the supplied point and tangent at the indicated value
-  // of t.  To do this, we build a matrix T such that:
-
-  // Column 0 of T is the same as column 0 of B^(-1)
-  //   This refers to the first control point.
-  // Column 1 of T is the vector [ t^3 t^2 t 1 ]
-  //   This refers to the point on the curve at t.
-  // Column 2 of T is the vector [ 3t^2 2t 1 0 ]
-  //   This refers to the tangent to the curve at t.
-  // Column 3 of T is the same as column 3 of B^(-1)
-  //   This refers to the last control point.
-
-  LMatrix4f T = Bi;
-  T.setCol(1, t*t*t, t*t, t, 1.0);
-  T.setCol(2, 3.0*t*t, 2.0*t, 1.0, 0.0);
-
-  LMatrix4f Ti;
-  if (!Ti.invertFull(T)) {
-    cerr << "Cannot invert T!\n";
-  }
-
-  // Now we build the matrix P such that P represents the solution of
-  // T, above, when T is applied to the geometry and basis matrices.
-  // That is, P = G * B * T.
-
-  // Column 0 of P is the first control point.
-  // Column 1 of P is the (new) desired point on the curve at t.
-  // Column 2 of P is the (new) desired tangent to the curve at t.
-  // Column 3 of P is the last control point.
-
-  LMatrix4f P;
-  
-  P.setCol(0, cvs[0][0], cvs[0][1], cvs[0][2], cvs[0][3]);
-  P.setCol(1, px, py, pz, 1.0);
-  P.setCol(2, tx, ty, tz, 0.0);
-  P.setCol(3, cvs[3][0], cvs[3][1], cvs[3][2], cvs[3][3]);
-
-  // Now we simply solve for G to get G = P * T^(-1) * B^(-1).
-
-  LMatrix4f G = P * Ti * Bi;
-
-  // Now extract the new CV's from the new G matrix, and restore them
-  // to the curve.
-  for (c = 0; c < _order; c++) {
-    LVecBase4f &s = _cvs[c+cv]._p;
-    G.getCol(c, &s[0], &s[1], &s[2], &s[3]);
-  }
-}
-#endif 
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::rebuild_curveseg
-//       Access: Public, Virtual
-//  Description: Rebuilds the current curve segment (as selected by
-//               the most recent call to find_curve()) according to
-//               the specified properties (see
-//               CubicCurveseg::compute_seg).  Returns true if
-//               possible, false if something goes horribly wrong.
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-rebuild_curveseg(int rtype0, double t0, const LVecBase4f &v0,
-		 int rtype1, double t1, const LVecBase4f &v1,
-		 int rtype2, double t2, const LVecBase4f &v2,
-		 int rtype3, double t3, const LVecBase4f &v3) {
-  // Figure out which CV's contributed to this segment.
-  int seg = 0;
-
-  nassertr((int)_cvs.size() > _order-1, false);
-
-  int cv = 0;
-  for (cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
-    if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
-      if (seg == _last_ti) {
-	break;
-      }
-      seg++;
-    }
-  }
-
-  // Now copy the cvs and knots in question.
-  LMatrix4f G;
-  double knots[8];
-
-  int c;
-
-  // We only need to build the geometry matrix if at least one of the
-  // properties depends on the original value.
-  if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
-    for (c = 0; c < 4; c++) {
-      static const LVecBase4f zero(0.0, 0.0, 0.0, 0.0);
-      const LVecBase4f &s = (c < _order) ? _cvs[c+cv]._p : zero;
-      
-      G.set_col(c, s);
-    }
-  }
-
-  // But we always need the knot vector to determine the basis matrix.
-  for (c = 0; c < _order+_order; c++) {
-    knots[c] = get_knot(c+cv);
-  }
-
-  LMatrix4f B;
-  compute_nurbs_basis(_order, knots, B);
-
-  LMatrix4f Bi;
-  Bi = invert(B);
-
-  if (!CubicCurveseg::compute_seg(rtype0, t0, v0,
-				  rtype1, t1, v1,
-				  rtype2, t2, v2,
-				  rtype3, t3, v3,
-				  B, Bi, G)) {
-    return false;
-  }
-
-  // Now extract the new CV's from the new G matrix, and restore them
-  // to the curve.
-  for (c = 0; c < _order; c++) {
-    _cvs[c+cv]._p = G.get_col(c);
-  }
-
-  return true;
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::write_egg
-//       Access: Public, Scheme
-//  Description: Writes an egg description of the nurbs curve to the
-//               specified output file.  Creates the file if it does
-//               not exist; appends to the end of it if it does.
-//               Returns true if the file is successfully written.
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-write_egg(const char *filename, CoordinateSystem cs) {
-  const char *basename = strrchr(filename, '/');
-  basename = (basename==NULL) ? filename : basename+1;
-
-  ofstream out(filename, ios::app);
-  return write_egg(out, basename, cs);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::write_egg
-//       Access: Public, Scheme
-//  Description: Writes an egg description of the nurbs curve to the
-//               specified output stream.  Returns true if the file is
-//               successfully written.
-////////////////////////////////////////////////////////////////////
-bool NurbsCurve::
-write_egg(ostream &out, const char *basename, CoordinateSystem cs) {
-  if (get_name().empty()) {
-    // If we don't have a name, come up with one.
-    int len = strlen(basename);
-    if (len>4 && strcmp(basename+len-4, ".egg")==0) {
-      len -= 4;
-    }
-
-    char *name = (char *)alloca(len + 5);
-    strncpy(name, basename, len);
-    switch (_curve_type) {
-    case PCT_XYZ:
-      strcpy(name+len, "_xyz");
-      break;
-
-    case PCT_HPR:
-      strcpy(name+len, "_hpr");
-      break;
-
-    case PCT_T:
-      strcpy(name+len, "_t");
-      break;
-      
-    default:
-      name[len] = '\0';
-    };
-
-    set_name(name);
-  }
-
-  format_egg(out, cs, 0);
-
-  if (out) {
-    return true;
-  } else {
-    return false;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::splice
-//       Access: Public, Scheme
-//  Description: Joins the indicated curve onto the end of this curve,
-//               beginning at the indicated time (which must be
-//               greater than or equal to max_t).  Normally, the first
-//               point of the new curve will be the same as the last
-//               point of this curve, but if they are different, the
-//               curves will automatically connect; however, the
-//               connection may not be smooth and terminal point of
-//               the original curve may be lost.
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-splice(double t, const NurbsCurve &other) {
-  if (other._order != _order) {
-    parametrics_cat->warning()
-      << "Cannot splice NURBS curves of different orders!" << endl;
-    return;
-  }
-
-  double old_t = get_max_t();
-
-  if (t < old_t) {
-    parametrics_cat->warning()
-      << "Invalid splicing in the middle of a curve!" << endl;
-    t = old_t;
-  }
-
-  // First, define a vector of all the current CV's except the last
-  // one.
-  vector<CV> new_cvs(_cvs);
-  if (!new_cvs.empty()) {
-    new_cvs.pop_back();
-  }
-
-  // Now add all the new CV's.
-  int cv;
-  for (cv = 0; cv < (int)other._cvs.size(); cv++) {
-    CV new_cv(other._cvs[cv]);
-
-    if (cv+1 < _order) {
-      new_cv._t = old_t;
-    } else {
-      new_cv._t += t;
-    }
-    new_cvs.push_back(new_cv);
-  }
-
-  // Now assign that vector.
-  _cvs = new_cvs;
-
-  recompute();
-}
-
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::format_egg
-//       Access: Public
-//  Description: Formats the Nurbs curve for output to an Egg file.
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-format_egg(ostream &out, CoordinateSystem cs, int indent_level) const {
-  if (cs == CS_default) {
-    cs = default_coordinate_system;
-  }
-
-  if (cs != CS_invalid) {
-    indent(out, indent_level)
-      << "<CoordinateSystem> { ";
-    switch (cs) {
-    case CS_zup_right:
-      out << "Z-Up";
-      break;
-      
-    case CS_yup_right:
-      out << "Y-Up";
-      break;
-      
-    case CS_zup_left:
-      out << "Z-Up-Left";
-      break;
-      
-    case CS_yup_left:
-      out << "Y-Up-Left";
-      break;
-
-    default:
-      break;
-    }
-    out << " }\n\n";
-  }
-
-  indent(out, indent_level)
-    << "<VertexPool> " << get_name() << ".pool {\n";
-
-  int cv;
-  for (cv = 0; cv < (int)_cvs.size(); cv++) {
-    indent(out, indent_level+2)
-      << "<Vertex> " << cv << " { " << _cvs[cv]._p << " }\n";
-  }
-  indent(out, indent_level)
-    << "}\n";
-    
-  indent(out, indent_level)
-    << "<NURBSCurve> " << get_name() << " {\n";
-
-  if (_curve_type!=PCT_NONE) {
-    indent(out, indent_level+2)
-      << "<Char*> type { ";
-    switch (_curve_type) {
-    case PCT_XYZ:
-      out << "XYZ";
-      break;
-
-    case PCT_HPR:
-      out << "HPR";
-      break;
-
-    case PCT_T:
-      out << "T";
-      break;
-    };
-    out << " }\n";
-  }
-
-  indent(out, indent_level+2) << "<Order> { " << _order << " }\n";
-  
-  indent(out, indent_level+2) << "<Knots> {";
-  int k;
-  int num_knots = _cvs.size() + _order;
-
-  for (k = 0; k < num_knots; k++) {
-    if (k%6 == 0) {
-      out << "\n";
-      indent(out, indent_level+4);
-    }
-    out << get_knot(k) << " ";
-  }
-  out << "\n";
-  indent(out, indent_level+2) << "}\n";
-
-  indent(out, indent_level+2) << "<VertexRef> {";
-  for (cv = 0; cv < (int)_cvs.size(); cv++) {
-    if (cv%10 == 0) {
-      out << "\n";
-      indent(out, indent_level+3);
-    }
-    out << " " << cv;
-  }
-  out << "\n";
-  indent(out, indent_level+4)
-    << "<Ref> { " << get_name() << ".pool }\n";
-  indent(out, indent_level+2) << "}\n";
-
-  indent(out, indent_level) << "}\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::find_cv
-//       Access: Protected
-//  Description: Finds the first knot whose value is >= t, or -1 if t
-//               is beyond the end of the curve.
-////////////////////////////////////////////////////////////////////
-int NurbsCurve::
-find_cv(double t) {
-  int i;
-  for (i = _order-1; i < (int)_cvs.size(); i++) {
-    if (_cvs[i]._t >= t) {
-      return i+1;
-    }
-  }
-
-  return -1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::register_with_factory
-//       Access: Public, Static
-//  Description: Initializes the factory for reading these things from
-//               Bam files.
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-register_with_read_factory() {
-  BamReader::get_factory()->register_factory(get_class_type(), make_NurbsCurve);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::make_NurbsCurve
-//       Access: Protected
-//  Description: Factory method to generate an object of this type.
-////////////////////////////////////////////////////////////////////
-TypedWriteable *NurbsCurve::
-make_NurbsCurve(const FactoryParams &params) {
-  NurbsCurve *me = new NurbsCurve;
-  BamReader *manager;
-  Datagram packet;
-
-  parse_params(params, manager, packet);
-  DatagramIterator scan(packet);
-
-  me->fillin(scan, manager);
-  return me;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::write_datagram
-//       Access: Protected, Virtual
-//  Description: Function to write the important information in
-//               the particular object to a Datagram
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-write_datagram(BamWriter *manager, Datagram &me) {
-  PiecewiseCurve::write_datagram(manager, me);
-
-  me.add_int8(_order);
-
-  me.add_uint32(_cvs.size());
-  size_t i;
-  for (i = 0; i < _cvs.size(); i++) {
-    const CV &cv = _cvs[i];
-    cv._p.write_datagram(me);
-    me.add_float64(cv._t);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: NurbsCurve::fillin
-//       Access: Protected
-//  Description: Function that reads out of the datagram (or asks
-//               manager to read) all of the data that is needed to
-//               re-create this object and stores it in the appropiate
-//               place
-////////////////////////////////////////////////////////////////////
-void NurbsCurve::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  PiecewiseCurve::fillin(scan, manager);
-
-  _order = scan.get_int8();
-
-  size_t num_cvs = scan.get_uint32();
-
-  _cvs.reserve(num_cvs);
-  size_t i;
-  for (i = 0; i < num_cvs; i++) {
-    CV cv;
-    cv._p.read_datagram(scan);
-    cv._t = scan.get_float64();
-    _cvs.push_back(cv);
-  }
-}

+ 0 - 169
panda/src/parametrics/nurbsCurve.h

@@ -1,169 +0,0 @@
-// Filename: nurbsCurve.h
-// Created by:  drose (27Feb98)
-// 
-////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
-#ifndef NURBSCURVE_H
-#define NURBSCURVE_H
-
-////////////////////////////////////////////////////////////////////
-// Includes 
-////////////////////////////////////////////////////////////////////
-#include "curve.h"
-
-////////////////////////////////////////////////////////////////////
-// Defines 
-////////////////////////////////////////////////////////////////////
-
-////#define LVecBase3f pfVec3
-//typedef pfVec3 LVecBase3f;
-
-
-class HermiteCurve;
-
-////////////////////////////////////////////////////////////////////
-// 	 Class : NurbsCurve
-// Description : A Nonuniform Rational B-Spline.
-//
-//               This class is actually implemented as a
-//               PiecewiseCurve made up of several CubicCurvesegs,
-//               each of which is created using the nurbs_basis()
-//               method.  The list of CV's and knots is kept here,
-//               within the NurbsCurve class.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA NurbsCurve : public PiecewiseCurve {
-
-////////////////////////////////////////////////////////////////////
-// Member functions visible to Scheme
-////////////////////////////////////////////////////////////////////
-
-PUBLISHED:
-  NurbsCurve();
-  NurbsCurve(const ParametricCurve &hc);
-  NurbsCurve(int order, int num_cvs,
-	     const double knots[], const LVecBase4f cvs[]);
-  virtual ~NurbsCurve();
-
-  void set_order(int order);
-  int get_order() const;
-
-  int get_num_cvs() const;
-  int get_num_knots() const {
-    return _cvs.size() + _order;
-  }
-
-  int insert_cv(double t);
-  int append_cv(float x, float y, float z);
-  inline int append_cv(const LVecBase3f &v) {
-    return append_cv(LVecBase4f(v[0], v[1], v[2], 1.0));
-  }
-  inline int append_cv(const LVecBase4f &v) {
-    _cvs.push_back(CV(v, get_knot(_cvs.size())+1.0));
-    return _cvs.size()-1;
-  }
-
-  bool remove_cv(int n);
-  void remove_all_cvs();
-
-  bool set_cv_point(int n, float x, float y, float z);
-  inline bool set_cv_point(int n, const LVecBase3f &v) {
-    return set_cv_point(n, v[0], v[1], v[2]);
-  }
-  void get_cv_point(int n, LVecBase3f &v) const;
-  const LVecBase3f &get_cv_point(int n) const;
-
-  bool set_cv_weight(int n, float w);
-  float get_cv_weight(int n) const;
-
-  bool set_knot(int n, double t);
-  double get_knot(int n) const {
-    if (n < _order || _cvs.empty()) {
-      return 0.0;
-    } else if (n-1 >= (int)_cvs.size()) {
-      return _cvs.back()._t;
-    } else {
-      return _cvs[n-1]._t;
-    }
-  }
-
-  virtual void write(ostream &out, int indent_level = 0) const;
-  void write_cv(ostream &out, int n) const;
-
-  bool recompute();
-
-  void normalize_tlength();
-
-  bool write_egg(const char *filename, CoordinateSystem cs = CS_default);
-  bool write_egg(ostream &out, const char *basename, CoordinateSystem cs);
-
-  void splice(double t, const NurbsCurve &other);
-  
-////////////////////////////////////////////////////////////////////
-// Member functions not visible to Scheme
-////////////////////////////////////////////////////////////////////
-public:
-  virtual bool
-  rebuild_curveseg(int rtype0, double t0, const LVecBase4f &v0,
-		   int rtype1, double t1, const LVecBase4f &v1,
-		   int rtype2, double t2, const LVecBase4f &v2,
-		   int rtype3, double t3, const LVecBase4f &v3);
-
-  CubicCurveseg *get_curveseg(int ti) {
-    return (CubicCurveseg *)PiecewiseCurve::get_curveseg(ti);
-  }
-
-  void format_egg(ostream &out, CoordinateSystem cs, int indent_level) const;
-
-protected:
-  int find_cv(double t);
-
-  int _order;
-
-  class CV {
-  public:
-    CV() {}
-    CV(const LVecBase4f &p, double t) : _p(p), _t(t) {}
-    LVecBase4f _p;
-    double _t;
-  };
-
-  vector<CV> _cvs;
-
-
-// TypedWriteable stuff
-public:
-  static void register_with_read_factory();
-
-protected:
-  static TypedWriteable *make_NurbsCurve(const FactoryParams &params);
-  virtual void write_datagram(BamWriter *manager, Datagram &me);  
-  void fillin(DatagramIterator &scan, BamReader *manager);
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    PiecewiseCurve::init_type();
-    register_type(_type_handle, "NurbsCurve",
-                  PiecewiseCurve::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
- 
-private:
-  static TypeHandle _type_handle;
-};
-
-#endif

+ 31 - 26
panda/src/parametrics/nurbsCurveDrawer.cxx

@@ -17,6 +17,8 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "nurbsCurveDrawer.h"
 #include "nurbsCurveDrawer.h"
+#include "nurbsCurveInterface.h"
+#include "parametricCurve.h"
 
 
 
 
 TypeHandle NurbsCurveDrawer::_type_handle;
 TypeHandle NurbsCurveDrawer::_type_handle;
@@ -28,7 +30,7 @@ TypeHandle NurbsCurveDrawer::_type_handle;
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NurbsCurveDrawer::
 NurbsCurveDrawer::
-NurbsCurveDrawer(NurbsCurve *curve) : ParametricCurveDrawer(curve) {
+NurbsCurveDrawer() {
   set_cv_color(1.0, 0.0, 0.0);
   set_cv_color(1.0, 0.0, 0.0);
   set_hull_color(1.0, 0.5, 0.5);
   set_hull_color(1.0, 0.5, 0.5);
   set_knot_color(0.0, 0.0, 1.0);
   set_knot_color(0.0, 0.0, 1.0);
@@ -108,29 +110,45 @@ set_hull_color(float r, float g, float b) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool NurbsCurveDrawer::
 bool NurbsCurveDrawer::
 draw() {
 draw() {
-  NurbsCurve *nurbs = (NurbsCurve *)_curve;
-  // Make sure the curve is fresh.
-  nurbs->recompute();
+  ParametricCurve *curve = (ParametricCurve *)NULL;
+  NurbsCurveInterface *nurbs = (NurbsCurveInterface *)NULL;
+
+  if (_curves != (ParametricCurveCollection *)NULL) {
+    curve = _curves->get_default_curve();
+  }
+
+  if (curve != (ParametricCurve *)NULL) {
+    nurbs = curve->get_nurbs_interface();
+    if (nurbs != (NurbsCurveInterface *)NULL) {
+      // Make sure the curve is fresh.
+      nurbs->recompute();
+    }
+  }
 
 
   // First, draw the curve itself.
   // First, draw the curve itself.
   if (!ParametricCurveDrawer::draw()) {
   if (!ParametricCurveDrawer::draw()) {
     return false;
     return false;
   }
   }
 
 
+  if (nurbs == (NurbsCurveInterface *)NULL) {
+    // The rest of this depends on having an actual NURBS curve.
+    return true;
+  }
+
   int i;
   int i;
   if (_show_knots) {
   if (_show_knots) {
     _num_cvs = nurbs->get_num_cvs();
     _num_cvs = nurbs->get_num_cvs();
     _knotnums.erase(_knotnums.begin(), _knotnums.end());
     _knotnums.erase(_knotnums.begin(), _knotnums.end());
 
 
-    double lt = -1.0;
+    float lt = -1.0;
     int ki = -1;
     int ki = -1;
     for (i = 0; i < _num_cvs; i++) {
     for (i = 0; i < _num_cvs; i++) {
-      double t = nurbs->get_knot(i);
+      float t = nurbs->get_knot(i);
       if (t != lt) {
       if (t != lt) {
 	lt = t;
 	lt = t;
-	LVecBase3f knot_pos, knot_tan;
-	nurbs->get_pt(nurbs->get_knot(i), knot_pos, knot_tan);
-	_knots.move_to(_mapper(knot_pos, knot_tan, t));
+	LVecBase3f knot_pos;
+	curve->get_point(nurbs->get_knot(i), knot_pos);
+	_knots.move_to(knot_pos);
 	ki++;
 	ki++;
       }
       }
       _knotnums.push_back(ki);
       _knotnums.push_back(ki);
@@ -142,8 +160,7 @@ draw() {
   if (_show_cvs) {
   if (_show_cvs) {
     _num_cvs = nurbs->get_num_cvs();
     _num_cvs = nurbs->get_num_cvs();
     for (i = 0; i < _num_cvs; i++) {
     for (i = 0; i < _num_cvs; i++) {
-      _cvs.move_to(_mapper(nurbs->get_cv_point(i), LVecBase3f(0.0, 0.0, 0.0),
-			   nurbs->get_knot(i+1)));
+      _cvs.move_to(nurbs->get_cv_point(i));
     }
     }
 
 
     _cvs.create(_geom_node, _frame_accurate);
     _cvs.create(_geom_node, _frame_accurate);
@@ -152,8 +169,7 @@ draw() {
   if (_show_hull) {
   if (_show_hull) {
     _num_cvs = nurbs->get_num_cvs();
     _num_cvs = nurbs->get_num_cvs();
     for (i = 0; i < _num_cvs; i++) {
     for (i = 0; i < _num_cvs; i++) {
-      _hull.draw_to(_mapper(nurbs->get_cv_point(i), LVecBase3f(0.0, 0.0, 0.0),
-			    nurbs->get_knot(i+1)));
+      _hull.draw_to(nurbs->get_cv_point(i));
     }
     }
 
 
     _hull.create(_geom_node, _frame_accurate);
     _hull.create(_geom_node, _frame_accurate);
@@ -170,7 +186,7 @@ draw() {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool NurbsCurveDrawer::
 bool NurbsCurveDrawer::
-recompute(double t1, double t2, ParametricCurve *curve) {
+recompute(float t1, float t2, ParametricCurve *curve) {
   return draw();
   return draw();
 }
 }
 
 
@@ -254,17 +270,11 @@ get_show_knots() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool NurbsCurveDrawer::
 bool NurbsCurveDrawer::
 hilight(int n, float hr, float hg, float hb) {
 hilight(int n, float hr, float hg, float hb) {
-  // If there's no curve, do nothing and return false.
-  if (_curve==NULL || !_curve->is_valid()) {
-    return false;
-  }
-
   if (n < 0 || n >= _cvs.get_num_vertices()) {
   if (n < 0 || n >= _cvs.get_num_vertices()) {
-    // Also return false if we're out of range.
+    // Return false if we're out of range.
     return false;
     return false;
   }
   }
 
 
-  //  NurbsCurve *nurbs = (NurbsCurve *)_curve;
   if (_show_cvs) {
   if (_show_cvs) {
     _cvs.set_vertex_color(n, hr, hg, hb);
     _cvs.set_vertex_color(n, hr, hg, hb);
   }
   }
@@ -284,15 +294,10 @@ hilight(int n, float hr, float hg, float hb) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool NurbsCurveDrawer::
 bool NurbsCurveDrawer::
 unhilight(int n) {
 unhilight(int n) {
-  if (_curve==NULL || !_curve->is_valid()) {
-    return false;
-  }
-
   if (n < 0 || n >= _cvs.get_num_vertices()) {
   if (n < 0 || n >= _cvs.get_num_vertices()) {
     return false;
     return false;
   }
   }
 
 
-  //  NurbsCurve *nurbs = (NurbsCurve *)_curve;
   if (_show_cvs) {
   if (_show_cvs) {
     _cvs.set_vertex_color(n, _cv_color[0], _cv_color[1], _cv_color[2]);
     _cvs.set_vertex_color(n, _cv_color[0], _cv_color[1], _cv_color[2]);
   }
   }

+ 6 - 31
panda/src/parametrics/nurbsCurveDrawer.h

@@ -2,31 +2,14 @@
 // Created by:  drose (27Feb98)
 // Created by:  drose (27Feb98)
 // 
 // 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
-// 
-// These  coded  instructions,  statements,  data   structures   and
-// computer  programs contain unpublished proprietary information of
-// Walt Disney Imagineering and are protected by  Federal  copyright
-// law.  They may  not be  disclosed to third  parties  or copied or
-// duplicated in any form, in whole or in part,  without  the  prior
-// written consent of Walt Disney Imagineering Inc.
-////////////////////////////////////////////////////////////////////
-//
+
 #ifndef NURBSCURVEDRAWER_H
 #ifndef NURBSCURVEDRAWER_H
 #define NURBSCURVEDRAWER_H
 #define NURBSCURVEDRAWER_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes 
-////////////////////////////////////////////////////////////////////
-
-#include "curveDrawer.h"
-#include "nurbsCurve.h"
-#include "lineSegs.h"
 
 
+#include <pandabase.h>
 
 
-////////////////////////////////////////////////////////////////////
-// Defines 
-////////////////////////////////////////////////////////////////////
+#include "parametricCurveDrawer.h"
+#include "lineSegs.h"
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -35,13 +18,8 @@
 //               vertices and tangent vectors.
 //               vertices and tangent vectors.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA NurbsCurveDrawer : public ParametricCurveDrawer {
 class EXPCL_PANDA NurbsCurveDrawer : public ParametricCurveDrawer {
-
-////////////////////////////////////////////////////////////////////
-// Member functions visible to Scheme
-////////////////////////////////////////////////////////////////////
-
 PUBLISHED:
 PUBLISHED:
-  NurbsCurveDrawer(NurbsCurve *curve);
+  NurbsCurveDrawer();
   virtual ~NurbsCurveDrawer();
   virtual ~NurbsCurveDrawer();
 
 
   void set_cv_color(float r, float g, float b);
   void set_cv_color(float r, float g, float b);
@@ -49,7 +27,7 @@ PUBLISHED:
   void set_knot_color(float r, float g, float b);
   void set_knot_color(float r, float g, float b);
 
 
   virtual bool draw();
   virtual bool draw();
-  virtual bool recompute(double t1, double t2, ParametricCurve *curve=NULL);
+  virtual bool recompute(float t1, float t2, ParametricCurve *curve=NULL);
 
 
   void set_show_cvs(bool flag);
   void set_show_cvs(bool flag);
   bool get_show_cvs() const;
   bool get_show_cvs() const;
@@ -61,9 +39,6 @@ PUBLISHED:
   bool hilight(int n, float hr=1.0, float hg=1.0, float hb=0.0);
   bool hilight(int n, float hr=1.0, float hg=1.0, float hb=0.0);
   bool unhilight(int n);
   bool unhilight(int n);
 
 
-////////////////////////////////////////////////////////////////////
-// Member functions not visible to Scheme
-////////////////////////////////////////////////////////////////////
 protected:
 protected:
   LVecBase3f _cv_color, _hull_color, _knot_color;
   LVecBase3f _cv_color, _hull_color, _knot_color;
   int _num_cvs, _num_hull, _num_knots;
   int _num_cvs, _num_hull, _num_knots;

+ 81 - 0
panda/src/parametrics/nurbsCurveInterface.I

@@ -0,0 +1,81 @@
+// Filename: nurbsCurveInterface.I
+// Created by:  drose (02Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::append_cv
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int NurbsCurveInterface::
+append_cv(float x, float y, float z) {
+  return append_cv(LVecBase3f(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::append_cv
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int NurbsCurveInterface::
+append_cv(const LVecBase3f &v) {
+  return append_cv(LVecBase4f(v[0], v[1], v[2], 1.0));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::append_cv
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int NurbsCurveInterface::
+append_cv(const LVecBase4f &v) {
+  return append_cv_impl(v);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::set_cv_point
+//       Access: Public, Scheme
+//  Description: Repositions the indicated CV.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool NurbsCurveInterface::
+set_cv_point(int n, float x, float y, float z) {
+  return set_cv_point(n, LVecBase3f(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::set_cv_point
+//       Access: Public, Scheme
+//  Description: Repositions the indicated CV.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool NurbsCurveInterface::
+set_cv_point(int n, const LVecBase3f &v) {
+  nassertr(n >= 0 && n < get_num_cvs(), false);
+  return set_cv(n, LVecBase4f(v[0], v[1], v[2], 1.0) * get_cv_weight(n));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::get_cv_point
+//       Access: Public, Scheme
+//  Description: Returns the position of the indicated CV.
+////////////////////////////////////////////////////////////////////
+INLINE LVecBase3f NurbsCurveInterface::
+get_cv_point(int n) const {
+  nassertr(n >= 0 && n < get_num_cvs(), LVecBase3f::zero());
+  LVecBase4f p = get_cv(n);
+  nassertr(p[3] != 0.0, LVecBase3f::zero());
+  return LVecBase3f(p[0], p[1], p[2]) / p[3];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::get_cv_weight
+//       Access: Published
+//  Description: Returns the weight of the indicated CV.
+////////////////////////////////////////////////////////////////////
+INLINE float NurbsCurveInterface::
+get_cv_weight(int n) const {
+  return get_cv(n)[3];
+}

+ 172 - 0
panda/src/parametrics/nurbsCurveInterface.cxx

@@ -0,0 +1,172 @@
+// Filename: nurbsCurveInterface.cxx
+// Created by:  drose (02Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "nurbsCurveInterface.h"
+#include "parametricCurve.h"
+#include "config_parametrics.h"
+
+TypeHandle NurbsCurveInterface::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::set_cv_weight
+//       Access: Published
+//  Description: Sets the weight of the indicated CV without affecting
+//               its position in 3-d space.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveInterface::
+set_cv_weight(int n, float w) {
+  nassertr(n >= 0 && n < get_num_cvs(), false);
+  LVecBase4f cv = get_cv(n);
+  if (cv[3] == 0.0) {
+    cv.set(0.0, 0.0, 0.0, w);
+  } else {
+    cv *= w / cv[3];
+  }
+  return set_cv(n, cv);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::write_cv
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void NurbsCurveInterface::
+write_cv(ostream &out, int n) const {
+  nassertv(n >= 0 && n < get_num_cvs());
+
+  out << "CV " << n << ": " << get_cv_point(n) << ", weight " 
+      << get_cv_weight(n) << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::write
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void NurbsCurveInterface::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level);
+
+  float min_t = 0.0;
+  float max_t = 0.0;
+
+  if (get_num_knots() > 0) {
+    min_t = get_knot(0);
+    max_t = get_knot(get_num_knots() - 1);
+  }
+
+  out << "NurbsCurve, order " << get_order() << ", " << get_num_cvs()
+      << " CV's.  t ranges from " << min_t << " to " << max_t << ".\n";
+
+  indent(out, indent_level)
+    << "CV's:\n";
+  int i;
+  int num_cvs = get_num_cvs();
+  for (i = 0; i < num_cvs; i++) {
+    indent(out, indent_level)
+      << i << ") " << get_cv_point(i) << ", weight " 
+      << get_cv_weight(i) << "\n";
+  }
+
+  indent(out, indent_level)
+    << "Knots: ";
+  int num_knots = get_num_knots();
+  for (i = 0; i < num_knots; i++) {
+    out << " " << get_knot(i);
+  }
+  out << "\n";
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::format_egg
+//       Access: Protected
+//  Description: Formats the Nurbs curve for output to an Egg file.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveInterface::
+format_egg(ostream &out, const string &name, const string &curve_type,
+	   int indent_level) const {
+  indent(out, indent_level)
+    << "<VertexPool> " << name << ".pool {\n";
+
+  int num_cvs = get_num_cvs();
+  int cv;
+  for (cv = 0; cv < get_num_cvs(); cv++) {
+    indent(out, indent_level+2)
+      << "<Vertex> " << cv << " { " << get_cv(cv) << " }\n";
+  }
+  indent(out, indent_level)
+    << "}\n";
+    
+  indent(out, indent_level)
+    << "<NurbsCurve> " << name << " {\n";
+
+  if (!curve_type.empty()) {
+    indent(out, indent_level+2)
+      << "<Scalar> type { " << curve_type << " }\n";
+  }
+
+  indent(out, indent_level+2) << "<Order> { " << get_order() << " }\n";
+  
+  indent(out, indent_level+2) << "<Knots> {";
+  int k;
+  int num_knots = get_num_knots();
+
+  for (k = 0; k < num_knots; k++) {
+    if (k%6 == 0) {
+      out << "\n";
+      indent(out, indent_level+4);
+    }
+    out << get_knot(k) << " ";
+  }
+  out << "\n";
+  indent(out, indent_level+2) << "}\n";
+
+  indent(out, indent_level+2) << "<VertexRef> {";
+  for (cv = 0; cv < num_cvs; cv++) {
+    if (cv%10 == 0) {
+      out << "\n";
+      indent(out, indent_level+3);
+    }
+    out << " " << cv;
+  }
+  out << "\n";
+  indent(out, indent_level+4)
+    << "<Ref> { " << name << ".pool }\n";
+  indent(out, indent_level+2) << "}\n";
+
+  indent(out, indent_level) << "}\n";
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveInterface::convert_to_nurbs
+//       Access: Protected
+//  Description: Stores in the indicated NurbsCurve a NURBS
+//               representation of an equivalent curve.  Returns true
+//               if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveInterface::
+convert_to_nurbs(ParametricCurve *nc) const {
+  NurbsCurveInterface *nurbs = nc->get_nurbs_interface();
+  nassertr(nurbs != (NurbsCurveInterface *)NULL, false);
+
+  nurbs->remove_all_cvs();
+  nurbs->set_order(get_order());
+
+  int num_cvs = get_num_cvs();
+  int i;
+  for (i = 0; i < num_cvs; i++) {
+    nurbs->append_cv(get_cv(i));
+  }
+
+  int num_knots = get_num_knots();
+  for (i = 0; i < num_knots; i++) {
+    nurbs->set_knot(i, get_knot(i));
+  }
+
+  return nurbs->recompute();
+}

+ 83 - 0
panda/src/parametrics/nurbsCurveInterface.h

@@ -0,0 +1,83 @@
+// Filename: nurbsCurveInterface.h
+// Created by:  drose (02Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef NURBSCURVEINTERFACE_H
+#define NURBSCURVEINTERFACE_H
+
+#include <pandabase.h>
+
+#include <luse.h>
+#include <filename.h>
+
+class ParametricCurve;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : NurbsCurveInterface
+// Description : This abstract class defines the interface only for a
+//               Nurbs-style curve, with knots and coordinates in
+//               homogeneous space.
+//
+//               The NurbsCurve class inherits both from this and from
+//               ParametricCurve.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA NurbsCurveInterface {
+PUBLISHED:
+  virtual void set_order(int order)=0;
+  virtual int get_order() const=0;
+
+  virtual int get_num_cvs() const=0;
+  virtual int get_num_knots() const=0;
+
+  virtual bool insert_cv(float t)=0;
+
+  INLINE int append_cv(float x, float y, float z);
+  INLINE int append_cv(const LVecBase3f &v);
+  INLINE int append_cv(const LVecBase4f &v);
+
+  virtual bool remove_cv(int n)=0;
+  virtual void remove_all_cvs()=0;
+
+  INLINE bool set_cv_point(int n, float x, float y, float z);
+  INLINE bool set_cv_point(int n, const LVecBase3f &v);
+  INLINE LVecBase3f get_cv_point(int n) const;
+
+  bool set_cv_weight(int n, float w);
+  INLINE float get_cv_weight(int n) const;
+
+  virtual bool set_cv(int n, const LVecBase4f &v)=0;
+  virtual LVecBase4f get_cv(int n) const=0;
+
+  virtual bool set_knot(int n, float t)=0;
+  virtual float get_knot(int n) const=0;
+
+  virtual bool recompute()=0;
+
+  void write_cv(ostream &out, int n) const;
+
+
+protected:
+  virtual int append_cv_impl(const LVecBase4f &v)=0;
+
+  void write(ostream &out, int indent_level) const;
+  bool format_egg(ostream &out, const string &name,
+		  const string &curve_type, int indent_level) const;
+
+  bool convert_to_nurbs(ParametricCurve *nc) const;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "NurbsCurveInterface");
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "nurbsCurveInterface.I"
+
+#endif

+ 4 - 0
panda/src/parametrics/nurbsPPCurve.I

@@ -0,0 +1,4 @@
+// Filename: nurbsPPCurve.I
+// Created by:  drose (02Mar01)
+// 
+////////////////////////////////////////////////////////////////////

+ 691 - 0
panda/src/parametrics/nurbsPPCurve.cxx

@@ -0,0 +1,691 @@
+// Filename: nurbsPPCurve.cxx
+// Created by:  drose (01Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "nurbsPPCurve.h"
+#include "config_parametrics.h"
+
+#include <indent.h>
+
+
+TypeHandle NurbsPPCurve::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+NurbsPPCurve::
+NurbsPPCurve() {
+  _nurbs_valid = false;
+  _order = 4;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::Copy Constructor
+//       Access: Published
+//  Description: Constructs a NURBS curve equivalent to the indicated
+//               (possibly non-NURBS) curve.
+////////////////////////////////////////////////////////////////////
+NurbsPPCurve::
+NurbsPPCurve(const ParametricCurve &pc) {
+  _nurbs_valid = false;
+  _order = 4;
+  
+  if (!pc.convert_to_nurbs(this)) {
+    parametrics_cat->warning() 
+      << "Cannot make a NURBS from the indicated curve.\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::Constructor
+//       Access: Published
+//  Description: Constructs a NURBS curve according to the indicated
+//               NURBS parameters.
+////////////////////////////////////////////////////////////////////
+NurbsPPCurve::
+NurbsPPCurve(int order, int num_cvs,
+	     const float knots[], const LVecBase4f cvs[]) {
+  _nurbs_valid = false;
+  _order = order;
+
+  _points = Points(cvs, cvs + num_cvs);
+  _knots = Knots(knots, knots + num_cvs + _order);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+NurbsPPCurve::
+~NurbsPPCurve() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_max_t
+//       Access: Published, Virtual
+//  Description: Returns the upper bound of t for the entire curve.
+//               The curve is defined in the range 0.0 <= t <=
+//               get_max_t().
+////////////////////////////////////////////////////////////////////
+float NurbsPPCurve::
+get_max_t() const {
+  if (_nurbs_valid) {
+    return _nurbs.maxKnot();
+
+  } else {
+    if (_knots.empty()) {
+      return 0.0;
+    } else {
+      return _knots.back();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::set_order
+//       Access: Published, Virtual
+//  Description: Changes the order of the curve.  Must be a value from
+//               1 to 4.  Can only be done when there are no cv's.
+////////////////////////////////////////////////////////////////////
+void NurbsPPCurve::
+set_order(int order) {
+  nassertv(order >= 1 && order <= 4);
+  nassertv(!_nurbs_valid && _points.empty());
+
+  _order = order;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_order
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int NurbsPPCurve::
+get_order() const {
+  if (_nurbs_valid) {
+    return _nurbs.degree() + 1;
+  } else {
+    return _order;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_num_cvs
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int NurbsPPCurve::
+get_num_cvs() const {
+  if (_nurbs_valid) {
+    return _nurbs.ctrlPnts().size();
+  } else {
+    return _points.size();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_num_knots
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int NurbsPPCurve::
+get_num_knots() const {
+  if (_nurbs_valid) {
+    return _nurbs.knot().size();
+  } else {
+    return _knots.size();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::insert_cv
+//       Access: Published, Virtual
+//  Description: Inserts a new CV into the middle of the curve at the
+//               indicated parametric value.  This doesn't change the
+//               shape or timing of the curve; however, it is
+//               irreversible: if the new CV is immediately removed,
+//               the curve will be changed.  Returns true on success,
+//               false on failure.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+insert_cv(float t) {
+  if (!make_nurbs_valid()) {
+    return false;
+  }
+
+  PLib::NurbsCurvef result;
+  _nurbs.knotInsertion(t, 1, result);
+  _nurbs = result;
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::remove_cv
+//       Access: Published, Virtual
+//  Description: Removes the indicated CV from the curve.  Returns
+//               true if the CV index was valid, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+remove_cv(int n) {
+  if (_nurbs_valid) {
+    _nurbs.removeKnot(n + _order, 1, 1);
+
+  } else {
+    if (n < 0 || n >= (int)_points.size()) {
+      return false;
+    }
+
+    _points.erase(_points.begin() + n);
+    if (_points.empty()) {
+      _knots.clear();
+    } else {
+      _knots.erase(_knots.begin() + _order + n);
+    }
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::remove_all_cvs
+//       Access: Published, Virtual
+//  Description: Removes all CV's from the curve.
+////////////////////////////////////////////////////////////////////
+void NurbsPPCurve::
+remove_all_cvs() {
+  _nurbs_valid = false;
+  _points.clear();
+  _knots.clear();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::set_cv
+//       Access: Published, Virtual
+//  Description: Repositions the indicated CV.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+set_cv(int n, const LVecBase4f &v) {
+  nassertr(n >= 0 && n < get_num_cvs(), false);
+
+  if (_nurbs_valid) {
+    _nurbs.modCP(n, PLib::HPoint3Df(v[0], v[1], v[2], v[3]));
+  } else {
+    _points[n] = v;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_cv
+//       Access: Published, Virtual
+//  Description: Returns the position in homogeneous space of the
+//               indicated CV.
+////////////////////////////////////////////////////////////////////
+LVecBase4f NurbsPPCurve::
+get_cv(int n) const {
+  nassertr(n >= 0 && n < get_num_cvs(), LVecBase4f::zero());
+
+  if (_nurbs_valid) {
+    PLib::HPoint3Df p = _nurbs.ctrlPnts(n);
+    return LVecBase4f(p.x(), p.y(), p.z(), p.w());
+  } else {
+    return _points[n];
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::set_knot
+//       Access: Published, Virtual
+//  Description: Sets the value of the indicated knot directly.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+set_knot(int n, float t) {
+  nassertr(n >= 0 && n < get_num_knots(), false);
+
+  make_arrays_valid();
+  nassertr(n >= 0 && n < (int)_knots.size(), false);
+  _knots[n] = t;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_knot
+//       Access: Published, Virtual
+//  Description: Returns the nth knot value on the curve.
+////////////////////////////////////////////////////////////////////
+float NurbsPPCurve::
+get_knot(int n) const {
+  nassertr(n >= 0 && n < get_num_knots(), 0.0);
+
+  if (_nurbs_valid) {
+    return _nurbs.knot(n);
+  } else {
+    nassertr(n >= 0 && n < (int)_knots.size(), 0.0);
+    return _knots[n];
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::recompute
+//       Access: Published, Virtual
+//  Description: Recalculates the curve basis according to the latest
+//               position of the CV's, knots, etc.  Until this
+//               function is called, adjusting the NURBS parameters
+//               will have no visible effect on the curve.  Returns
+//               true if the resulting curve is valid, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+recompute() {
+  return make_nurbs_valid();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_point
+//       Access: Public, Virtual
+//  Description: Returns the point of the curve at a given parametric
+//               point t.  Returns true if t is in the valid range 0.0
+//               <= t <= get_max_t(); if t is outside this range, sets
+//               point to the value of the curve at the beginning or
+//               end (whichever is nearer) and returns false.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+get_point(float t, LVecBase3f &point) const {
+  nassertr(_nurbs_valid, false);
+
+  bool in_range = true;
+  float max_t = get_max_t();
+
+  if (t < 0.0) {
+    t = 0.0;
+    in_range = false;
+
+  } else if (t > max_t) {
+    t = max_t;
+    in_range = false;
+  }
+
+  PLib::Point3Df p = Cp(t, _nurbs);
+  point.set(p.x(), p.y(), p.z());
+  return in_range;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_tangent
+//       Access: Public, Virtual
+//  Description: Returns the tangent of the curve at a given parametric
+//               point t.  Returns true if t is in the valid range 0.0
+//               <= t <= get_max_t(); if t is outside this range, sets
+//               tangent to the value of the curve at the beginning or
+//               end (whichever is nearer) and returns false.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+get_tangent(float t, LVecBase3f &tangent) const {
+  nassertr(_nurbs_valid, false);
+
+  bool in_range = true;
+  float max_t = get_max_t();
+
+  if (t < 0.0) {
+    t = 0.0;
+    in_range = false;
+
+  } else if (t > max_t) {
+    t = max_t;
+    in_range = false;
+  }
+
+  PLib::Point3Df p = _nurbs.derive3D(t, 1);
+  tangent.set(p.x(), p.y(), p.z());
+  return in_range;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_pt
+//       Access: Public, Virtual
+//  Description: Returns the both the point and the tangent
+//               simultaneously.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const {
+  nassertr(_nurbs_valid, false);
+
+  bool in_range = true;
+  float max_t = get_max_t();
+
+  if (t < 0.0) {
+    t = 0.0;
+    in_range = false;
+
+  } else if (t > max_t) {
+    t = max_t;
+    in_range = false;
+  }
+
+  PLib::Point3Df p = Cp(t, _nurbs);
+  point.set(p.x(), p.y(), p.z());
+  p = _nurbs.derive3D(t, 1);
+  tangent.set(p.x(), p.y(), p.z());
+  return in_range;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_2ndtangent
+//       Access: Public, Virtual
+//  Description: Returns the second derivative of the curve at a given
+//               parametric point t.  Returns true if t is in the
+//               valid range 0.0 <= t <= get_max_t(); if t is outside
+//               this range, sets tangent to the value of the curve at
+//               the beginning or end (whichever is nearer) and
+//               returns false.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+get_2ndtangent(float t, LVecBase3f &tangent2) const {
+  nassertr(_nurbs_valid, false);
+
+  bool in_range = true;
+  float max_t = get_max_t();
+
+  if (t < 0.0) {
+    t = 0.0;
+    in_range = false;
+
+  } else if (t > max_t) {
+    t = max_t;
+    in_range = false;
+  }
+
+  PLib::Point3Df p = _nurbs.derive3D(t, 2);
+  tangent2.set(p.x(), p.y(), p.z());
+  return in_range;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::stitch
+//       Access: Published, Virtual
+//  Description: Regenerates this curve as one long curve: the first
+//               curve connected end-to-end with the second one.
+//               Either a or b may be the same as 'this'.
+//
+//               Returns true if successful, false on failure or if
+//               the curve type does not support stitching.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+stitch(const ParametricCurve *a, const ParametricCurve *b) {
+  // First, make a copy of both of our curves.  This ensures they are
+  // of the correct type, and also protects us in case one of them is
+  // the same as 'this'.
+  PT(NurbsPPCurve) na = new NurbsPPCurve(*a);
+  PT(NurbsPPCurve) nb = new NurbsPPCurve(*b);
+
+  if (na->get_num_cvs() == 0 || nb->get_num_cvs() == 0) {
+    return false;
+  }
+
+  if (!na->make_nurbs_valid()) {
+    return false;
+  }
+
+  // First, translate curve B to move its first CV to curve A's last
+  // CV.
+  LVecBase3f point_offset = 
+    na->get_cv_point(na->get_num_cvs() - 1) - nb->get_cv_point(0);
+  int num_b_cvs = nb->get_num_cvs();
+  for (int i = 0; i < num_b_cvs; i++) {
+    nb->set_cv_point(i, nb->get_cv_point(i) + point_offset);
+  }
+
+  // Now get the arrays from B and reparameterize them so that the
+  // first knot value of B is the last knot value of A.
+  Points b_points;
+  Knots b_knots;
+  int b_order;
+  nb->copy_arrays(b_points, b_knots, b_order);
+  nassertr(!b_knots.empty(), false)
+
+  float knot_offset = na->get_max_t() - b_knots.front();
+  Knots::iterator ki;
+  for (ki = b_knots.begin(); ki != b_knots.end(); ++ki) {
+    (*ki) += knot_offset;
+  }
+
+  // Now we can regenerate the other curve.
+  PLib::NurbsCurvef b_nurbs;
+  if (!make_nurbs_from(b_nurbs, b_points, b_knots, b_order)) {
+    return false;
+  }
+
+  PLib::NurbsCurvef result;
+  if (!result.mergeOf(na->_nurbs, b_nurbs)) {
+    return false;
+  }
+    
+  _nurbs = result;
+  _nurbs_valid = true;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::get_nurbs_interface
+//       Access: Public, Virtual
+//  Description: Returns a pointer to the object as a
+//               NurbsCurveInterface object if it happens to be a
+//               NURBS-style curve; otherwise, returns NULL.
+////////////////////////////////////////////////////////////////////
+NurbsCurveInterface *NurbsPPCurve::
+get_nurbs_interface() {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::convert_to_nurbs
+//       Access: Protected, Virtual
+//  Description: Stores in the indicated NurbsCurve a NURBS
+//               representation of an equivalent curve.  Returns true
+//               if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+convert_to_nurbs(ParametricCurve *nc) const {
+  nc->set_curve_type(_curve_type);
+  return NurbsCurveInterface::convert_to_nurbs(nc);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void NurbsPPCurve::
+write(ostream &out, int indent_level) const {
+  NurbsCurveInterface::write(out, indent_level);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::append_cv_impl
+//       Access: Protected, Virtual
+//  Description: Adds a new CV to the end of the curve.  Creates a new
+//               knot value by adding 1 to the last knot value.
+//               Returns the index of the new CV.
+////////////////////////////////////////////////////////////////////
+int NurbsPPCurve::
+append_cv_impl(const LVecBase4f &cv) {
+  make_arrays_valid();
+
+  _points.push_back(cv);
+
+  if (_knots.empty()) {
+    for (int i = 0; i < _order; i++) {
+      _knots.push_back(0.0);
+    }
+    _knots.push_back(1.0);
+  } else {
+    _knots.push_back(_knots.back() + 1.0);
+  }
+
+  return _points.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::format_egg
+//       Access: Protected, Virtual
+//  Description: Formats the curve as an egg structure to write to the
+//               indicated stream.  Returns true on success, false on
+//               failure.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+format_egg(ostream &out, const string &name, const string &curve_type,
+	   int indent_level) const {
+  return NurbsCurveInterface::format_egg(out, name, curve_type, indent_level);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::make_nurbs_valid
+//       Access: Private
+//  Description: Converts from the _knots and _points array
+//               representation into the actual _nurbs representation.
+//               Returns true if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+make_nurbs_valid() {
+  if (_nurbs_valid) {
+    return true;
+  }
+
+  if (!make_nurbs_from(_nurbs, _points, _knots, _order)) {
+    return false;
+  }
+
+  _nurbs_valid = true;
+  _points.clear();
+  _knots.clear();
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::make_arrays_valid
+//       Access: Private
+//  Description: Converts from the _nurbs representation to separate
+//               _knots and _points arrays.
+////////////////////////////////////////////////////////////////////
+void NurbsPPCurve::
+make_arrays_valid() {
+  if (!_nurbs_valid) {
+    return;
+  }
+
+  make_arrays_from(_nurbs, _points, _knots, _order);
+
+  _nurbs_valid = false;
+  _nurbs = PLib::NurbsCurvef();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::copy_nurbs
+//       Access: Private
+//  Description: Fills nurbs up with a copy of the nurbs curve.
+//               Returns true if valid, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+copy_nurbs(PLib::NurbsCurvef &nurbs) const {
+  if (_nurbs_valid) {
+    nurbs = _nurbs;
+    return true;
+  } else {
+    return make_nurbs_from(nurbs, _points, _knots, _order);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::copy_arrays
+//       Access: Private
+//  Description: Fills the arrays up with a copy of the nurbs data.
+////////////////////////////////////////////////////////////////////
+void NurbsPPCurve::
+copy_arrays(NurbsPPCurve::Points &points, NurbsPPCurve::Knots &knots, 
+	    int &order) const {
+  if (_nurbs_valid) {
+    make_arrays_from(_nurbs, points, knots, order);
+  } else {
+    points = _points;
+    knots = _knots;
+    order = _order;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::make_nurbs_from
+//       Access: Private, Static
+//  Description: Makes a NURBS curve from the indicated array values.
+//               Returns true if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsPPCurve::
+make_nurbs_from(PLib::NurbsCurvef &nurbs,
+		const NurbsPPCurve::Points &points,
+		const NurbsPPCurve::Knots &knots, int order) {
+  if (order < 1 || knots.size() != points.size() + order) {
+    parametrics_cat.error()
+      << "Invalid NURBS curve: order " << order << " with "
+      << points.size() << " CV's and " << knots.size() << " knots.\n";
+    nassertr(false, false);
+    return false;
+  }
+
+  Vector_HPoint3Df v_points(points.size());
+  Vector_FLOAT v_knots(knots.size());
+
+  size_t i;
+  for (i = 0; i < points.size(); i++) {
+    const LVecBase4f &p = points[i];
+    v_points[i] = PLib::HPoint3Df(p[0], p[1], p[2], p[3]);
+  }
+  for (i = 0; i < knots.size(); i++) {
+    v_knots[i] = knots[i];
+  }
+
+  nassertr(v_knots.size() == v_points.size() + order, false);
+
+  nurbs.reset(v_points, v_knots, order - 1);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsPPCurve::make_arrays_from
+//       Access: Private, Static
+//  Description: Fills up the array values from the given NURBS curve.
+////////////////////////////////////////////////////////////////////
+void NurbsPPCurve::
+make_arrays_from(const PLib::NurbsCurvef &nurbs,
+		 NurbsPPCurve::Points &points,
+		 NurbsPPCurve::Knots &knots, int &order) {
+  const Vector_HPoint3Df &v_points = nurbs.ctrlPnts();
+  const Vector_FLOAT &v_knots = nurbs.knot();
+
+  points.clear();
+  knots.clear();
+
+  points.reserve(v_points.size());
+  knots.reserve(v_knots.size());
+
+  Vector_HPoint3Df::const_iterator pi;
+  for (pi = v_points.begin(); pi != v_points.end(); ++pi) {
+    const PLib::HPoint3Df &p = (*pi);
+    points.push_back(LVecBase4f(p.x(), p.y(), p.z(), p.w()));
+  }
+  Vector_FLOAT::const_iterator ki;
+  for (ki = v_knots.begin(); ki != v_knots.end(); ++ki) {
+    knots.push_back(*ki);
+  }
+
+  order = nurbs.degree() + 1;
+}

+ 123 - 0
panda/src/parametrics/nurbsPPCurve.h

@@ -0,0 +1,123 @@
+// Filename: nurbsPPCurve.h
+// Created by:  drose (01Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef NURBSPPCURVE_H
+#define NURBSPPCURVE_H
+
+#include <pandabase.h>
+
+#include "parametricCurve.h"
+#include "nurbsCurveInterface.h"
+
+#include <nurbs.hh>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : NurbsPPCurve
+// Description : A Nonuniform Rational B-Spline.
+//
+//               This class is an extension to Panda NURBS curves,
+//               taking advantage of the external NURBS++ library, if
+//               it is available.  It stands in for the Panda-native
+//               ClassicNurbsCurve, providing more and better
+//               functionality.
+//
+//               This file is only compiled if NURBS++ is available.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA NurbsPPCurve : public ParametricCurve, public NurbsCurveInterface {
+PUBLISHED:
+  NurbsPPCurve();
+  NurbsPPCurve(const ParametricCurve &pc);
+  NurbsPPCurve(int order, int num_cvs,
+	       const float knots[], const LVecBase4f cvs[]);
+  virtual ~NurbsPPCurve();
+
+public:
+  // We don't need to re-publish these, since they're all published
+  // from NurbsCurveInterface.
+  virtual float get_max_t() const;
+
+  virtual void set_order(int order);
+  virtual int get_order() const;
+
+  virtual int get_num_cvs() const;
+  virtual int get_num_knots() const;
+
+  virtual bool insert_cv(float t);
+
+  virtual bool remove_cv(int n);
+  virtual void remove_all_cvs();
+
+  virtual bool set_cv(int n, const LVecBase4f &v);
+  virtual LVecBase4f get_cv(int n) const;
+
+  virtual bool set_knot(int n, float t);
+  virtual float get_knot(int n) const;
+
+  virtual bool recompute();
+  
+public:
+  virtual bool get_point(float t, LVecBase3f &point) const;
+  virtual bool get_tangent(float t, LVecBase3f &tangent) const;
+  virtual bool get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const;
+  virtual bool get_2ndtangent(float t, LVecBase3f &tangent2) const;
+
+  virtual bool stitch(const ParametricCurve *a, const ParametricCurve *b);
+
+  virtual NurbsCurveInterface *get_nurbs_interface();
+  virtual bool convert_to_nurbs(ParametricCurve *nc) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+protected:
+  virtual int append_cv_impl(const LVecBase4f &v);
+  virtual bool format_egg(ostream &out, const string &name, 
+			  const string &curve_type, int indent_level) const;
+
+private:
+  typedef vector<LVecBase4f> Points;
+  typedef vector<float> Knots;
+
+  bool make_nurbs_valid();
+  void make_arrays_valid();
+  bool copy_nurbs(PLib::NurbsCurvef &nurbs) const;
+  void copy_arrays(Points &points, Knots &knots, int &order) const;
+
+  static bool make_nurbs_from(PLib::NurbsCurvef &nurbs,
+			      const Points &points, const Knots &knots,
+			      int order);
+  static void make_arrays_from(const PLib::NurbsCurvef &nurbs,
+			       Points &points, Knots &knots, int &order);
+
+  PLib::NurbsCurvef _nurbs;
+
+  int _order;
+  Points _points;
+  Knots _knots;
+  bool _nurbs_valid;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParametricCurve::init_type();
+    NurbsCurveInterface::init_type();
+    register_type(_type_handle, "NurbsPPCurve",
+                  ParametricCurve::get_class_type(),
+		  NurbsCurveInterface::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+ 
+private:
+  static TypeHandle _type_handle;
+};
+
+typedef NurbsPPCurve NurbsCurve;
+
+#include "nurbsPPCurve.I"
+
+#endif

+ 818 - 0
panda/src/parametrics/parametricCurve.cxx

@@ -0,0 +1,818 @@
+// Filename: parametricCurve.cxx
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "parametricCurve.h"
+#include "config_parametrics.h"
+#include "hermiteCurve.h"
+#include "classicNurbsCurve.h"
+#include "parametricCurveDrawer.h"
+
+#include <datagram.h>
+#include <datagramIterator.h>
+#include <bamWriter.h>
+#include <bamReader.h>
+#include <renderRelation.h>
+#include <omniBoundingVolume.h>
+
+TypeHandle ParametricCurve::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::Constructor
+//       Access: Public
+//  Description: This is a virtual base class.  Don't try to construct
+//               one from Scheme.
+////////////////////////////////////////////////////////////////////
+ParametricCurve::
+ParametricCurve() {
+  _curve_type = PCT_NONE;
+  _num_dimensions = 3;
+
+  // This CurveDrawer object is used to draw the curve implicitly if
+  // it happens to get parented to render, but only if the curve_type
+  // is set to PCT_XYZ or PCT_NONE.
+  _implicit_drawer = (ParametricCurveDrawer *)NULL;
+
+  // And until we attempt to draw it, we set our bounding volume to be
+  // infinitely large (since we don't know).  Once we draw it the
+  // first time, we'll recompute the bounding volume according to the
+  // geometry of what we just drew.
+  set_bound(OmniBoundingVolume());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+ParametricCurve::
+~ParametricCurve() {
+  if (_implicit_drawer != (ParametricCurveDrawer *)NULL) {
+    delete _implicit_drawer;
+  }
+
+  // Our drawer list must be empty by the time we destruct, since our
+  // drawers all maintain reference-counting pointers to us!  If this
+  // is not so, we have lost a reference count somewhere, or we have
+  // gotten confused about which drawers we're registered to.
+  nassertv(_drawers.empty());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::is_valid
+//       Access: Published, Virtual
+//  Description: Returns true if the curve is defined.  This base
+//               class function always returns true; derived classes
+//               might override this to sometimes return false.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+is_valid() const {
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::get_max_t
+//       Access: Published, Virtual
+//  Description: Returns the upper bound of t for the entire curve.
+//               The curve is defined in the range 0.0 <= t <=
+//               get_max_t().  This base class function always returns
+//               1.0; derived classes might override this to return
+//               something else.
+////////////////////////////////////////////////////////////////////
+float ParametricCurve::
+get_max_t() const {
+  return 1.0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::set_curve_type
+//       Access: Published
+//  Description: Sets the flag indicating the use to which the curve
+//               is intended to be put.  This flag is optional and
+//               only serves to provide a hint to the egg reader and
+//               writer code; it has no effect on the curve's
+//               behavior.
+//
+//               Setting the curve type also sets the num_dimensions
+//               to 3 or 1 according to the type.
+//
+//               THis flag may have one of the values PCT_XYZ,
+//               PCT_HPR, or PCT_T.
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+set_curve_type(int type) {
+  _curve_type = type;
+  switch (_curve_type) {
+  case PCT_XYZ:
+  case PCT_HPR:
+  case PCT_NONE:
+    _num_dimensions = 3;
+    break;
+
+  case PCT_T:
+    _num_dimensions = 1;
+    break;
+
+  default:
+    assert(0);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::get_curve_type
+//       Access: Published
+//  Description: Returns the flag indicating the use to which the curve
+//               is intended to be put.
+////////////////////////////////////////////////////////////////////
+int ParametricCurve::
+get_curve_type() const {
+  return _curve_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::set_num_dimensions
+//       Access: Published
+//  Description: Specifies the number of significant dimensions in the
+//               curve's vertices.  This should be one of 1, 2, or 3.
+//               Normally, XYZ and HPR curves have three dimensions;
+//               time curves should always have one dimension.  This
+//               only serves as a hint to the mopath editor, and also
+//               controls how the curve is written out.
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+set_num_dimensions(int num) {
+  _num_dimensions = num;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::get_num_dimensions
+//       Access: Published
+//  Description: Returns the number of significant dimensions in the
+//               curve's vertices, as set by a previous call to
+//               set_num_dimensions().  This is only a hint as to how
+//               the curve is intended to be used; the actual number
+//               of dimensions of any curve is always three.
+////////////////////////////////////////////////////////////////////
+int ParametricCurve::
+get_num_dimensions() const {
+  return _num_dimensions;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::calc_length
+//       Access: Published
+//  Description: Approximates the length of the entire curve to within
+//               a few decimal places.
+////////////////////////////////////////////////////////////////////
+float ParametricCurve::
+calc_length() const {
+  return calc_length(0.0, get_max_t());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::calc_length
+//       Access: Published
+//  Description: Approximates the length of the curve segment from
+//               parametric time from to time to.
+////////////////////////////////////////////////////////////////////
+float ParametricCurve::
+calc_length(float from, float to) const {
+  float t1, t2;
+  LPoint3f p1, p2;
+
+  // Normally we expect from < to.  If they came in backwards, reverse
+  // them.
+  if (to < from) {
+    float temp = to;
+    to = from;
+    from = temp;
+  }
+
+  // Start with a segment for each unit of t.
+  int num_segs = (int)floor(to - from + 1);
+  t2 = from;
+  get_point(t2, p2);
+  float net = 0.0;
+
+  for (int i = 1; i <= num_segs; i++) {
+    t1 = t2;
+    p1 = p2;
+
+    t2 = (to - from) * (float)i / (float)num_segs + from;
+    get_point(t2, p2);
+
+    net += r_calc_length(t1, t2, p1, p2, (p1 - p2).length());
+  }
+  return net;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::find_length
+//       Access: Published
+//  Description: Returns the parametric value corresponding to the
+//               indicated distance along the curve from the starting
+//               parametric value.
+//
+//               This is the inverse of calc_length(): rather than
+//               determining the length along the curve between two
+//               parametric points, it determines the position in
+//               parametric time of a point n units along the curve.
+//
+//               The search distance must not be negative.
+////////////////////////////////////////////////////////////////////
+float ParametricCurve::
+find_length(float start_t, float length_offset) const {
+  nassertr(length_offset >= 0.0, start_t);
+  nassertr(start_t >= 0.0 && start_t <= get_max_t(), start_t);
+
+  float t1, t2;
+  LPoint3f p1, p2;
+
+  // Start with a segment for each unit of t.
+  float max_t = get_max_t();
+  int num_segs = (int)floor(max_t - start_t + 1);
+  t2 = start_t;
+  get_point(t2, p2);
+  float net = 0.0;
+
+  for (int i = 1; i <= num_segs; i++) {
+    t1 = t2;
+    p1 = p2;
+
+    t2 = (max_t - start_t) * (float)i / (float)num_segs + start_t;
+    get_point(t2, p2);
+
+    float seglength = (p1 - p2).length();
+    float result;
+
+    if (r_find_length(length_offset - net, result,
+		      t1, t2, p1, p2, seglength)) {
+      // Found it!
+      return result;
+    }
+
+    net += seglength;
+  }
+
+  // Not on the curve?  Huh.
+  return max_t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::stitch
+//       Access: Published, Virtual
+//  Description: Regenerates this curve as one long curve: the first
+//               curve connected end-to-end with the second one.
+//               Either a or b may be the same as 'this'.
+//
+//               Returns true if successful, false on failure or if
+//               the curve type does not support stitching.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+stitch(const ParametricCurve *, const ParametricCurve *) {
+  parametrics_cat.error()
+    << get_type() << " does not support stitching.\n";
+  return false;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::write_egg
+//       Access: Published
+//  Description: Writes an egg description of the nurbs curve to the
+//               specified output file.  Returns true if the file is
+//               successfully written.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+write_egg(Filename filename, CoordinateSystem cs) {
+  ofstream out;
+  filename.set_text();
+
+  if (!filename.open_write(out)) {
+    parametrics_cat.error()
+      << "Unable to write to " << filename << "\n";
+    return false;
+  }
+  return write_egg(out, filename, cs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::write_egg
+//       Access: Published
+//  Description: Writes an egg description of the nurbs curve to the
+//               specified output stream.  Returns true if the file is
+//               successfully written.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+write_egg(ostream &out, const Filename &filename, CoordinateSystem cs) {
+  string curve_type;
+  switch (get_curve_type()) {
+  case PCT_XYZ:
+    curve_type = "xyz";
+    break;
+
+  case PCT_HPR:
+    curve_type = "hpr";
+    break;
+
+  case PCT_T:
+    curve_type = "t";
+    break;
+  }
+
+  if (!has_name()) {
+    // If we don't have a name, come up with one.
+    string name = filename.get_basename_wo_extension();
+
+    if (!curve_type.empty()) {
+      name += "_";
+      name += curve_type;
+    }
+
+    set_name(name);
+  }
+
+  if (cs == CS_default) {
+    cs = default_coordinate_system;
+  }
+
+  if (cs != CS_invalid) {
+    out << "<CoordinateSystem> { ";
+    switch (cs) {
+    case CS_zup_right:
+      out << "Z-Up";
+      break;
+      
+    case CS_yup_right:
+      out << "Y-Up";
+      break;
+      
+    case CS_zup_left:
+      out << "Z-Up-Left";
+      break;
+      
+    case CS_yup_left:
+      out << "Y-Up-Left";
+      break;
+
+    default:
+      break;
+    }
+    out << " }\n\n";
+  }
+
+
+  if (!format_egg(out, get_name(), curve_type, 0)) {
+    return false;
+  }
+
+  if (out) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::get_bezier_segs
+//       Access: Public, Virtual
+//  Description: Fills up the indicated vector with a list of
+//               BezierSeg structs that describe the curve.  This
+//               assumes the curve is a PiecewiseCurve of
+//               CubicCurvesegs.  Returns true if successful, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+get_bezier_segs(ParametricCurve::BezierSegs &) const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::get_bezier_seg
+//       Access: Public, Virtual
+//  Description: Fills the BezierSeg structure with a description of
+//               the curve segment as a Bezier, if possible, but does
+//               not change the _t member of the structure.  Returns
+//               true if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+get_bezier_seg(ParametricCurve::BezierSeg &) const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::get_nurbs_interface
+//       Access: Public, Virtual
+//  Description: Returns a pointer to the object as a
+//               NurbsCurveInterface object if it happens to be a
+//               NURBS-style curve; otherwise, returns NULL.
+////////////////////////////////////////////////////////////////////
+NurbsCurveInterface *ParametricCurve::
+get_nurbs_interface() {
+  return (NurbsCurveInterface *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::convert_to_hermite
+//       Access: Public, Virtual
+//  Description: Stores an equivalent curve representation in the
+//               indicated Hermite curve, if possible.  Returns true
+//               if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+convert_to_hermite(HermiteCurve *hc) const {
+  BezierSegs bz_segs;
+  if (!get_bezier_segs(bz_segs)) {
+    return false;
+  }
+
+  hc->set_curve_type(_curve_type);
+
+  // Now convert the Bezier segments to a Hermite.  Normally, the
+  // Beziers will match up head-to-tail, but if they don't, that's a
+  // cut.
+  hc->remove_all_cvs();
+
+  int i, n;
+  if (!bz_segs.empty()) {
+    float scale_in = 0.0;
+    float scale_out = bz_segs[0]._t;
+    n = hc->append_cv(HC_SMOOTH, bz_segs[0]._v[0]);
+    hc->set_cv_out(n, 3.0 * (bz_segs[0]._v[1] - bz_segs[0]._v[0]) / scale_out);
+
+    for (i = 0; i < (int)bz_segs.size()-1; i++) {
+      scale_in = scale_out;
+      scale_out = bz_segs[i+1]._t - bz_segs[i]._t;
+
+      if (!bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001)) {
+        // Oops, we have a cut.
+        hc->set_cv_type(n, HC_CUT);
+      }
+
+      n = hc->append_cv(HC_FREE, bz_segs[i+1]._v[0]);
+      hc->set_cv_in(n, 3.0 * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in);
+      hc->set_cv_tstart(n, bz_segs[i]._t);
+
+      hc->set_cv_out(n, 3.0 * (bz_segs[i+1]._v[1] - bz_segs[i+1]._v[0]) / scale_out);
+    }
+
+    // Now the last CV.
+    scale_in = scale_out;
+    i = bz_segs.size()-1;
+    n = hc->append_cv(HC_SMOOTH, bz_segs[i]._v[3]);
+    hc->set_cv_in(n, 3.0 * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in);
+    hc->set_cv_tstart(n, bz_segs[i]._t);
+  }
+
+  // Finally, go through and figure out which CV's are smooth or G1.
+  int num_cvs = hc->get_num_cvs();
+  for (n = 1; n < num_cvs-1; n++) {
+    if (hc->get_cv_type(n)!=HC_CUT) {
+      LVector3f in = hc->get_cv_in(n);
+      LVector3f out = hc->get_cv_out(n);
+      
+      if (in.almost_equal(out, 0.0001)) {
+        hc->set_cv_type(n, HC_SMOOTH);
+      } else {
+        in.normalize();
+        out.normalize();
+        if (in.almost_equal(out, 0.0001)) {
+          hc->set_cv_type(n, HC_G1);
+        }
+      }
+    }
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::convert_to_nurbs
+//       Access: Public, Virtual
+//  Description: Stores in the indicated NurbsCurve a NURBS
+//               representation of an equivalent curve.  Returns true
+//               if successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+convert_to_nurbs(ParametricCurve *nc) const {
+  NurbsCurveInterface *nurbs = nc->get_nurbs_interface();
+  nassertr(nurbs != (NurbsCurveInterface *)NULL, false);
+
+  BezierSegs bz_segs;
+  if (!get_bezier_segs(bz_segs)) {
+    return false;
+  }
+
+  nc->set_curve_type(_curve_type);
+
+  nurbs->remove_all_cvs();
+  nurbs->set_order(4);
+  if (!bz_segs.empty()) {
+    int i;
+    for (i = 0; i < (int)bz_segs.size(); i++) {
+      nurbs->append_cv(bz_segs[i]._v[0]);
+      nurbs->append_cv(bz_segs[i]._v[1]);
+      nurbs->append_cv(bz_segs[i]._v[2]);
+      if (i == (int)bz_segs.size()-1 ||
+          !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001)) {
+        nurbs->append_cv(bz_segs[i]._v[3]);
+      }
+    }
+
+    float t;
+    int ki = 4;
+    nurbs->set_knot(0, 0.0);
+    nurbs->set_knot(1, 0.0);
+    nurbs->set_knot(2, 0.0);
+    nurbs->set_knot(3, 0.0);
+
+    for (i = 0; i < (int)bz_segs.size(); i++) {
+      t = bz_segs[i]._t;
+
+      nurbs->set_knot(ki, t);
+      nurbs->set_knot(ki+1, t);
+      nurbs->set_knot(ki+2, t);
+      ki += 3;
+      if (i == (int)bz_segs.size()-1 ||
+          !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001)) {
+        nurbs->set_knot(ki, t);
+        ki++;
+      }
+    }
+  }
+
+  nurbs->recompute();
+
+  return nc->is_valid();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::draw_traverse
+//       Access: Public, Virtual
+//  Description: This is called by the Draw traversal by virtue of the
+//               node's being present in the scene graph.  Its job is
+//               to make sure the visualization of the collideable
+//               geometry is up-to-date.
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+draw_traverse() {
+  if (_implicit_drawer == (ParametricCurveDrawer *)NULL) {
+    _implicit_drawer = new ParametricCurveDrawer();
+    _implicit_drawer->set_curve(this);
+    _implicit_drawer->draw();
+    _viz_arc = new RenderRelation(this, _implicit_drawer->detach_geom_node());
+    
+    // We must then tell the drawer to forget about us, so we don't
+    // maintain a circular reference count.
+    _implicit_drawer->clear_curves();
+
+    // Set our bounding type to accurately reflect the new geometry
+    // beneath this node.
+    set_bound(BVT_dynamic_sphere);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::register_drawer
+//       Access: Public
+//  Description: Registers a Drawer with this curve that will
+//               automatically be updated whenever the curve is
+//               modified, so that the visible representation of the
+//               curve is kept up to date.  This is called
+//               automatically by the ParametricCurveDrawer.
+//
+//               Any number of Drawers may be registered with a
+//               particular curve.
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+register_drawer(ParametricCurveDrawer *drawer) {
+  _drawers.push_back(drawer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::unregister_drawer
+//       Access: Public
+//  Description: Removes a previously registered drawer from the list
+//               of automatically-refreshed drawers.  This is called
+//               automatically by the ParametricCurveDrawer.
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+unregister_drawer(ParametricCurveDrawer *drawer) {
+  _drawers.remove(drawer);
+}
+
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::invalidate
+//       Access: Protected
+//  Description: Called from a base class to mark a section of the
+//               curve that has been modified and must be redrawn or
+//               recomputed in some way.
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+invalidate(float, float) {
+  invalidate_all();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::invalidate_all
+//       Access: Protected
+//  Description: Called from a base class to indicate that the curve
+//               has changed in some substantial way and must be
+//               entirely redrawn.
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+invalidate_all() {
+  DrawerList::iterator n;
+  for (n = _drawers.begin();
+       n != _drawers.end();
+       ++n) {
+    (*n)->redraw();
+  }
+
+  // Also update the implicit representation.
+  if (_implicit_drawer != (ParametricCurveDrawer *)NULL) {
+    if (_viz_arc->get_parent() != this) {
+      // Hey, someone has moved the visualization.  In that case,
+      // forget about it.
+      _viz_arc = (NodeRelation *)NULL;
+      delete _implicit_drawer;
+      _implicit_drawer = (ParametricCurveDrawer *)NULL;
+      set_bound(OmniBoundingVolume());
+
+    } else {
+      // Ok, the visualization is still there.  Regenerate it.
+      remove_arc(_viz_arc);
+      _implicit_drawer->set_curve(this);
+      _implicit_drawer->draw();
+      _viz_arc = new RenderRelation(this, _implicit_drawer->detach_geom_node());
+      _implicit_drawer->clear_curves();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::format_egg
+//       Access: Protected, Virtual
+//  Description: Formats the curve as an egg structure to write to the
+//               indicated stream.  Returns true on success, false on
+//               failure.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+format_egg(ostream &, const string &, const string &, int) const {
+  return false;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::r_calc_length
+//       Access: Private
+//  Description: The recursive implementation of calc_length.  This
+//               function calculates the length of a segment of the
+//               curve between points t1 and t2, which presumably
+//               evaluate to the endpoints p1 and p2, and the segment
+//               has the length seglength.
+////////////////////////////////////////////////////////////////////
+float ParametricCurve::
+r_calc_length(float t1, float t2, const LPoint3f &p1, const LPoint3f &p2,
+              float seglength) const {
+  static const float length_tolerance = 0.0000001;
+  static const float t_tolerance = 0.000001;
+
+  if (t2 - t1 < t_tolerance) {
+    // Stop recursing--we've just walked off the limit for
+    // representing smaller values of t.
+    return 0.0;
+  } else {
+    float tmid;
+    LPoint3f pmid;
+    float left, right;
+
+    // Calculate the point on the curve midway between the two
+    // endpoints.
+    tmid = (t1+t2)/2.0;
+    get_point(tmid, pmid);
+
+    // Did we increase the length of the segment measurably?
+    left = (p1 - pmid).length();
+    right = (pmid - p2).length();
+
+    if ((left + right) - seglength < length_tolerance) {
+      // No.  We're done.
+      return seglength;
+    } else {
+      // Yes.  Keep going.
+      return r_calc_length(t1, tmid, p1, pmid, left) +
+             r_calc_length(tmid, t2, pmid, p2, right);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::r_find_length
+//       Access: Private
+//  Description: The recursive implementation of find_length.  This is
+//               similar to r_calc_length, above.  target_length is
+//               the length along the curve past t1 that we hope to
+//               find.  If the indicated target_length falls within
+//               this segment, returns true and sets found_t to the
+//               point along the segment.  Otherwise, updates
+//               seglength with the calculated length of the segment
+//               and returns false.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+r_find_length(float target_length, float &found_t,
+	      float t1, float t2, 
+	      const LPoint3f &p1, const LPoint3f &p2,
+	      float &seglength) const {
+  static const float length_tolerance = 0.0000001;
+  static const float t_tolerance = 0.000001;
+
+  if (target_length < t_tolerance) {
+    // Stop recursing--we've just walked off the limit for
+    // representing smaller values of t.
+    found_t = t1;
+    return true;
+
+  } else {
+    float tmid;
+    LPoint3f pmid;
+    float left, right;
+
+    // Calculate the point on the curve midway between the two
+    // endpoints.
+    tmid = (t1+t2)/2.0;
+    get_point(tmid, pmid);
+
+    // Did we increase the length of the segment measurably?
+    left = (p1 - pmid).length();
+    right = (pmid - p2).length();
+
+    if ((left + right) - seglength < length_tolerance) {
+      // No.  We're done.
+      if (target_length <= seglength) {
+	found_t = t1 + (target_length / seglength) * (t2 - t1);
+	return true;
+      }
+      return false;
+
+    } else {
+      // Yes.  Keep going.
+
+      // Maybe the point is in the left half of the segment?
+      if (r_find_length(target_length, found_t, t1, tmid, p1, pmid, left)) {
+	return true;
+      }
+
+      // Maybe it's on the right half?
+      if (r_find_length(target_length - left, found_t, tmid, t2, pmid, p2, right)) {
+	return true;
+      }
+
+      // Neither.  Keep going.
+      seglength = left + right;
+      return false;
+    }
+  }
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::write_datagram
+//       Access: Protected, Virtual
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+write_datagram(BamWriter *manager, Datagram &me) {
+  NamedNode::write_datagram(manager, me);
+
+  me.add_int8(_curve_type);
+  me.add_int8(_num_dimensions);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::fillin
+//       Access: Protected
+//  Description: Function that reads out of the datagram (or asks
+//               manager to read) all of the data that is needed to
+//               re-create this object and stores it in the appropiate
+//               place
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  NamedNode::fillin(scan, manager);
+
+  _curve_type = scan.get_int8();
+  _num_dimensions = scan.get_int8();
+}

+ 149 - 0
panda/src/parametrics/parametricCurve.h

@@ -0,0 +1,149 @@
+// Filename: parametricCurve.h
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PARAMETRICCURVE_H
+#define PARAMETRICCURVE_H
+
+#include <pandabase.h>
+
+#include <namedNode.h>
+#include <luse.h>
+
+#include <typedef.h>
+#include <list>
+#include <vector>
+
+
+// Parametric curve semantic types.  A parametric curve may have one
+// of these types specified.  These serve as hints to the egg reader
+// and writer code about the intention of this curve, and have no
+// other effect on the curve.
+
+BEGIN_PUBLISH //[
+#define PCT_NONE        0
+// Unspecified type.
+
+#define PCT_XYZ         1
+// A three-dimensional curve in space.
+
+#define PCT_HPR         2
+// The curve represents Euler rotation angles.
+
+#define PCT_T           3
+// A one-dimensional timewarp curve.
+END_PUBLISH //]
+
+class ParametricCurveDrawer;
+class HermiteCurveCV;
+class HermiteCurve;
+class ClassicNurbsCurve;
+class NurbsCurveInterface;
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : ParametricCurve
+// Description : A virtual base class for parametric curves.
+//               This encapsulates all curves in 3-d space defined
+//               for a single parameter t in the range [0,get_max_t()].
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ParametricCurve : public NamedNode {
+PUBLISHED:
+  ParametricCurve();
+  virtual ~ParametricCurve();
+
+  virtual bool is_valid() const;
+
+  virtual float get_max_t() const;
+
+  void set_curve_type(int type);
+  int get_curve_type() const;
+
+  void set_num_dimensions(int num);
+  int get_num_dimensions() const;
+
+  float calc_length() const;
+  float calc_length(float from, float to) const;
+  float find_length(float start_t, float length_offset) const;
+
+  virtual bool get_point(float t, LVecBase3f &point) const=0;
+  virtual bool get_tangent(float t, LVecBase3f &tangent) const=0;
+  virtual bool get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const=0;
+  virtual bool get_2ndtangent(float t, LVecBase3f &tangent2) const=0;
+
+  virtual bool stitch(const ParametricCurve *a, const ParametricCurve *b);
+
+  bool write_egg(Filename filename, CoordinateSystem cs = CS_default);
+  bool write_egg(ostream &out, const Filename &filename, CoordinateSystem cs);
+
+public:
+  struct BezierSeg {
+  public:
+    LVecBase3f _v[4];
+    float _t;
+  };
+  typedef vector<BezierSeg> BezierSegs;
+
+  virtual bool get_bezier_segs(BezierSegs &) const;
+  virtual bool get_bezier_seg(BezierSeg &) const;
+  virtual NurbsCurveInterface *get_nurbs_interface();
+
+  virtual bool convert_to_hermite(HermiteCurve *hc) const;
+  virtual bool convert_to_nurbs(ParametricCurve *nc) const;
+
+  virtual void draw_traverse();
+
+  void register_drawer(ParametricCurveDrawer *drawer);
+  void unregister_drawer(ParametricCurveDrawer *drawer);
+
+protected:
+  void invalidate(float t1, float t2);
+  void invalidate_all();
+
+  virtual bool format_egg(ostream &out, const string &name, 
+			  const string &curve_type, int indent_level) const;
+
+private:
+  float r_calc_length(float t1, float t2,
+                      const LPoint3f &p1, const LPoint3f &p2,
+                      float seglength) const;
+  bool r_find_length(float target_length, float &found_t,
+		     float t1, float t2, 
+		     const LPoint3f &p1, const LPoint3f &p2,
+		     float &seglength) const;
+
+protected:
+  int _curve_type;
+  int _num_dimensions;
+
+private:
+  typedef list<ParametricCurveDrawer *> DrawerList;
+  DrawerList _drawers;
+  ParametricCurveDrawer *_implicit_drawer;
+  PT(NodeRelation) _viz_arc;
+
+// TypedWriteable stuff
+protected:
+  virtual void write_datagram(BamWriter *manager, Datagram &me);  
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    NamedNode::init_type();
+    register_type(_type_handle, "ParametricCurve",
+		  NamedNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif

+ 75 - 0
panda/src/parametrics/parametricCurveCollection.I

@@ -0,0 +1,75 @@
+// Filename: parametricCurveCollection.I
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ParametricCurveCollection::
+~ParametricCurveCollection() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_num_curves
+//       Access: Published
+//  Description: Returns the number of ParametricCurves in the collection.
+////////////////////////////////////////////////////////////////////
+INLINE int ParametricCurveCollection::
+get_num_curves() const {
+  return _curves.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_curve
+//       Access: Published
+//  Description: Returns the nth ParametricCurve in the collection.
+////////////////////////////////////////////////////////////////////
+INLINE ParametricCurve *ParametricCurveCollection::
+get_curve(int index) const {
+  nassertr(index >= 0 && index < (int)_curves.size(), (ParametricCurve *)NULL);
+
+  return _curves[index];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_max_t
+//       Access: Published
+//  Description: Returns the maximum T value associated with the
+//               *last* curve in the collection.  Normally, this will
+//               be either the XYZ or HPR curve, or a timewarp curve.
+////////////////////////////////////////////////////////////////////
+INLINE float ParametricCurveCollection::
+get_max_t() const {
+  if (_curves.empty()) {
+    return 0.0;
+  }
+  return _curves.back()->get_max_t();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::evaluate_xyz
+//       Access: Published
+//  Description: Computes only the XYZ part of the curves.  See
+//               evaluate().
+////////////////////////////////////////////////////////////////////
+INLINE bool ParametricCurveCollection::
+evaluate_xyz(float t, LVecBase3f &xyz) const {
+  LVecBase3f hpr;
+  return evaluate(t, xyz, hpr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::evaluate_hpr
+//       Access: Published
+//  Description: Computes only the HPR part of the curves.  See
+//               evaluate().
+////////////////////////////////////////////////////////////////////
+INLINE bool ParametricCurveCollection::
+evaluate_hpr(float t, LVecBase3f &hpr) const {
+  LVecBase3f xyz;
+  return evaluate(t, xyz, hpr);
+}

+ 721 - 0
panda/src/parametrics/parametricCurveCollection.cxx

@@ -0,0 +1,721 @@
+// Filename: parametricCurveCollection.cxx
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "parametricCurveCollection.h"
+#include "config_parametrics.h"
+#include "curveFitter.h"
+#include "parametricCurveDrawer.h"
+#include "nurbsCurve.h"
+
+#include <indent.h>
+#include <compose_matrix.h>
+#include <renderRelation.h>
+#include <string_utils.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ParametricCurveCollection::
+ParametricCurveCollection() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::add_curve
+//       Access: Published
+//  Description: Adds a new ParametricCurve to the collection.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+add_curve(ParametricCurve *curve) {
+  _curves.push_back(curve);
+
+  DrawerList::iterator di;
+  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+    ParametricCurveDrawer *drawer = (*di);
+    curve->register_drawer(drawer);
+
+    drawer->redraw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::add_curves
+//       Access: Published
+//  Description: Adds all the curves found in the scene graph rooted
+//               at the given node.  Returns the number of curves
+//               found.
+////////////////////////////////////////////////////////////////////
+int ParametricCurveCollection::
+add_curves(Node *node) {
+  int num_curves = r_add_curves(node);
+
+  if (num_curves > 0) {
+    DrawerList::iterator di;
+    for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+      ParametricCurveDrawer *drawer = (*di);
+      drawer->redraw();
+    }
+  }
+
+  return num_curves;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::remove_curve
+//       Access: Published
+//  Description: Removes the indicated ParametricCurve from the
+//               collection.  Returns true if the curve was removed,
+//               false if it was not a member of the collection.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+remove_curve(ParametricCurve *curve) {
+  int curve_index = -1;
+  for (int i = 0; curve_index == -1 && i < (int)_curves.size(); i++) {
+    if (_curves[i] == curve) {
+      curve_index = i;
+    }
+  }
+
+  if (curve_index == -1) {
+    // The indicated curve was not a member of the collection.
+    return false;
+  }
+
+  remove_curve(curve_index);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::remove_curve
+//       Access: Published
+//  Description: Removes the indicated ParametricCurve from the
+//               collection, by its index number.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+remove_curve(int index) {
+  nassertv(index >= 0 && index < (int)_curves.size());
+  PT(ParametricCurve) curve = _curves[index];
+  _curves.erase(_curves.begin() + index);
+
+  DrawerList::iterator di;
+  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+    ParametricCurveDrawer *drawer = (*di);
+    curve->unregister_drawer(drawer);
+
+    drawer->redraw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::has_curve
+//       Access: Published
+//  Description: Returns true if the indicated ParametricCurve appears in
+//               this collection, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+has_curve(ParametricCurve *curve) const {
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    if (curve == (*ci)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::clear
+//       Access: Published
+//  Description: Removes all ParametricCurves from the collection.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+clear() {
+  DrawerList::iterator di;
+  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+    ParametricCurveDrawer *drawer = (*di);
+    ParametricCurves::iterator ci;
+    for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+      ParametricCurve *curve = (*ci);
+      curve->unregister_drawer(drawer);
+    }
+
+    drawer->redraw();
+  }
+  _curves.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::clear_timewarps
+//       Access: Published
+//  Description: Removes all the timewarp curves from the collection.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+clear_timewarps() {
+  PT(ParametricCurve) xyz_curve = (ParametricCurve *)NULL;
+  PT(ParametricCurve) hpr_curve = (ParametricCurve *)NULL;
+  ParametricCurves discard;
+
+  ParametricCurves::iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+
+    switch (curve->get_curve_type()) {
+    case PCT_XYZ:
+      if (xyz_curve == (ParametricCurve *)NULL) {
+	xyz_curve = curve;
+      } else {
+	discard.push_back(curve);
+      }
+      break;
+
+    case PCT_HPR:
+      if (hpr_curve == (ParametricCurve *)NULL) {
+	hpr_curve = curve;
+      } else {
+	discard.push_back(curve);
+      }
+      break;
+
+    default:
+      discard.push_back(curve);
+    }
+  }
+
+  _curves.clear();
+  _curves.push_back(xyz_curve);
+
+  if (hpr_curve != (ParametricCurve *)NULL) {
+    _curves.push_back(hpr_curve);
+  }
+
+  // Properly unregister all the curves we're discarding.
+  DrawerList::iterator di;
+  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+    ParametricCurveDrawer *drawer = (*di);
+    ParametricCurves::iterator ci;
+    for (ci = discard.begin(); ci != discard.end(); ++ci) {
+      ParametricCurve *curve = (*ci);
+      curve->unregister_drawer(drawer);
+    }
+
+    drawer->redraw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_xyz_curve
+//       Access: Published
+//  Description: Returns the first XYZ curve in the collection, if
+//               any, or NULL if there are none.
+////////////////////////////////////////////////////////////////////
+ParametricCurve *ParametricCurveCollection::
+get_xyz_curve() const {
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    if (curve->get_curve_type() == PCT_XYZ) {
+      return curve;
+    }
+  }
+  return (ParametricCurve *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_hpr_curve
+//       Access: Published
+//  Description: Returns the first HPR curve in the collection, if
+//               any, or NULL if there are none.
+////////////////////////////////////////////////////////////////////
+ParametricCurve *ParametricCurveCollection::
+get_hpr_curve() const {
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    if (curve->get_curve_type() == PCT_HPR) {
+      return curve;
+    }
+  }
+  return (ParametricCurve *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_default_curve
+//       Access: Published
+//  Description: If there is an XYZ curve in the collection, returns
+//               it; otherwise, returns the first curve whose type is
+//               unspecified.  Returns NULL if no curve meets the
+//               criteria.
+////////////////////////////////////////////////////////////////////
+ParametricCurve *ParametricCurveCollection::
+get_default_curve() const {
+  ParametricCurve *xyz_curve = get_xyz_curve();
+  if (xyz_curve != (ParametricCurve *)NULL) {
+    return xyz_curve;
+  }
+
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    if (curve->get_curve_type() == PCT_NONE) {
+      return curve;
+    }
+  }
+  return (ParametricCurve *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_num_timewarps
+//       Access: Published
+//  Description: Returns the number of timewarp curves in the
+//               collection.
+////////////////////////////////////////////////////////////////////
+int ParametricCurveCollection::
+get_num_timewarps() const {
+  int count = 0;
+
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    if (curve->get_curve_type() == PCT_T) {
+      count++;
+    }
+  }
+
+  return count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::make_even
+//       Access: Published
+//  Description: Discards all existing timewarp curves and recomputes
+//               a new timewarp curve that maps distance along the
+//               curve to parametric time, so that the distance
+//               between any two points in parametric time is
+//               proprotional to the approximate distance of those
+//               same two points along the XYZ curve.
+//
+//               segments_per_unit represents the number of segments to
+//               take per each unit of parametric time of the original
+//               XYZ curve.
+//
+//               The new timewarp curve (and thus, the apparent range
+//               of the collection) will range from 0 to max_t.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+make_even(float max_t, float segments_per_unit) {
+  ParametricCurve *xyz_curve = get_xyz_curve();
+  if (xyz_curve == (ParametricCurve *)NULL) {
+    parametrics_cat.error()
+      << "No XYZ curve for make_even().\n";
+    return;
+  }
+
+  clear_timewarps();
+
+  // Now divvy up the XYZ curve into num_segments sections, each
+  // approximately the same length as all the others.
+  CurveFitter fitter;
+
+  int num_segments = (int)floor(segments_per_unit * xyz_curve->get_max_t() + 0.5);
+
+  float net_length = xyz_curve->calc_length();
+  float segment_length = net_length / (float)num_segments;
+
+  float last_t = 0.0;
+  fitter.add_xyz(0.0, LVecBase3f(last_t, 0.0, 0.0));
+
+  for (int i = 0; i < num_segments; i++) {
+    float next_t = xyz_curve->find_length(last_t, segment_length);
+    fitter.add_xyz((float)(i + 1) / (num_segments + 1) * max_t,
+		   LVecBase3f(next_t, 0.0, 0.0));
+    last_t = next_t;
+  }
+
+  fitter.compute_tangents(1);
+  PT(ParametricCurveCollection) fit = fitter.make_nurbs();
+  ParametricCurve *t_curve = fit->get_xyz_curve();
+  nassertv(t_curve != (ParametricCurve *)NULL);
+  t_curve->set_curve_type(PCT_T);
+  add_curve(t_curve);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::evaluate
+//       Access: Published
+//  Description: Computes the position and rotation represented by the
+//               first XYZ and HPR curves in the collection at the
+//               given point t, after t has been modified by all the
+//               timewarp curves in the collection applied in
+//               sequence, from back to front.
+//
+//               Returns true if the point is valid (i.e. t is within
+//               the bounds indicated by all the timewarp curves and
+//               within the bounds of the curves themselves), or false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+evaluate(float t, LVecBase3f &xyz, LVecBase3f &hpr) const {
+  // First, apply all the timewarps in sequence, from back to front.
+  // Also take note of the XYZ and HPR curves.
+  ParametricCurve *xyz_curve = (ParametricCurve *)NULL;
+  ParametricCurve *hpr_curve = (ParametricCurve *)NULL;
+  ParametricCurve *default_curve = (ParametricCurve *)NULL;
+
+  float t0 = t;
+  LVecBase3f point;
+
+  ParametricCurves::const_reverse_iterator ci;
+  for (ci = _curves.rbegin(); ci != _curves.rend(); ++ci) {
+    ParametricCurve *curve = (*ci);
+
+    switch (curve->get_curve_type()) {
+    case PCT_XYZ:
+      xyz_curve = curve;
+      break;
+
+    case PCT_HPR:
+      hpr_curve = curve;
+      break;
+
+    case PCT_NONE:
+      default_curve = curve;
+      break;
+
+    case PCT_T:
+      if (!curve->get_point(t0, point)) {
+	return false;
+      }
+      t0 = point[0];
+    }
+  }
+
+  if (xyz_curve == (ParametricCurve *)NULL) {
+    xyz_curve = default_curve;
+  }
+
+  // Now compute the position and orientation.
+  if (xyz_curve != (ParametricCurve *)NULL) {
+    if (!xyz_curve->get_point(t0, xyz)) {
+      return false;
+    }
+  }
+
+  if (hpr_curve != (ParametricCurve *)NULL) {
+    if (!hpr_curve->get_point(t0, hpr)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::evaluate
+//       Access: Published
+//  Description: Computes the transform matrix representing
+//               translation to the position indicated by the first
+//               XYZ curve in the collection and the rotation
+//               indicated by the first HPR curve in the collection,
+//               after t has been modified by all the timewarp curves
+//               in the collection applied in sequence, from back to
+//               front.
+//
+//               Returns true if the point is valid (i.e. t is within
+//               the bounds indicated by all the timewarp curves and
+//               within the bounds of the curves themselves), or false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+evaluate(float t, LMatrix4f &result, CoordinateSystem cs) const {
+  LVecBase3f xyz(0.0, 0.0, 0.0);
+  LVecBase3f hpr(0.0, 0.0, 0.0);
+
+  if (!evaluate(t, xyz, hpr)) {
+    return false;
+  }
+
+  compose_matrix(result, LVecBase3f(1.0, 1.0, 1.0), hpr, xyz, cs);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::evaluate_t
+//       Access: Published
+//  Description: Determines the value of t that should be passed to
+//               the XYZ and HPR curves, after applying the given
+//               value of t to all the timewarps.  Return -1.0 if the
+//               value of t exceeds one of the timewarps' ranges.
+////////////////////////////////////////////////////////////////////
+float ParametricCurveCollection::
+evaluate_t(float t) const {
+  float t0 = t;
+  LVecBase3f point;
+
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+
+    if (curve->get_curve_type() == PCT_T) {
+      if (!curve->get_point(t0, point)) {
+	return -1.0;
+      }
+      t0 = point[0];
+    }
+  }
+
+  return t0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::stitch
+//       Access: Published
+//  Description: Regenerates this curve as one long curve: the first
+//               curve connected end-to-end with the second one.
+//               Either a or b may be the same as 'this'.  This will
+//               lose any timewarps on the input curves.
+//
+//               Returns true if successful, false on failure.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+stitch(const ParametricCurveCollection *a, 
+       const ParametricCurveCollection *b) {
+  PT(ParametricCurve) a_xyz = a->get_xyz_curve();
+  PT(ParametricCurve) b_xyz = b->get_xyz_curve();
+
+  PT(ParametricCurve) a_hpr = a->get_hpr_curve();
+  PT(ParametricCurve) b_hpr = b->get_hpr_curve();
+
+  clear();
+
+  if (a_xyz != (ParametricCurve *)NULL && b_xyz != (ParametricCurve *)NULL) {
+    PT(NurbsCurve) new_xyz = new NurbsCurve;
+    if (!new_xyz->stitch(a_xyz, b_xyz)) {
+      return false;
+    }
+    new_xyz->set_curve_type(PCT_XYZ);
+    add_curve(new_xyz);
+  }
+
+  if (a_hpr != (ParametricCurve *)NULL && b_hpr != (ParametricCurve *)NULL) {
+    PT(NurbsCurve) new_hpr = new NurbsCurve;
+    if (!new_hpr->stitch(a_hpr, b_hpr)) {
+      return false;
+    }
+    new_hpr->set_curve_type(PCT_HPR);
+    add_curve(new_hpr);
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::output
+//       Access: Published
+//  Description: Writes a brief one-line description of the
+//               ParametricCurveCollection to the indicated output stream.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+output(ostream &out) const {
+  if (get_num_curves() == 1) {
+    out << "1 ParametricCurve";
+  } else {
+    out << get_num_curves() << " ParametricCurves";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::write
+//       Access: Published
+//  Description: Writes a complete multi-line description of the
+//               ParametricCurveCollection to the indicated output stream.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+write(ostream &out, int indent_level) const {
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    indent(out, indent_level) << *curve << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::write_egg
+//       Access: Published
+//  Description: Writes an egg description of all the nurbs curves in
+//               the collection to the specified output file.  Returns
+//               true if the file is successfully written.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+write_egg(Filename filename, CoordinateSystem cs) {
+  ofstream out;
+  filename.set_text();
+
+  if (!filename.open_write(out)) {
+    parametrics_cat.error()
+      << "Unable to write to " << filename << "\n";
+    return false;
+  }
+  return write_egg(out, filename, cs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::write_egg
+//       Access: Published
+//  Description: Writes an egg description of all the nurbs curves in
+//               the collection to the specified output stream.  Returns
+//               true if the file is successfully written.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+write_egg(ostream &out, const Filename &filename, CoordinateSystem cs) {
+  if (cs == CS_default) {
+    cs = default_coordinate_system;
+  }
+
+  if (cs != CS_invalid) {
+    out << "<CoordinateSystem> { ";
+    switch (cs) {
+    case CS_zup_right:
+      out << "Z-Up";
+      break;
+      
+    case CS_yup_right:
+      out << "Y-Up";
+      break;
+      
+    case CS_zup_left:
+      out << "Z-Up-Left";
+      break;
+      
+    case CS_yup_left:
+      out << "Y-Up-Left";
+      break;
+
+    default:
+      break;
+    }
+    out << " }\n\n";
+  }
+
+  int xyz_count = 0;
+  int hpr_count = 0;
+  int t_count = 0;
+
+  ParametricCurves::iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+
+    if (!curve->has_name()) {
+      // If we don't have a name, come up with one.
+      string name = filename.get_basename_wo_extension();
+
+      switch (curve->get_curve_type()) {
+      case PCT_XYZ:
+	name += "_xyz";
+	if (xyz_count > 0) {
+	  name += format_string(xyz_count);
+	}
+	xyz_count++;
+	break;
+
+      case PCT_HPR:
+	name += "_hpr";
+	if (hpr_count > 0) {
+	  name += format_string(hpr_count);
+	}
+	hpr_count++;
+	break;
+
+      case PCT_T:
+	name += "_t";
+	if (t_count > 0) {
+	  name += format_string(t_count);
+	}
+	t_count++;
+	break;
+      }
+      
+      curve->set_name(name);
+    }
+
+    if (!curve->write_egg(out, filename, CS_invalid)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::r_add_curves
+//       Access: Private
+//  Description: The recursive implementation of add_curves().
+////////////////////////////////////////////////////////////////////
+int ParametricCurveCollection::
+r_add_curves(Node *node) {
+  int num_curves = 0;
+
+  if (node->is_of_type(ParametricCurve::get_class_type())) {
+    ParametricCurve *curve = DCAST(ParametricCurve, node);
+    _curves.push_back(curve);
+
+    DrawerList::iterator di;
+    for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+      ParametricCurveDrawer *drawer = (*di);
+      curve->register_drawer(drawer);
+    }
+    num_curves++;
+  }
+
+  int num_children = node->get_num_children(RenderRelation::get_class_type());
+  for (int i = 0; i < num_children; i++) {
+    NodeRelation *arc = node->get_child(RenderRelation::get_class_type(), i);
+    num_curves += r_add_curves(arc->get_child());
+  }
+
+  return num_curves;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::register_drawer
+//       Access: Public
+//  Description: Registers a Drawer with this curve collection that
+//               will automatically be updated whenever the collection
+//               is modified, so that the visible representation of
+//               the curve is kept up to date.  This is called
+//               automatically by the ParametricCurveDrawer.
+//
+//               Any number of Drawers may be registered with a
+//               particular curve collection.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+register_drawer(ParametricCurveDrawer *drawer) {
+  _drawers.push_back(drawer);
+
+  ParametricCurves::iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    curve->register_drawer(drawer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::unregister_drawer
+//       Access: Public
+//  Description: Removes a previously registered drawer from the list
+//               of automatically-refreshed drawers.  This is called
+//               automatically by the ParametricCurveDrawer.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+unregister_drawer(ParametricCurveDrawer *drawer) {
+  _drawers.remove(drawer);
+
+  ParametricCurves::iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    curve->unregister_drawer(drawer);
+  }
+}

+ 95 - 0
panda/src/parametrics/parametricCurveCollection.h

@@ -0,0 +1,95 @@
+// Filename: parametricCurveCollection.h
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef NODEPATHCOLLECTION_H
+#define NODEPATHCOLLECTION_H
+
+#include <pandabase.h>
+
+#include "parametricCurve.h"
+
+#include <referenceCount.h>
+#include <pointerTo.h>
+#include <luse.h>
+
+#include <vector>
+#include <list>
+
+class ParametricCurveDrawer;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ParametricCurveCollection
+// Description : This is a set of zero or more ParametricCurves, which
+//               may or may not be related.  If they are related, the
+//               set should contain no more than one XYZ curve, no
+//               more than one HPR curve, and zero or more Timewarp
+//               curves, which can then be evaluated as a unit to
+//               return a single transformation matrix for a given
+//               unit of time.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ParametricCurveCollection : public ReferenceCount {
+PUBLISHED:
+  ParametricCurveCollection();
+  INLINE ~ParametricCurveCollection();
+
+  void add_curve(ParametricCurve *curve);
+  int add_curves(Node *node);
+  bool remove_curve(ParametricCurve *curve);
+  void remove_curve(int index);
+  bool has_curve(ParametricCurve *curve) const;
+  void clear();
+  void clear_timewarps();
+
+  INLINE int get_num_curves() const;
+  INLINE ParametricCurve *get_curve(int index) const;
+  
+  ParametricCurve *get_xyz_curve() const;
+  ParametricCurve *get_hpr_curve() const;
+  ParametricCurve *get_default_curve() const;
+  int get_num_timewarps() const;
+
+  INLINE float get_max_t() const;
+
+  void make_even(float max_t, float segments_per_unit);
+
+  bool evaluate(float t, LVecBase3f &xyz, LVecBase3f &hpr) const;
+  bool evaluate(float t, LMatrix4f &result, CoordinateSystem cs = CS_default) const;
+
+  float evaluate_t(float t) const;
+  INLINE bool evaluate_xyz(float t, LVecBase3f &xyz) const;
+  INLINE bool evaluate_hpr(float t, LVecBase3f &hpr) const;
+
+  bool stitch(const ParametricCurveCollection *a, 
+	      const ParametricCurveCollection *b);
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level = 0) const;
+
+  bool write_egg(Filename filename, CoordinateSystem cs = CS_default);
+  bool write_egg(ostream &out, const Filename &filename, CoordinateSystem cs);
+
+public:
+  int r_add_curves(Node *node);
+  void register_drawer(ParametricCurveDrawer *drawer);
+  void unregister_drawer(ParametricCurveDrawer *drawer);
+
+private:
+  typedef vector<PT(ParametricCurve)> ParametricCurves;
+  ParametricCurves _curves;
+  typedef list<ParametricCurveDrawer *> DrawerList;
+  DrawerList _drawers;
+};
+
+INLINE ostream &
+operator << (ostream &out, const ParametricCurveCollection &col) {
+  col.output(out);
+  return out;
+}
+
+#include "parametricCurveCollection.I"
+
+#endif
+
+

+ 31 - 0
panda/src/parametrics/parametricCurveDrawer.I

@@ -0,0 +1,31 @@
+// Filename: parametricCurveDrawer.I
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_max_t
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE float ParametricCurveDrawer::
+get_max_t() const {
+  if (_curves == (ParametricCurveCollection *)NULL) {
+    return 0.0;
+  } else {
+    return _curves->get_max_t();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::redraw
+//       Access: Public
+//  Description: Redraws the curve if it is currently visible.
+////////////////////////////////////////////////////////////////////
+INLINE void ParametricCurveDrawer::
+redraw() {
+  if (_drawn) {
+    draw();
+  }
+}

+ 451 - 0
panda/src/parametrics/parametricCurveDrawer.cxx

@@ -0,0 +1,451 @@
+// Filename: parametricCurveDrawer.C
+// Created by:  drose (14Mar97)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+#include "parametricCurveDrawer.h"
+#include "parametricCurve.h"
+#include "config_parametrics.h"
+
+#include <renderRelation.h>
+#include <transformTransition.h>
+#include <compose_matrix.h>
+
+TypeHandle ParametricCurveDrawer::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+ParametricCurveDrawer::
+ParametricCurveDrawer() {
+  _lines.set_color(1.0, 1.0, 1.0);
+  _ticks.set_color(1.0, 0.0, 0.0);
+  _tick_scale = 0.1;
+  _num_segs = 100.0;
+  _num_ticks = 0.0;
+  _frame_accurate = false;
+  _geom_node = new GeomNode;
+  _drawn = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+ParametricCurveDrawer::
+~ParametricCurveDrawer() {
+  hide();
+
+  // Unregister all the curves.
+  clear_curves();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_curve
+//       Access: Published
+//  Description: Sets the drawer up to draw just the one curve.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_curve(ParametricCurve *curve) {
+  PT(ParametricCurveCollection) curves = new ParametricCurveCollection();
+  curves->add_curve(curve);
+  set_curves(curves);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_curves
+//       Access: Published
+//  Description: Sets the drawer up to draw the curves in the
+//               indicated collection.  The drawer will actually draw
+//               just the first XYZ curve in the collection, but if
+//               one or more timewarps are present, this will affect
+//               the placement of tick marks.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_curves(ParametricCurveCollection *curves) {
+  if (curves != _curves) {
+    // First, unregister the old curves.
+    if (_curves != (ParametricCurveCollection *)NULL) {
+      _curves->unregister_drawer(this);
+    }
+    
+    _curves = curves;
+    
+    // Now, register the new ones.
+    if (_curves != (ParametricCurveCollection *)NULL) {
+      _curves->register_drawer(this);
+    }
+    
+    redraw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::clear_curves
+//       Access: Published
+//  Description: Empties the list of curves the drawer will update.
+//               It will draw nothing.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+clear_curves() {
+  set_curves((ParametricCurveCollection *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_curves
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+ParametricCurveCollection *ParametricCurveDrawer::
+get_curves() {
+  return _curves;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_geom_node
+//       Access: Published
+//  Description: Returns a pointer to the drawer's GeomNode.  This is
+//               where the drawer will build the visible
+//               representation of the curve.  This GeomNode must be
+//               inserted into the scene graph to make the curve
+//               visible.  The GeomNode remains connected to the drawer,
+//               so that future updates to the drawer will reflect in
+//               the GeomNode, and the GeomNode will be emptied when the
+//               drawer destructs.  Also see detach_geom_node().
+////////////////////////////////////////////////////////////////////
+GeomNode *ParametricCurveDrawer::
+get_geom_node() {
+  return _geom_node;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::detach_geom_node
+//       Access: Published
+//  Description: Detaches the GeomNode from the drawer so that the
+//               drawing will remain after the death of the drawer.
+//               Returns the now-static GeomNode.  A new, dynamic GeomNode
+//               is created for the drawer's future use; get_geom_node()
+//               will return this new GeomNode which will be empty until
+//               the next call to draw().
+////////////////////////////////////////////////////////////////////
+GeomNode *ParametricCurveDrawer::
+detach_geom_node() {
+  if (!_drawn) {
+    draw();
+  }
+  PT(GeomNode) g = _geom_node;
+  _geom_node = new GeomNode;
+  _tick_arcs.clear();
+  _drawn = false;
+  return g;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_num_segs
+//       Access: Published
+//  Description: Specifies the number of line segments used to
+//               approximate the curve for each parametric unit.  This
+//               just affects the visual appearance of the curve as it
+//               is drawn.  The total number of segments drawn for the
+//               curve will be get_max_t() * get_num_segs().
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_num_segs(float num_segs) {
+  _num_segs = num_segs;
+  redraw();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_num_segs
+//       Access: Published
+//  Description: Returns the number of line segments used to
+//               approximate the curve for each parametric unit.  This
+//               just affects the visual appearance of the curve as it
+//               is drawn.  The total number of segments drawn for the
+//               curve will be get_max_t() * get_num_segs().
+////////////////////////////////////////////////////////////////////
+float ParametricCurveDrawer::
+get_num_segs() const {
+  return _num_segs;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_num_ticks
+//       Access: Published
+//  Description: Specifies the number of time tick marks drawn
+//               for each unit of time.  These tick marks are drawn at
+//               equal increments in time to give a visual
+//               approximation of speed.  Specify 0 to disable drawing
+//               of tick marks.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_num_ticks(float num_ticks) {
+  _num_ticks = num_ticks;
+  redraw();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_num_ticks
+//       Access: Published
+//  Description: Returns the number of time tick marks per unit of
+//               time drawn.
+////////////////////////////////////////////////////////////////////
+float ParametricCurveDrawer::
+get_num_ticks() const {
+  return _num_ticks;
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_color
+//       Access: Published
+//  Description: Specifies the color of the curve when it is drawn.
+//               The default is white.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_color(float r, float g, float b) {
+  _lines.set_color(r, g, b);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_color
+//       Access: Published
+//  Description: Specifies the color of the time tick marks drawn on
+//               the curve.  The default is red.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_tick_color(float r, float g, float b) {
+  _ticks.set_color(r, g, b);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_tick_geometry
+//       Access: Published
+//  Description: Indicates a piece of geometry that will be instanced
+//               to each tick point along the curve instead of a
+//               cross, representing the direction the curve is
+//               oriented.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_tick_geometry(Node *geom) {
+  _tick_geometry = geom;
+  redraw();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::clear_tick_geometry
+//       Access: Published
+//  Description: Resets the tick geometry to appear as crosses.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+clear_tick_geometry() {
+  _tick_geometry.clear();
+  redraw();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_frame_accurate
+//       Access: Published
+//  Description: Specifies whether the curve drawn is to be
+//               frame-accurate.  If true, then changes made to the
+//               curve dynamically after it has been drawn will be
+//               reflected correctly in the render window.  If false,
+//               dynamic updates may be drawn before the rest of the
+//               scene has updated.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_frame_accurate(bool frame_accurate) {
+  _frame_accurate = frame_accurate;
+  redraw();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_frame_accurate
+//       Access: Published
+//  Description: Returns whether the curve is drawn in frame-accurate
+//               mode.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveDrawer::
+get_frame_accurate() const {
+  return _frame_accurate;
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::draw
+//       Access: Published, Virtual
+//  Description: Creates a series of line segments that approximates
+//               the curve.  These line segments may be made visible
+//               by adding the GeomNode returned by get_geom_node() into the
+//               scene graph.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveDrawer::
+draw() {
+  // First, remove the old drawing, if any.
+  hide();
+
+  // If there's no curve, draw nothing and return false.
+  if (_curves == (ParametricCurveCollection *)NULL ||
+      _curves->get_default_curve() == (ParametricCurve *)NULL) {
+    return false;
+  }
+
+  // Otherwise, let's go to town!
+  int total_segs = (int)floor(_curves->get_max_t() * _num_segs + 0.5);
+
+  float max_t = get_max_t();
+  float scale = max_t / (float)(total_segs-1);
+  float t;
+  LVecBase3f point;
+  bool last_in, next_in;
+
+  last_in = false;
+  int i;
+
+  for (i = 0; i < total_segs; i++) {
+    t = (float)i * scale;
+
+    next_in = _curves->evaluate_xyz(t, point);
+
+    if (!next_in || !last_in) {
+      _lines.move_to(point);
+    } else {
+      _lines.draw_to(point);
+    }
+    last_in = next_in;
+  }
+
+  _lines.create(_geom_node, _frame_accurate);
+  _drawn = true;
+
+  // Now draw the time tick marks.
+  if (_num_ticks > 0.0) {
+    int total_ticks = (int)floor(max_t * _num_ticks + 0.5);
+    ParametricCurve *xyz_curve = _curves->get_default_curve();
+    ParametricCurve *hpr_curve = _curves->get_hpr_curve();
+
+    scale = max_t / (float)(total_ticks-1);
+    for (i = 0; i < total_ticks; i++) {
+      t = (float)i * scale;
+      float t0 = _curves->evaluate_t(t);
+      LVecBase3f tangent;
+
+      if (xyz_curve->get_pt(t0, point, tangent)) {
+	if (_tick_geometry == (Node *)NULL) {
+	  // Draw crosses.
+	  LVecBase3f t1, t2;
+	  get_tick_marks(tangent, t1, t2);
+	  
+	  _ticks.move_to(point - t1 * _tick_scale);
+	  _ticks.draw_to(point + t1 * _tick_scale);
+	  _ticks.move_to(point - t2 * _tick_scale);
+	  _ticks.draw_to(point + t2 * _tick_scale);
+
+	} else {
+	  // Instance the tick geometry.
+	  NodeRelation *arc = new RenderRelation(_geom_node, _tick_geometry);
+	  _tick_arcs.push_back(arc);
+
+	  LVecBase3f hpr(0.0, 0.0, 0.0);
+	  if (hpr_curve != (ParametricCurve *)NULL) {
+	    hpr_curve->get_point(t0, hpr);
+	  }
+	  LMatrix4f mat;
+	  compose_matrix(mat, LVecBase3f(_tick_scale, _tick_scale, _tick_scale),
+			 hpr, point);
+	  arc->set_transition(new TransformTransition(mat));
+	}
+      }
+    }
+    _ticks.create(_geom_node, _frame_accurate);
+  }
+
+  return true;
+}
+
+    
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::hide
+//       Access: Published
+//  Description: Removes the lines that were created by a previous
+//               call to draw().
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+hide() {
+  _geom_node->clear();
+  _drawn = false;
+
+  TickArcs::iterator ti;
+  for (ti = _tick_arcs.begin(); ti != _tick_arcs.end(); ++ti) {
+    remove_arc(*ti);
+  }
+  _tick_arcs.clear();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::set_tick_scale
+//       Access: Published
+//  Description: Sets the visible size of the time tick marks.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+set_tick_scale(float scale) {
+  _tick_scale = scale;
+  redraw();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_tick_scale
+//       Access: Published
+//  Description: Returns the size of the time tick marks.
+////////////////////////////////////////////////////////////////////
+float ParametricCurveDrawer::
+get_tick_scale() const {
+  return _tick_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveDrawer::get_tick_marks
+//       Access: Protected, Static
+//  Description: Given a tangent vector, computes two vectors at right
+//               angles to the tangent and to each other, suitable for
+//               drawing as tick marks.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveDrawer::
+get_tick_marks(const LVecBase3f &tangent, LVecBase3f &t1, LVecBase3f &t2) {
+  LVector3f tn = tangent;
+  tn.normalize();
+
+  // Decide the smallest axis of tn and cross with the corresponding
+  // unit vector.
+  if (fabs(tn[0]) <= fabs(tn[1]) && fabs(tn[0]) <= fabs(tn[2])) {
+    // X is smallest.
+    t1 = tn.cross(LVector3f(1.0, 0.0, 0.0));
+
+  } else if (fabs(tn[1]) <= fabs(tn[2])) {
+    // Y is smallest.
+    t1 = tn.cross(LVector3f(0.0, 1.0, 0.0));
+
+  } else {
+    // Z is smallest.
+    t1 = tn.cross(LVector3f(0.0, 0.0, 1.0));
+  }
+
+  t2 = tn.cross(t1);
+}

+ 93 - 0
panda/src/parametrics/parametricCurveDrawer.h

@@ -0,0 +1,93 @@
+// Filename: parametricCurveDrawer.h
+// Created by:  drose (14Mar97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PARAMETRICCURVEDRAWER_H
+#define PARAMETRICCURVEDRAWER_H
+
+#include "parametricCurveCollection.h"
+
+#include <lineSegs.h>
+#include <node.h>
+
+#include <typeHandle.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : ParametricCurveDrawer
+// Description : Draws a 3-d parametric curve in the scene by creating
+//               a series of line segments to approximate the curve.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ParametricCurveDrawer : public TypedObject {
+PUBLISHED:
+  ParametricCurveDrawer();
+  virtual ~ParametricCurveDrawer();
+
+  void set_curve(ParametricCurve *curve);
+  void set_curves(ParametricCurveCollection *curves);
+  void clear_curves();
+  ParametricCurveCollection *get_curves();
+
+  GeomNode *get_geom_node();
+  GeomNode *detach_geom_node();
+
+  void set_num_segs(float num_segs);
+  float get_num_segs() const;
+
+  void set_num_ticks(float num_ticks);
+  float get_num_ticks() const;
+
+  void set_color(float r, float g, float b);
+  void set_tick_color(float r, float g, float b);
+  void set_tick_geometry(Node *geom);
+  void clear_tick_geometry();
+
+  void set_frame_accurate(bool frame_accurate);
+  bool get_frame_accurate() const;
+
+  virtual bool draw();
+  void hide();
+
+  void set_tick_scale(float scale);
+  float get_tick_scale() const;
+
+public:
+  INLINE float get_max_t() const;
+  INLINE void redraw();
+
+protected:
+  static void get_tick_marks(const LVecBase3f &tangent, LVecBase3f &t1, LVecBase3f &t2);
+
+  PT(GeomNode) _geom_node;
+  PT(ParametricCurveCollection) _curves;
+  float _num_segs;
+  LineSegs _lines, _ticks;
+  PT(Node) _tick_geometry;
+  bool _drawn;
+  float _num_ticks;
+  float _tick_scale;
+  bool _frame_accurate;
+
+  typedef vector<PT(NodeRelation)> TickArcs;
+  TickArcs _tick_arcs;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "ParametricCurveDrawer",
+		  TypedObject::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+ 
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "parametricCurveDrawer.I"
+  
+#endif

+ 626 - 0
panda/src/parametrics/piecewiseCurve.cxx

@@ -0,0 +1,626 @@
+// Filename: piecewiseCurve.cxx
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "piecewiseCurve.h"
+#include "cubicCurveseg.h"
+#include "config_parametrics.h"
+
+#include <datagram.h>
+#include <datagramIterator.h>
+#include <bamWriter.h>
+#include <bamReader.h>
+
+TypeHandle PiecewiseCurve::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PiecewiseCurve::
+PiecewiseCurve() {
+  _last_ti = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+PiecewiseCurve::
+~PiecewiseCurve() {
+  remove_all_curvesegs();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::is_valid
+//       Access: Published, Virtual
+//  Description: Returns true if the curve is defined.  In the case of
+//               a PiecewiseCurve, this means we have at least one
+//               segment.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+is_valid() const {
+  return !_segs.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_max_t
+//       Access: Published, Virtual
+//  Description: Returns the upper bound of t for the entire curve.
+//               The curve is defined in the range 0.0 <= t <=
+//               get_max_t().
+////////////////////////////////////////////////////////////////////
+float PiecewiseCurve::
+get_max_t() const {
+  return _segs.empty() ? 0.0 : _segs.back()._tend;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_point
+//       Access: Published, Virtual
+//  Description: Returns the point of the curve at a given parametric
+//               point t.  Returns true if t is in the valid range 0.0
+//               <= t <= get_max_t(); if t is outside this range, sets
+//               point to the value of the curve at the beginning or
+//               end (whichever is nearer) and returns false.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+get_point(float t, LVecBase3f &point) const {
+  const ParametricCurve *curve;
+  bool result = find_curve(curve, t);
+
+  // We use | instead of || so we won't short-circuit this calculation.
+  return result | curve->get_point(t, point);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_tangent
+//       Access: Published, Virtual
+//  Description: Returns the tangent of the curve at a given parametric
+//               point t.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+get_tangent(float t, LVecBase3f &tangent) const {
+  const ParametricCurve *curve;
+  bool result = find_curve(curve, t);
+
+  // We use | instead of || so we won't short-circuit this calculation.
+  return result | curve->get_tangent(t, tangent);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_2ndtangent
+//       Access: Published, Virtual
+//  Description: Returns the tangent of the first derivative of the
+//               curve at the point t.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+get_2ndtangent(float t, LVecBase3f &tangent2) const {
+  const ParametricCurve *curve;
+  bool result = find_curve(curve, t);
+
+  // We use | instead of || so we won't short-circuit this calculation.
+  return result | curve->get_2ndtangent(t, tangent2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::adjust_point
+//       Access: Published
+//  Description: Recomputes the curve such that it passes through the
+//               point (px, py, pz) at time t, but keeps the same
+//               tangent value at that point.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+adjust_point(float t,
+             float px, float py, float pz) {
+  if (parametrics_cat.is_debug()) {
+    parametrics_cat.debug()
+      << "Adjusting point at " << t << " to " << px << " " << py << " "
+      << pz << "\n";
+  }
+      
+  const ParametricCurve *curve;
+  bool result = find_curve(curve, t);
+
+  if (!result) {
+    cerr << "No curve segment at t = " << t << "\n";
+    return false;
+  }
+
+  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f(),
+                   RT_POINT, t, LVecBase4f(px, py, pz, 1.0),
+                   RT_TANGENT | RT_KEEP_ORIG, t, LVecBase4f(),
+                   RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f());
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::adjust_tangent
+//       Access: Published
+//  Description: Recomputes the curve such that it has the tangent
+//               (tx, ty, tz) at time t, but keeps the same position
+//               at the point.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+adjust_tangent(float t,
+               float tx, float ty, float tz) {
+  const ParametricCurve *curve;
+  bool result = find_curve(curve, t);
+
+  if (!result) {
+    cerr << "No curve segment at t = " << t << "\n";
+    return false;
+  }
+
+  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f(),
+                   RT_POINT | RT_KEEP_ORIG, t, LVecBase4f(),
+                   RT_TANGENT, t, LVecBase4f(tx, ty, tz, 0.0),
+                   RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f());
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::adjust_pt
+//       Access: Published
+//  Description: Recomputes the curve such that it passes through the
+//               point (px, py, pz) with the tangent (tx, ty, tz).
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+adjust_pt(float t,
+          float px, float py, float pz,
+          float tx, float ty, float tz) {
+  const ParametricCurve *curve;
+  bool result = find_curve(curve, t);
+
+  if (!result) {
+    cerr << "No curve segment at t = " << t << "\n";
+    return false;
+  }
+
+  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f(),
+                   RT_POINT, t, LVecBase4f(px, py, pz, 1.0),
+                   RT_TANGENT, t, LVecBase4f(tx, ty, tz, 0.0),
+                   RT_CV | RT_KEEP_ORIG, 0.0, LVecBase4f());
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_pt
+//       Access: Published, Virtual
+//  Description: Simultaneously returns the point and tangent of the
+//               curve at a given parametric point t.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const {
+  const ParametricCurve *curve;
+  bool result = find_curve(curve, t);
+
+  // We use | instead of || so we won't short-circuit this calculation.
+  return result | curve->get_pt(t, point, tangent);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_num_segs
+//       Access: Public
+//  Description: Returns the number of curve segments that make up the
+//               Piecewise curve.
+////////////////////////////////////////////////////////////////////
+int PiecewiseCurve::
+get_num_segs() const {
+  return _segs.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_curveseg
+//       Access: Public
+//  Description: Returns the curve segment corresponding to the given
+//               index.
+////////////////////////////////////////////////////////////////////
+ParametricCurve *PiecewiseCurve::
+get_curveseg(int ti) {
+  assert(ti >= 0 && ti < (int)_segs.size());
+  return _segs[ti]._curve;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::insert_curveseg
+//       Access: Public
+//  Description: Inserts a new curve segment at the indicated index.
+//               The curve segment must have been allocated via
+//               new; it will be freed using delete when it is removed
+//               or the PiecewiseCurve destructs.
+//
+//               If the curve segment is not inserted at the end, its
+//               tlength is subtracted from that of the following
+//               segment, so that the overall length of the curve is
+//               not changed.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+insert_curveseg(int ti, ParametricCurve *seg, float tlength) {
+  if (ti < 0 || ti > (int)_segs.size()) {
+    return false;
+  }
+
+  if (ti == (int)_segs.size()) {
+    _segs.push_back(Curveseg(seg, get_max_t() + tlength));
+
+  } else if (ti==0) {
+    _segs.insert(_segs.begin(),
+                 Curveseg(seg, tlength));
+
+  } else {
+    _segs.insert(_segs.begin() + ti,
+                 Curveseg(seg, _segs[ti-1]._tend + tlength));
+  }
+
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::remove_curveseg
+//       Access: Public
+//  Description: Removes the given curve segment from the curve and
+//               frees it.  Returns true if the segment was defined,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+remove_curveseg(int ti) {
+  if (ti < 0 || ti >= (int)_segs.size()) {
+    return false;
+  }
+
+  float tlength = get_tlength(ti);
+  _segs.erase(_segs.begin() + ti);
+
+  // Now update the _tend figures for everything after the one we
+  // removed.
+  while (ti < (int)_segs.size()) {
+    _segs[ti]._tend -= tlength;
+    ti++;
+  }
+
+  _last_ti = 0;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::remove_all_curvesegs
+//       Access: Public
+//  Description: Removes all curve segments from the curve.
+////////////////////////////////////////////////////////////////////
+void PiecewiseCurve::
+remove_all_curvesegs() {
+  _segs.erase(_segs.begin(), _segs.end());
+  _last_ti = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_tlength
+//       Access: Public
+//  Description: Returns the parametric length of the given segment of
+//               the curve.
+////////////////////////////////////////////////////////////////////
+float PiecewiseCurve::
+get_tlength(int ti) const {
+  assert(ti >= 0 && ti < (int)_segs.size());
+  return (ti==0) ? _segs[ti]._tend : _segs[ti]._tend - _segs[ti-1]._tend;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_tstart
+//       Access: Public
+//  Description: Returns the parametric start of the given segment of
+//               the curve.
+////////////////////////////////////////////////////////////////////
+float PiecewiseCurve::
+get_tstart(int ti) const {
+  assert(ti >= 0 && ti <= (int)_segs.size());
+  return (ti==0) ? 0.0 : _segs[ti-1]._tend;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_tend
+//       Access: Public
+//  Description: Returns the parametric end of the given segment of
+//               the curve.
+////////////////////////////////////////////////////////////////////
+float PiecewiseCurve::
+get_tend(int ti) const {
+  assert(ti >= 0 && ti < (int)_segs.size());
+  return _segs[ti]._tend;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::set_tlength
+//       Access: Public
+//  Description: Sets the parametric length of the given segment of
+//               the curve.  The length of the following segment is
+//               lengthened by the corresponding amount to keep the
+//               overall length of the curve the same.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+set_tlength(int ti, float tlength) {
+  if (ti < 0 || ti >= (int)_segs.size()) {
+    return false;
+  }
+
+  _segs[ti]._tend += tlength - get_tlength(ti);
+  return true;
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::make_nurbs
+//       Access: Public
+//  Description: Defines the curve as a general NURBS curve.  The
+//               order is the degree plus one and must be 1, 2, 3, or
+//               4; cvs is an array of num_cvs points each with a
+//               homogeneous coordinate; knots is an array of
+//               num_cvs+order knot values.
+//
+//               This creates the individual curve segments and sets
+//               up the basis matrices, but does not store the CV's or
+//               knot values so the curve shape is not later
+//               modifiable.
+////////////////////////////////////////////////////////////////////
+void PiecewiseCurve::
+make_nurbs(int order, int num_cvs,
+           const float knots[], const LVecBase4f cvs[]) {
+  remove_all_curvesegs();
+
+  for (int i=0; i<num_cvs - order + 1; i++) {
+    if (knots[i+order] > knots[i+order-1]) {
+      int ti = get_num_segs();
+      bool result =
+        insert_curveseg(ti, new CubicCurveseg(order, knots+i, cvs+i),
+                        knots[i+order] - knots[i+order-1]);
+      assert(result);
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::get_bezier_segs
+//       Access: Public, Virtual
+//  Description: Fills up the indicated vector with a list of
+//               BezierSeg structs that describe the curve.  This
+//               assumes the curve is a PiecewiseCurve of
+//               CubicCurvesegs.  Returns true if successful, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+get_bezier_segs(BezierSegs &bz_segs) const {
+  bz_segs.erase(bz_segs.begin(), bz_segs.end());
+  int i;
+  BezierSeg seg;
+  for (i = 0; i < (int)_segs.size(); i++) {
+    if (!_segs[i]._curve->get_bezier_seg(seg)) {
+      return false;
+    }
+    seg._t = _segs[i]._tend;
+    bz_segs.push_back(seg);
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::rebuild_curveseg
+//       Access: Public, Virtual
+//  Description: Rebuilds the current curve segment (as selected by
+//               the most recent call to find_curve()) according to
+//               the specified properties (see
+//               CubicCurveseg::compute_seg).  Returns true if
+//               possible, false if something goes horribly wrong.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+rebuild_curveseg(int, float, const LVecBase4f &,
+                 int, float, const LVecBase4f &,
+                 int, float, const LVecBase4f &,
+                 int, float, const LVecBase4f &) {
+  cerr << "rebuild_curveseg not implemented for this curve type.\n";
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::find_curve
+//       Access: Protected
+//  Description: Finds the curve corresponding to the given value of
+//               t.  If t is inside the curve's defined range, sets
+//               curve to the appropriate segment, translates t to
+//               [0,1] to index into the segment's coordinate system,
+//               and returns true.  If t is outside the curve's
+//               defined range, sets curve to the nearest segment and
+//               t to the nearest point on this segment, and returns
+//               false.
+////////////////////////////////////////////////////////////////////
+bool PiecewiseCurve::
+find_curve(const ParametricCurve *&curve, float &t) const {
+  // Check the index computed by the last call to find_curve().  If
+  // it's still a reasonable starting value, start searching from
+  // there.  This way, we take advantage of locality of reference: the
+  // search is trivial it is the same segment as last time, or the
+  // next segment after the last one.
+  if (_last_ti>0 && _segs[_last_ti-1]._tend>=t) {
+    // However, if the new t value precedes that of last time, we'll
+    // have to start over.
+
+    // We do some messy casting so we can get away with assigning a
+    // value to a member within a const function.  This assignment
+    // doesn't really count as a const violation since we're just
+    // updating a cached value, not changing any real data of the
+    // class.
+    ((PiecewiseCurve *)this)->_last_ti = 0;
+  }
+
+  int ti;
+  for (ti = _last_ti; ti < (int)_segs.size(); ti++) {
+    if (_segs[ti]._tend+0.00001 > t) {
+      break;
+    }
+  }
+
+  if (ti < (int)_segs.size()) {
+    // Adjust t to the range [0,1).
+    if (ti > 0) {
+      t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend);
+    } else {
+      t /= _segs[0]._tend;
+    }
+  }
+
+  if (t < 0) {
+    // Oops.
+    curve = _segs[0]._curve;
+    t = 0.0;
+    return false;
+  }
+
+  if (ti >= (int)_segs.size() || !_segs[ti]._curve->is_valid()) {
+    assert(ti <= (int)_segs.size());
+
+    // If we're out of bounds, or the curve is undefined, we're probably
+    // screwed.  There's one exception: if we were right on a border between
+    // curves, try the curve before.
+
+    if (ti > 0 && t < _segs[ti-1]._tend+0.0001) {
+      ti--;
+      t = 1.0;
+    }
+
+    if (ti >= (int)_segs.size()) {
+      if (_segs.empty()) {
+        curve = NULL;
+        t = 0.0;
+        return false;
+      } else {
+        curve = _segs.back()._curve;
+        t = 1.0;
+        return false;
+      }
+    } else if (!_segs[ti]._curve->is_valid()) {
+      curve = _segs[ti]._curve;
+      return false;
+    }
+  }
+
+  // Again, some messy casting so we can get away with updating the
+  // cached index value for next time.
+  ((PiecewiseCurve *)this)->_last_ti = ti;
+
+  // Now scale t back into the curve's own valid range.
+  t *= _segs[ti]._curve->get_max_t();
+  curve = _segs[ti]._curve;
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::current_seg_range
+//       Access: Protected
+//  Description: Returns a number in the range [0,1], representing the
+//               conversion of t into the current segment's coordinate
+//               system (the segment last returned by find_curve).
+//               This operation is already performed automatically on
+//               the t passed into find_seg; this function is useful
+//               only to adjust a different value into the same range.
+//
+//               It is an error to call this function if find_curve()
+//               has not yet been called, or if find_curve() returned
+//               false from its previous call.
+////////////////////////////////////////////////////////////////////
+float PiecewiseCurve::
+current_seg_range(float t) const {
+  int ti = _last_ti;
+
+  assert(ti < (int)_segs.size());
+
+  // Adjust t to the range [0,1).
+  if (ti > 0) {
+    t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend);
+  } else {
+    t /= _segs[0]._tend;
+  }
+
+  return t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::write_datagram
+//       Access: Protected, Virtual
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void PiecewiseCurve::
+write_datagram(BamWriter *manager, Datagram &me) {
+  ParametricCurve::write_datagram(manager, me);
+
+  me.add_uint32(_segs.size());
+  size_t i;
+  for (i = 0; i < _segs.size(); i++) {
+    const Curveseg &seg = _segs[i];
+    manager->write_pointer(me, seg._curve);
+    me.add_float64(seg._tend);
+  }
+
+  _last_ti = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::fillin
+//       Access: Protected
+//  Description: Function that reads out of the datagram (or asks
+//               manager to read) all of the data that is needed to
+//               re-create this object and stores it in the appropiate
+//               place
+////////////////////////////////////////////////////////////////////
+void PiecewiseCurve::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  ParametricCurve::fillin(scan, manager);
+
+  size_t num_segs = scan.get_uint32();
+  _segs.reserve(num_segs);
+  size_t i;
+  for (i = 0; i < num_segs; i++) {
+    Curveseg seg;
+    manager->read_pointer(scan, this);
+    seg._curve = (ParametricCurve *)NULL;
+    seg._tend = scan.get_float64();
+    _segs.push_back(seg);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::complete_pointers
+//       Access: Protected, Virtual
+//  Description: Takes in a vector of pointes to TypedWriteable
+//               objects that correspond to all the requests for 
+//               pointers that this object made to BamReader.
+////////////////////////////////////////////////////////////////////
+int PiecewiseCurve::
+complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
+  int used = ParametricCurve::complete_pointers(plist, manager);
+
+  nassertr(used + _segs.size() <= plist.size(), used);
+
+  size_t i;
+  for (i = 0; i < _segs.size(); i++) {
+    DCAST_INTO_R(_segs[i]._curve, plist[used + i], used);
+  }
+  
+  return used + _segs.size();
+}

+ 107 - 0
panda/src/parametrics/piecewiseCurve.h

@@ -0,0 +1,107 @@
+// Filename: piecewiseCurve.h
+// Created by:  drose (04Mar01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PIECEWISECURVE_H
+#define PIECEWISECURVE_H
+
+#include <pandabase.h>
+
+#include "parametricCurve.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PiecewiseCurve
+// Description : A PiecewiseCurve is a curve made up of several curve
+//               segments, connected in a head-to-tail fashion.  The
+//               length of each curve segment in parametric space is
+//               definable.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PiecewiseCurve : public ParametricCurve {
+PUBLISHED:
+  PiecewiseCurve();
+  ~PiecewiseCurve();
+
+  virtual bool is_valid() const;
+  virtual float get_max_t() const;
+
+  virtual bool get_point(float t, LVecBase3f &point) const;
+  virtual bool get_tangent(float t, LVecBase3f &tangent) const;
+  virtual bool get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const;
+  virtual bool get_2ndtangent(float t, LVecBase3f &tangent2) const;
+
+  bool adjust_point(float t, float px, float py, float pz);
+  bool adjust_tangent(float t, float tx, float ty, float tz);
+  bool adjust_pt(float t,
+		 float px, float py, float pz,
+		 float tx, float ty, float tz);
+
+public:
+  int get_num_segs() const;
+
+  ParametricCurve *get_curveseg(int ti);
+  bool insert_curveseg(int ti, ParametricCurve *seg, float tlength);
+
+  bool remove_curveseg(int ti);
+  void remove_all_curvesegs();
+
+  float get_tlength(int ti) const;
+  float get_tstart(int ti) const;
+  float get_tend(int ti) const;
+  bool set_tlength(int ti, float tlength);
+
+  void make_nurbs(int order, int num_cvs,
+                  const float knots[], const LVecBase4f cvs[]);
+
+  virtual bool get_bezier_segs(BezierSegs &bz_segs) const;
+
+  virtual bool
+  rebuild_curveseg(int rtype0, float t0, const LVecBase4f &v0,
+                   int rtype1, float t1, const LVecBase4f &v1,
+                   int rtype2, float t2, const LVecBase4f &v2,
+                   int rtype3, float t3, const LVecBase4f &v3);
+
+protected:
+  bool find_curve(const ParametricCurve *&curve, float &t) const;
+  float current_seg_range(float t) const;
+
+  class Curveseg {
+  public:
+    Curveseg() {}
+    Curveseg(ParametricCurve *c, float t) : _curve(c), _tend(t) {}
+
+    ParametricCurve *_curve;
+    float _tend;
+  };
+
+  vector<Curveseg> _segs;
+  int _last_ti;
+
+
+// TypedWriteable stuff
+protected:
+  virtual void write_datagram(BamWriter *manager, Datagram &me);  
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  virtual int complete_pointers(vector_typedWriteable &plist, 
+				BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParametricCurve::init_type();
+    register_type(_type_handle, "PiecewiseCurve",
+                  ParametricCurve::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+
+#endif

+ 10 - 1
panda/src/sgmanip/nodePathCollection.I

@@ -4,9 +4,18 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePathCollection::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE NodePathCollection::
+~NodePathCollection() {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::ls
 //     Function: NodePathCollection::ls
-//       Access: Public
+//       Access: Published
 //  Description: Lists all the nodes at and below each node in the
 //  Description: Lists all the nodes at and below each node in the
 //               collection hierarchically.
 //               collection hierarchically.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 30 - 30
panda/src/sgmanip/nodePathCollection.cxx

@@ -12,7 +12,7 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::Constructor
 //     Function: NodePathCollection::Constructor
-//       Access: Public
+//       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NodePathCollection::
 NodePathCollection::
@@ -21,7 +21,7 @@ NodePathCollection() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::Copy Constructor
 //     Function: NodePathCollection::Copy Constructor
-//       Access: Public
+//       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NodePathCollection::
 NodePathCollection::
@@ -32,7 +32,7 @@ NodePathCollection(const NodePathCollection &copy) :
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::Copy Assignment Operator
 //     Function: NodePathCollection::Copy Assignment Operator
-//       Access: Public
+//       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePathCollection::
 void NodePathCollection::
@@ -42,7 +42,7 @@ operator = (const NodePathCollection &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::add_path
 //     Function: NodePathCollection::add_path
-//       Access: Public
+//       Access: Published
 //  Description: Adds a new NodePath to the collection.
 //  Description: Adds a new NodePath to the collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePathCollection::
 void NodePathCollection::
@@ -62,11 +62,11 @@ add_path(const NodePath &node_path) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: NodePathCollection::removes_path
-//       Access: Public
-//  Description: Removes a new NodePath from the collection.  Returns
-//               true if the path was removed, false if it was not a
-//               member of the collection.
+//     Function: NodePathCollection::remove_path
+//       Access: Published
+//  Description: Removes the indicated NodePath from the collection.
+//               Returns true if the path was removed, false if it was
+//               not a member of the collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool NodePathCollection::
 bool NodePathCollection::
 remove_path(const NodePath &node_path) {
 remove_path(const NodePath &node_path) {
@@ -99,7 +99,7 @@ remove_path(const NodePath &node_path) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::add_paths_from
 //     Function: NodePathCollection::add_paths_from
-//       Access: Public
+//       Access: Published
 //  Description: Adds all the NodePaths indicated in the other
 //  Description: Adds all the NodePaths indicated in the other
 //               collection to this path.  The other paths are simply
 //               collection to this path.  The other paths are simply
 //               appended to the end of the paths in this list;
 //               appended to the end of the paths in this list;
@@ -116,7 +116,7 @@ add_paths_from(const NodePathCollection &other) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::remove_paths_from
 //     Function: NodePathCollection::remove_paths_from
-//       Access: Public
+//       Access: Published
 //  Description: Removes from this collection all of the NodePaths
 //  Description: Removes from this collection all of the NodePaths
 //               listed in the other collection.
 //               listed in the other collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -135,7 +135,7 @@ remove_paths_from(const NodePathCollection &other) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::remove_duplicate_paths
 //     Function: NodePathCollection::remove_duplicate_paths
-//       Access: Public
+//       Access: Published
 //  Description: Removes any duplicate entries of the same NodePaths
 //  Description: Removes any duplicate entries of the same NodePaths
 //               on this collection.  If a NodePath appears multiple
 //               on this collection.  If a NodePath appears multiple
 //               times, the first appearance is retained; subsequent
 //               times, the first appearance is retained; subsequent
@@ -164,7 +164,7 @@ remove_duplicate_paths() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::has_path
 //     Function: NodePathCollection::has_path
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the indicated NodePath appears in
 //  Description: Returns true if the indicated NodePath appears in
 //               this collection, false otherwise.
 //               this collection, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -180,7 +180,7 @@ has_path(const NodePath &path) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::clear
 //     Function: NodePathCollection::clear
-//       Access: Public
+//       Access: Published
 //  Description: Removes all NodePaths from the collection.
 //  Description: Removes all NodePaths from the collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePathCollection::
 void NodePathCollection::
@@ -190,7 +190,7 @@ clear() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::is_empty
 //     Function: NodePathCollection::is_empty
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if there are no NodePaths in the
 //  Description: Returns true if there are no NodePaths in the
 //               collection, false otherwise.
 //               collection, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -201,7 +201,7 @@ is_empty() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::get_num_paths
 //     Function: NodePathCollection::get_num_paths
-//       Access: Public
+//       Access: Published
 //  Description: Returns the number of NodePaths in the collection.
 //  Description: Returns the number of NodePaths in the collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int NodePathCollection::
 int NodePathCollection::
@@ -211,7 +211,7 @@ get_num_paths() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::get_path
 //     Function: NodePathCollection::get_path
-//       Access: Public
+//       Access: Published
 //  Description: Returns the nth NodePath in the collection.
 //  Description: Returns the nth NodePath in the collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NodePath NodePathCollection::
 NodePath NodePathCollection::
@@ -223,7 +223,7 @@ get_path(int index) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::ls
 //     Function: NodePathCollection::ls
-//       Access: Public
+//       Access: Published
 //  Description: Lists all the nodes at and below each node in the
 //  Description: Lists all the nodes at and below each node in the
 //               collection hierarchically.
 //               collection hierarchically.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -239,18 +239,18 @@ ls(ostream &out, int indent_level) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::find_all_matches
 //     Function: NodePathCollection::find_all_matches
-//       Access: Public
+//       Access: Published
 //  Description: Returns the complete set of all NodePaths that begin
 //  Description: Returns the complete set of all NodePaths that begin
 //               with any NodePath in this collection and can be
 //               with any NodePath in this collection and can be
-//               extended by approx_path_str.  The shortest paths will
-//               be listed first.
+//               extended by path.  The shortest paths will be listed
+//               first.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NodePathCollection NodePathCollection::
 NodePathCollection NodePathCollection::
-find_all_matches(const char *approx_path_str) const {
+find_all_matches(const string &path) const {
   NodePathCollection result;
   NodePathCollection result;
 
 
   FindApproxPath approx_path;
   FindApproxPath approx_path;
-  if (approx_path.add_string(approx_path_str)) {
+  if (approx_path.add_string(path)) {
     if (!is_empty()) {
     if (!is_empty()) {
       FindApproxLevel level;
       FindApproxLevel level;
       for (int i = 0; i < get_num_paths(); i++) {
       for (int i = 0; i < get_num_paths(); i++) {
@@ -267,7 +267,7 @@ find_all_matches(const char *approx_path_str) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::reparent_to
 //     Function: NodePathCollection::reparent_to
-//       Access: Public
+//       Access: Published
 //  Description: Reparents all the NodePaths in the collection to the
 //  Description: Reparents all the NodePaths in the collection to the
 //               indicated node.
 //               indicated node.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -280,7 +280,7 @@ reparent_to(const NodePath &other) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::wrt_reparent_to
 //     Function: NodePathCollection::wrt_reparent_to
-//       Access: Public
+//       Access: Published
 //  Description: Reparents all the NodePaths in the collection to the
 //  Description: Reparents all the NodePaths in the collection to the
 //               indicated node, adjusting each transform so as not to
 //               indicated node, adjusting each transform so as not to
 //               move in world coordinates.
 //               move in world coordinates.
@@ -294,7 +294,7 @@ wrt_reparent_to(const NodePath &other) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::instance_to
 //     Function: NodePathCollection::instance_to
-//       Access: Public
+//       Access: Published
 //  Description: Creates another instance for each NodePath in the
 //  Description: Creates another instance for each NodePath in the
 //               collection under the indicated node; returns the
 //               collection under the indicated node; returns the
 //               collection of new instances.
 //               collection of new instances.
@@ -312,7 +312,7 @@ instance_to(const NodePath &other) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::show
 //     Function: NodePathCollection::show
-//       Access: Public
+//       Access: Published
 //  Description: Shows all NodePaths in the collection.
 //  Description: Shows all NodePaths in the collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePathCollection::
 void NodePathCollection::
@@ -324,7 +324,7 @@ show() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::show
 //     Function: NodePathCollection::show
-//       Access: Public
+//       Access: Published
 //  Description: Hides all NodePaths in the collection.
 //  Description: Hides all NodePaths in the collection.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NodePathCollection::
 void NodePathCollection::
@@ -336,7 +336,7 @@ hide() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::output
 //     Function: NodePathCollection::output
-//       Access: Public
+//       Access: Published
 //  Description: Writes a brief one-line description of the
 //  Description: Writes a brief one-line description of the
 //               NodePathCollection to the indicated output stream.
 //               NodePathCollection to the indicated output stream.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -351,7 +351,7 @@ output(ostream &out) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::write
 //     Function: NodePathCollection::write
-//       Access: Public
+//       Access: Published
 //  Description: Writes a complete multi-line description of the
 //  Description: Writes a complete multi-line description of the
 //               NodePathCollection to the indicated output stream.
 //               NodePathCollection to the indicated output stream.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/sgmanip/nodePathCollection.h

@@ -28,6 +28,7 @@ PUBLISHED:
   NodePathCollection();
   NodePathCollection();
   NodePathCollection(const NodePathCollection &copy);
   NodePathCollection(const NodePathCollection &copy);
   void operator = (const NodePathCollection &copy);
   void operator = (const NodePathCollection &copy);
+  INLINE ~NodePathCollection();
 
 
   void add_path(const NodePath &node_path);
   void add_path(const NodePath &node_path);
   bool remove_path(const NodePath &node_path);
   bool remove_path(const NodePath &node_path);
@@ -45,7 +46,7 @@ PUBLISHED:
   INLINE void ls() const;
   INLINE void ls() const;
   void ls(ostream &out, int indent_level = 0) const;
   void ls(ostream &out, int indent_level = 0) const;
 
 
-  NodePathCollection find_all_matches(const char *approx_path_str) const;
+  NodePathCollection find_all_matches(const string &path) const;
   void reparent_to(const NodePath &other);
   void reparent_to(const NodePath &other);
   void wrt_reparent_to(const NodePath &other);
   void wrt_reparent_to(const NodePath &other);
   NodePathCollection instance_to(const NodePath &other) const;
   NodePathCollection instance_to(const NodePath &other) const;