Browse Source

*** empty log message ***

David Rose 25 years ago
parent
commit
f020b438fc

+ 7 - 11
panda/src/parametrics/nurbsCurveDrawer.cxx

@@ -91,26 +91,22 @@ set_hull_color(float r, float g, float b) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool NurbsCurveDrawer::
 bool NurbsCurveDrawer::
 draw() {
 draw() {
+  // First, draw the curve itself.
+  if (!ParametricCurveDrawer::draw()) {
+    return false;
+  }
+
   ParametricCurve *curve = (ParametricCurve *)NULL;
   ParametricCurve *curve = (ParametricCurve *)NULL;
   NurbsCurveInterface *nurbs = (NurbsCurveInterface *)NULL;
   NurbsCurveInterface *nurbs = (NurbsCurveInterface *)NULL;
 
 
   if (_curves != (ParametricCurveCollection *)NULL) {
   if (_curves != (ParametricCurveCollection *)NULL) {
     curve = _curves->get_default_curve();
     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();
+    if (curve != (ParametricCurve *)NULL) {
+      nurbs = curve->get_nurbs_interface();
     }
     }
   }
   }
 
 
-  // First, draw the curve itself.
-  if (!ParametricCurveDrawer::draw()) {
-    return false;
-  }
-
   if (nurbs == (NurbsCurveInterface *)NULL) {
   if (nurbs == (NurbsCurveInterface *)NULL) {
     // The rest of this depends on having an actual NURBS curve.
     // The rest of this depends on having an actual NURBS curve.
     return true;
     return true;

+ 1 - 1
panda/src/parametrics/nurbsCurveInterface.cxx

@@ -168,5 +168,5 @@ convert_to_nurbs(ParametricCurve *nc) const {
     nurbs->set_knot(i, get_knot(i));
     nurbs->set_knot(i, get_knot(i));
   }
   }
 
 
-  return nurbs->recompute();
+  return nc->recompute();
 }
 }

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

@@ -52,8 +52,6 @@ PUBLISHED:
   virtual bool set_knot(int n, float t)=0;
   virtual bool set_knot(int n, float t)=0;
   virtual float get_knot(int n) const=0;
   virtual float get_knot(int n) const=0;
 
 
-  virtual bool recompute()=0;
-
   void write_cv(ostream &out, int n) const;
   void write_cv(ostream &out, int n) const;
 
 
 
 

+ 48 - 3
panda/src/parametrics/parametricCurve.cxx

@@ -263,6 +263,53 @@ find_length(float start_t, float length_offset) const {
   return max_t;
   return max_t;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::adjust_point
+//       Access: Published, Virtual
+//  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 ParametricCurve::
+adjust_point(float, float, float, float) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::adjust_tangent
+//       Access: Published, Virtual
+//  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 ParametricCurve::
+adjust_tangent(float, float, float, float) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::adjust_pt
+//       Access: Published, Virtual
+//  Description: Recomputes the curve such that it passes through the
+//               point (px, py, pz) with the tangent (tx, ty, tz).
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+adjust_pt(float, float, float, float, float, float, float) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurve::recompute
+//       Access: Published, Virtual
+//  Description: Recalculates the curve, if necessary.  Returns
+//               true if the resulting curve is valid, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurve::
+recompute() {
+  return is_valid();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ParametricCurve::stitch
 //     Function: ParametricCurve::stitch
 //       Access: Published, Virtual
 //       Access: Published, Virtual
@@ -547,9 +594,7 @@ convert_to_nurbs(ParametricCurve *nc) const {
     }
     }
   }
   }
 
 
-  nurbs->recompute();
-
-  return nc->is_valid();
+  return nc->recompute();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -72,6 +72,14 @@ PUBLISHED:
   virtual bool get_pt(float t, LVecBase3f &point, 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 get_2ndtangent(float t, LVecBase3f &tangent2) const=0;
 
 
+  virtual bool adjust_point(float t, float px, float py, float pz);
+  virtual bool adjust_tangent(float t, float tx, float ty, float tz);
+  virtual bool adjust_pt(float t,
+			 float px, float py, float pz,
+			 float tx, float ty, float tz);
+
+  virtual bool recompute();
+
   virtual bool stitch(const ParametricCurve *a, const ParametricCurve *b);
   virtual bool stitch(const ParametricCurve *a, const ParametricCurve *b);
 
 
   bool write_egg(Filename filename, CoordinateSystem cs = CS_default);
   bool write_egg(Filename filename, CoordinateSystem cs = CS_default);

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

@@ -73,3 +73,29 @@ evaluate_hpr(float t, LVecBase3f &hpr) const {
   LVecBase3f xyz;
   LVecBase3f xyz;
   return evaluate(t, xyz, hpr);
   return evaluate(t, xyz, hpr);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::adjust_xyz
+//       Access: Published
+//  Description: Adjust the XYZ curve at the indicated time to the new
+//               value.  The curve shape will change correspondingly.
+//               Returns true if successful, false if unable to make
+//               the adjustment for some reason.
+////////////////////////////////////////////////////////////////////
+INLINE bool ParametricCurveCollection::
+adjust_xyz(float t, float x, float y, float z) {
+  return adjust_xyz(t, LVecBase3f(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::adjust_hpr
+//       Access: Published
+//  Description: Adjust the HPR curve at the indicated time to the new
+//               value.  The curve shape will change correspondingly.
+//               Returns true if successful, false if unable to make
+//               the adjustment for some reason.
+////////////////////////////////////////////////////////////////////
+INLINE bool ParametricCurveCollection::
+adjust_hpr(float t, float h, float p, float r) {
+  return adjust_hpr(t, LVecBase3f(h, p, r));
+}

+ 307 - 54
panda/src/parametrics/parametricCurveCollection.cxx

@@ -13,6 +13,7 @@
 #include <compose_matrix.h>
 #include <compose_matrix.h>
 #include <renderRelation.h>
 #include <renderRelation.h>
 #include <string_utils.h>
 #include <string_utils.h>
+#include <look_at.h>
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ParametricCurveCollection::Constructor
 //     Function: ParametricCurveCollection::Constructor
@@ -30,15 +31,23 @@ ParametricCurveCollection() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ParametricCurveCollection::
 void ParametricCurveCollection::
 add_curve(ParametricCurve *curve) {
 add_curve(ParametricCurve *curve) {
+  prepare_add_curve(curve);
   _curves.push_back(curve);
   _curves.push_back(curve);
+  redraw();
+}
 
 
-  DrawerList::iterator di;
-  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
-    ParametricCurveDrawer *drawer = (*di);
-    curve->register_drawer(drawer);
-
-    drawer->redraw();
-  }
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::add_curve
+//       Access: Published
+//  Description: Adds a new ParametricCurve to the collection at the
+//               indicated index.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+add_curve(ParametricCurve *curve, int index) {
+  prepare_add_curve(curve);
+  index = max(min(index, (int)_curves.size()), 0);
+  _curves.insert(_curves.begin() + index, curve);
+  redraw();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -53,11 +62,7 @@ add_curves(Node *node) {
   int num_curves = r_add_curves(node);
   int num_curves = r_add_curves(node);
 
 
   if (num_curves > 0) {
   if (num_curves > 0) {
-    DrawerList::iterator di;
-    for (di = _drawers.begin(); di != _drawers.end(); ++di) {
-      ParametricCurveDrawer *drawer = (*di);
-      drawer->redraw();
-    }
+    redraw();
   }
   }
 
 
   return num_curves;
   return num_curves;
@@ -99,15 +104,9 @@ void ParametricCurveCollection::
 remove_curve(int index) {
 remove_curve(int index) {
   nassertv(index >= 0 && index < (int)_curves.size());
   nassertv(index >= 0 && index < (int)_curves.size());
   PT(ParametricCurve) curve = _curves[index];
   PT(ParametricCurve) curve = _curves[index];
+  prepare_remove_curve(curve);
   _curves.erase(_curves.begin() + 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();
-  }
+  redraw();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -134,18 +133,14 @@ has_curve(ParametricCurve *curve) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ParametricCurveCollection::
 void ParametricCurveCollection::
 clear() {
 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();
+  ParametricCurves::iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    prepare_remove_curve(curve);
   }
   }
   _curves.clear();
   _curves.clear();
+
+  redraw();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -157,7 +152,6 @@ void ParametricCurveCollection::
 clear_timewarps() {
 clear_timewarps() {
   PT(ParametricCurve) xyz_curve = (ParametricCurve *)NULL;
   PT(ParametricCurve) xyz_curve = (ParametricCurve *)NULL;
   PT(ParametricCurve) hpr_curve = (ParametricCurve *)NULL;
   PT(ParametricCurve) hpr_curve = (ParametricCurve *)NULL;
-  ParametricCurves discard;
 
 
   ParametricCurves::iterator ci;
   ParametricCurves::iterator ci;
   for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
   for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
@@ -168,7 +162,7 @@ clear_timewarps() {
       if (xyz_curve == (ParametricCurve *)NULL) {
       if (xyz_curve == (ParametricCurve *)NULL) {
 	xyz_curve = curve;
 	xyz_curve = curve;
       } else {
       } else {
-	discard.push_back(curve);
+	prepare_remove_curve(curve);
       }
       }
       break;
       break;
 
 
@@ -176,12 +170,12 @@ clear_timewarps() {
       if (hpr_curve == (ParametricCurve *)NULL) {
       if (hpr_curve == (ParametricCurve *)NULL) {
 	hpr_curve = curve;
 	hpr_curve = curve;
       } else {
       } else {
-	discard.push_back(curve);
+	prepare_remove_curve(curve);
       }
       }
       break;
       break;
 
 
     default:
     default:
-      discard.push_back(curve);
+      prepare_remove_curve(curve);
     }
     }
   }
   }
 
 
@@ -192,18 +186,7 @@ clear_timewarps() {
     _curves.push_back(hpr_curve);
     _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();
-  }
+  redraw();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -288,6 +271,27 @@ get_num_timewarps() const {
   return count;
   return count;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::get_timewarp_curve
+//       Access: Published
+//  Description: Returns the nth timewarp curve in the collection.
+////////////////////////////////////////////////////////////////////
+ParametricCurve *ParametricCurveCollection::
+get_timewarp_curve(int n) const {
+  ParametricCurves::const_iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    if (curve->get_curve_type() == PCT_T) {
+      if (n == 0) {
+	return curve;
+      }
+      n--;
+    }
+  }
+  nassertr(false, (ParametricCurve *)NULL);
+  return (ParametricCurve *)NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ParametricCurveCollection::make_even
 //     Function: ParametricCurveCollection::make_even
 //       Access: Published
 //       Access: Published
@@ -320,21 +324,43 @@ make_even(float max_t, float segments_per_unit) {
   // approximately the same length as all the others.
   // approximately the same length as all the others.
   CurveFitter fitter;
   CurveFitter fitter;
 
 
-  int num_segments = (int)floor(segments_per_unit * xyz_curve->get_max_t() + 0.5);
+  int num_segments = max(1, (int)floor(segments_per_unit * xyz_curve->get_max_t() + 0.5));
+
+  if (parametrics_cat.is_debug()) {
+    parametrics_cat.debug()
+      << "Calculating length of curve.\n";
+  }
 
 
   float net_length = xyz_curve->calc_length();
   float net_length = xyz_curve->calc_length();
   float segment_length = net_length / (float)num_segments;
   float segment_length = net_length / (float)num_segments;
 
 
+  if (parametrics_cat.is_debug()) {
+    parametrics_cat.debug()
+      << "Curve has total length " << net_length << "; dividing into "
+      << num_segments << " segments of " << segment_length << " units each.\n";
+  }
+
   float last_t = 0.0;
   float last_t = 0.0;
   fitter.add_xyz(0.0, LVecBase3f(last_t, 0.0, 0.0));
   fitter.add_xyz(0.0, LVecBase3f(last_t, 0.0, 0.0));
 
 
   for (int i = 0; i < num_segments; i++) {
   for (int i = 0; i < num_segments; i++) {
     float next_t = xyz_curve->find_length(last_t, segment_length);
     float next_t = xyz_curve->find_length(last_t, segment_length);
-    fitter.add_xyz((float)(i + 1) / (num_segments + 1) * max_t,
+    fitter.add_xyz((float)(i + 1) / num_segments * max_t,
 		   LVecBase3f(next_t, 0.0, 0.0));
 		   LVecBase3f(next_t, 0.0, 0.0));
+
+    if (parametrics_cat.is_spam()) {
+      parametrics_cat.spam()
+	<< "Point " << i << " is at " << next_t << "\n";
+    }
+
     last_t = next_t;
     last_t = next_t;
   }
   }
 
 
+  if (parametrics_cat.is_debug()) {
+    parametrics_cat.debug()
+      << "Done computing segments.\n";
+  }
+
   fitter.compute_tangents(1);
   fitter.compute_tangents(1);
   PT(ParametricCurveCollection) fit = fitter.make_nurbs();
   PT(ParametricCurveCollection) fit = fitter.make_nurbs();
   ParametricCurve *t_curve = fit->get_xyz_curve();
   ParametricCurve *t_curve = fit->get_xyz_curve();
@@ -343,6 +369,96 @@ make_even(float max_t, float segments_per_unit) {
   add_curve(t_curve);
   add_curve(t_curve);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::face_forward
+//       Access: Published
+//  Description: Discards the existing HPR curve and generates a new
+//               one that looks in the direction of travel along the
+//               XYZ curve, based on the XYZ curve's tangent at each
+//               point.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+face_forward(float segments_per_unit) {
+  ParametricCurve *xyz_curve = get_xyz_curve();
+  if (xyz_curve == (ParametricCurve *)NULL) {
+    parametrics_cat.error()
+      << "No XYZ curve for face_forward().\n";
+    return;
+  }
+
+  // Eliminate all the old hpr curves, and also take note of the index
+  // number of the first XYZ curve.
+  int xyz_index = -1;
+  ParametricCurves::const_iterator ci;
+  ParametricCurves new_curves;
+
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    if (curve->get_curve_type() == PCT_HPR) {
+      prepare_remove_curve(curve);
+    } else {
+      if (curve->get_curve_type() == PCT_XYZ && xyz_index == -1) {
+	xyz_index = (ci - _curves.begin());
+      }
+      new_curves.push_back(curve);
+    }
+  }
+  _curves.swap(new_curves);
+
+  // Now divvy up the XYZ curve into num_segments sections, of equal
+  // length in parametric time (based on the timewarp curves).
+  CurveFitter fitter;
+
+  float max_t = get_max_t();
+  int num_segments = (int)floor(segments_per_unit * max_t + 0.5);
+
+  LVecBase3f hpr(0.0, 0.0, 0.0);
+
+  // We compute the first HPR point a little point into the beginning
+  // of the curve, instead of at 0.0, because the tangent at 0.0 is
+  // likely to be zero.
+  determine_hpr(0.001, xyz_curve, hpr);
+  fitter.add_hpr(0.0, hpr);
+
+  for (int i = 0; i < num_segments; i++) {
+    float t = (float)(i + 1) / num_segments * max_t;
+    determine_hpr(t, xyz_curve, hpr);
+    fitter.add_hpr(t, hpr);
+  }
+
+  fitter.wrap_hpr();
+  fitter.compute_tangents(1);
+  PT(ParametricCurveCollection) fit = fitter.make_nurbs();
+  ParametricCurve *hpr_curve = fit->get_hpr_curve();
+  nassertv(hpr_curve != (ParametricCurve *)NULL);
+  add_curve(hpr_curve, xyz_index + 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::reset_max_t
+//       Access: Published
+//  Description: Adjusts the apparent length of the curve by applying
+//               a new timewarp that maps the range [0..max_t] to the
+//               range [0..get_max_t()].  After this call, the curve
+//               collection will contain one more timewarp curve, and
+//               get_max_t() will return the given max_t value.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+reset_max_t(float max_t) {
+  // Define a linear NURBS curve.
+  PT(NurbsCurve) nurbs = new NurbsCurve;
+  nurbs->set_curve_type(PCT_T);
+  nurbs->set_order(2);
+  nurbs->append_cv(LVecBase3f(0.0, 0.0, 0.0));
+  nurbs->append_cv(LVecBase3f(get_max_t(), 0.0, 0.0));
+  nurbs->set_knot(0, 0.0);
+  nurbs->set_knot(1, 0.0);
+  nurbs->set_knot(2, max_t);
+  nurbs->set_knot(3, max_t);
+  nurbs->recompute();
+  add_curve(nurbs);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ParametricCurveCollection::evaluate
 //     Function: ParametricCurveCollection::evaluate
 //       Access: Published
 //       Access: Published
@@ -470,6 +586,72 @@ evaluate_t(float t) const {
   return t0;
   return t0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::adjust_xyz
+//       Access: Published
+//  Description: Adjust the XYZ curve at the indicated time to the new
+//               value.  The curve shape will change correspondingly.
+//               Returns true if successful, false if unable to make
+//               the adjustment for some reason.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+adjust_xyz(float t, const LVecBase3f &xyz) {
+  ParametricCurve *xyz_curve = get_xyz_curve();
+  if (xyz_curve == (ParametricCurve *)NULL) {
+    return false;
+  }
+
+  float t0 = evaluate_t(t);
+  if (t0 >= 0.0 && t < xyz_curve->get_max_t()) {
+    return xyz_curve->adjust_point(t, xyz[0], xyz[1], xyz[2]);
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::adjust_hpr
+//       Access: Published
+//  Description: Adjust the HPR curve at the indicated time to the new
+//               value.  The curve shape will change correspondingly.
+//               Returns true if successful, false if unable to make
+//               the adjustment for some reason.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+adjust_hpr(float t, const LVecBase3f &hpr) {
+  ParametricCurve *hpr_curve = get_hpr_curve();
+  if (hpr_curve == (ParametricCurve *)NULL) {
+    return false;
+  }
+
+  float t0 = evaluate_t(t);
+  if (t0 >= 0.0 && t < hpr_curve->get_max_t()) {
+    return hpr_curve->adjust_point(t, hpr[0], hpr[1], hpr[2]);
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::recompute
+//       Access: Published
+//  Description: Ensures all the curves are freshly computed and
+//               up-to-date.  Returns true if everything is valid,
+//               false if at least one curve is incorrect.
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+recompute() {
+  bool all_ok = true;
+
+  ParametricCurves::iterator ci;
+  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
+    ParametricCurve *curve = (*ci);
+    if (!curve->recompute()) {
+      all_ok = false;
+    }
+  }
+
+  return all_ok;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ParametricCurveCollection::stitch
 //     Function: ParametricCurveCollection::stitch
 //       Access: Published
 //       Access: Published
@@ -660,13 +842,8 @@ r_add_curves(Node *node) {
 
 
   if (node->is_of_type(ParametricCurve::get_class_type())) {
   if (node->is_of_type(ParametricCurve::get_class_type())) {
     ParametricCurve *curve = DCAST(ParametricCurve, node);
     ParametricCurve *curve = DCAST(ParametricCurve, node);
+    prepare_add_curve(curve);
     _curves.push_back(curve);
     _curves.push_back(curve);
-
-    DrawerList::iterator di;
-    for (di = _drawers.begin(); di != _drawers.end(); ++di) {
-      ParametricCurveDrawer *drawer = (*di);
-      curve->register_drawer(drawer);
-    }
     num_curves++;
     num_curves++;
   }
   }
 
 
@@ -719,3 +896,79 @@ unregister_drawer(ParametricCurveDrawer *drawer) {
     curve->unregister_drawer(drawer);
     curve->unregister_drawer(drawer);
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::determine_hpr
+//       Access: Private
+//  Description: Computes the orientation at the given point in time,
+//               based on the tangent of the XYZ curve.  Returns true
+//               if the orientation can be determined, or false if it
+//               cannot (in which case hpr is left unchanged).
+////////////////////////////////////////////////////////////////////
+bool ParametricCurveCollection::
+determine_hpr(float t, ParametricCurve *xyz_curve, LVecBase3f &hpr) const {
+  float t0 = evaluate_t(t);
+
+  LVector3f tangent;
+  if (!xyz_curve->get_tangent(t0, tangent)) {
+    return false;
+  }
+
+  if (tangent.length_squared() == 0.0) {
+    return false;
+  }
+
+  LMatrix3f mat;
+  look_at(mat, tangent);
+
+  LVecBase3f scale;
+  decompose_matrix(mat, scale, hpr);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::prepare_add_curve
+//       Access: Private
+//  Description: Registers the curve with the list of drawers that
+//               share this collection, in preparation for adding it
+//               to the _curves list.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+prepare_add_curve(ParametricCurve *curve) {
+  DrawerList::iterator di;
+  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+    ParametricCurveDrawer *drawer = (*di);
+    curve->register_drawer(drawer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::prepare_remove_curve
+//       Access: Private
+//  Description: Unregisters the curve with the list of drawers that
+//               share this collection, in preparation for removing it
+//               from the _curves list.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+prepare_remove_curve(ParametricCurve *curve) {
+  DrawerList::iterator di;
+  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+    ParametricCurveDrawer *drawer = (*di);
+    curve->unregister_drawer(drawer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParametricCurveCollection::redraw
+//       Access: Private
+//  Description: Calls redraw() on all drawers that share this
+//               collection.
+////////////////////////////////////////////////////////////////////
+void ParametricCurveCollection::
+redraw() {
+  DrawerList::iterator di;
+  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
+    ParametricCurveDrawer *drawer = (*di);
+    drawer->redraw();
+  }
+}

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

@@ -35,6 +35,7 @@ PUBLISHED:
   INLINE ~ParametricCurveCollection();
   INLINE ~ParametricCurveCollection();
 
 
   void add_curve(ParametricCurve *curve);
   void add_curve(ParametricCurve *curve);
+  void add_curve(ParametricCurve *curve, int index);
   int add_curves(Node *node);
   int add_curves(Node *node);
   bool remove_curve(ParametricCurve *curve);
   bool remove_curve(ParametricCurve *curve);
   void remove_curve(int index);
   void remove_curve(int index);
@@ -49,10 +50,13 @@ PUBLISHED:
   ParametricCurve *get_hpr_curve() const;
   ParametricCurve *get_hpr_curve() const;
   ParametricCurve *get_default_curve() const;
   ParametricCurve *get_default_curve() const;
   int get_num_timewarps() const;
   int get_num_timewarps() const;
+  ParametricCurve *get_timewarp_curve(int n) const;
 
 
   INLINE float get_max_t() const;
   INLINE float get_max_t() const;
 
 
   void make_even(float max_t, float segments_per_unit);
   void make_even(float max_t, float segments_per_unit);
+  void face_forward(float segments_per_unit);
+  void reset_max_t(float max_t);
 
 
   bool evaluate(float t, LVecBase3f &xyz, LVecBase3f &hpr) const;
   bool evaluate(float t, LVecBase3f &xyz, LVecBase3f &hpr) const;
   bool evaluate(float t, LMatrix4f &result, CoordinateSystem cs = CS_default) const;
   bool evaluate(float t, LMatrix4f &result, CoordinateSystem cs = CS_default) const;
@@ -61,6 +65,13 @@ PUBLISHED:
   INLINE bool evaluate_xyz(float t, LVecBase3f &xyz) const;
   INLINE bool evaluate_xyz(float t, LVecBase3f &xyz) const;
   INLINE bool evaluate_hpr(float t, LVecBase3f &hpr) const;
   INLINE bool evaluate_hpr(float t, LVecBase3f &hpr) const;
 
 
+  INLINE bool adjust_xyz(float t, float x, float y, float z);
+  bool adjust_xyz(float t, const LVecBase3f &xyz);
+  INLINE bool adjust_hpr(float t, float h, float p, float r);
+  bool adjust_hpr(float t, const LVecBase3f &xyz);
+
+  bool recompute();
+
   bool stitch(const ParametricCurveCollection *a, 
   bool stitch(const ParametricCurveCollection *a, 
 	      const ParametricCurveCollection *b);
 	      const ParametricCurveCollection *b);
 
 
@@ -75,6 +86,12 @@ public:
   void register_drawer(ParametricCurveDrawer *drawer);
   void register_drawer(ParametricCurveDrawer *drawer);
   void unregister_drawer(ParametricCurveDrawer *drawer);
   void unregister_drawer(ParametricCurveDrawer *drawer);
 
 
+private:
+  bool determine_hpr(float t, ParametricCurve *xyz_curve, LVecBase3f &hpr) const;
+  void prepare_add_curve(ParametricCurve *curve);
+  void prepare_remove_curve(ParametricCurve *curve);
+  void redraw();
+
 private:
 private:
   typedef vector<PT(ParametricCurve)> ParametricCurves;
   typedef vector<PT(ParametricCurve)> ParametricCurves;
   ParametricCurves _curves;
   ParametricCurves _curves;

+ 17 - 5
panda/src/parametrics/parametricCurveDrawer.cxx

@@ -297,16 +297,26 @@ draw() {
   // First, remove the old drawing, if any.
   // First, remove the old drawing, if any.
   hide();
   hide();
 
 
+  _drawn = true;
+
   // If there's no curve, draw nothing and return false.
   // If there's no curve, draw nothing and return false.
-  if (_curves == (ParametricCurveCollection *)NULL ||
-      _curves->get_default_curve() == (ParametricCurve *)NULL) {
+  if (_curves == (ParametricCurveCollection *)NULL) {
+    return false;
+  }
+
+  ParametricCurve *xyz_curve = _curves->get_default_curve();
+  if (xyz_curve == (ParametricCurve *)NULL) {
     return false;
     return false;
   }
   }
 
 
   // Otherwise, let's go to town!
   // Otherwise, let's go to town!
+
+  // Make sure the curve(s) are fresh.
+  _curves->recompute();
+
   int total_segs = (int)floor(_curves->get_max_t() * _num_segs + 0.5);
   int total_segs = (int)floor(_curves->get_max_t() * _num_segs + 0.5);
 
 
-  float max_t = get_max_t();
+  float max_t = xyz_curve->get_max_t();
   float scale = max_t / (float)(total_segs-1);
   float scale = max_t / (float)(total_segs-1);
   float t;
   float t;
   LVecBase3f point;
   LVecBase3f point;
@@ -318,7 +328,7 @@ draw() {
   for (i = 0; i < total_segs; i++) {
   for (i = 0; i < total_segs; i++) {
     t = (float)i * scale;
     t = (float)i * scale;
 
 
-    next_in = _curves->evaluate_xyz(t, point);
+    next_in = xyz_curve->get_point(t, point);
 
 
     if (!next_in || !last_in) {
     if (!next_in || !last_in) {
       _lines.move_to(point);
       _lines.move_to(point);
@@ -329,7 +339,9 @@ draw() {
   }
   }
 
 
   _lines.create(_geom_node, _frame_accurate);
   _lines.create(_geom_node, _frame_accurate);
-  _drawn = true;
+
+  max_t = get_max_t();
+  scale = max_t / (float)(total_segs-1);
 
 
   // Now draw the time tick marks.
   // Now draw the time tick marks.
   if (_num_ticks > 0.0) {
   if (_num_ticks > 0.0) {

+ 3 - 3
panda/src/parametrics/piecewiseCurve.cxx

@@ -112,7 +112,7 @@ get_2ndtangent(float t, LVecBase3f &tangent2) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PiecewiseCurve::adjust_point
 //     Function: PiecewiseCurve::adjust_point
-//       Access: Published
+//       Access: Published, Virtual
 //  Description: Recomputes the curve such that it passes through the
 //  Description: Recomputes the curve such that it passes through the
 //               point (px, py, pz) at time t, but keeps the same
 //               point (px, py, pz) at time t, but keeps the same
 //               tangent value at that point.
 //               tangent value at that point.
@@ -143,7 +143,7 @@ adjust_point(float t,
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PiecewiseCurve::adjust_tangent
 //     Function: PiecewiseCurve::adjust_tangent
-//       Access: Published
+//       Access: Published, Virtual
 //  Description: Recomputes the curve such that it has the tangent
 //  Description: Recomputes the curve such that it has the tangent
 //               (tx, ty, tz) at time t, but keeps the same position
 //               (tx, ty, tz) at time t, but keeps the same position
 //               at the point.
 //               at the point.
@@ -168,7 +168,7 @@ adjust_tangent(float t,
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PiecewiseCurve::adjust_pt
 //     Function: PiecewiseCurve::adjust_pt
-//       Access: Published
+//       Access: Published, Virtual
 //  Description: Recomputes the curve such that it passes through the
 //  Description: Recomputes the curve such that it passes through the
 //               point (px, py, pz) with the tangent (tx, ty, tz).
 //               point (px, py, pz) with the tangent (tx, ty, tz).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 8 - 5
panda/src/parametrics/piecewiseCurve.h

@@ -22,6 +22,9 @@ PUBLISHED:
   PiecewiseCurve();
   PiecewiseCurve();
   ~PiecewiseCurve();
   ~PiecewiseCurve();
 
 
+public:
+  // These functions are all inherited from ParametricCurve, and need
+  // not be re-published.
   virtual bool is_valid() const;
   virtual bool is_valid() const;
   virtual float get_max_t() const;
   virtual float get_max_t() const;
 
 
@@ -30,11 +33,11 @@ PUBLISHED:
   virtual bool get_pt(float t, LVecBase3f &point, 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 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);
+  virtual bool adjust_point(float t, float px, float py, float pz);
+  virtual bool adjust_tangent(float t, float tx, float ty, float tz);
+  virtual bool adjust_pt(float t,
+			 float px, float py, float pz,
+			 float tx, float ty, float tz);
 
 
 public:
 public:
   int get_num_segs() const;
   int get_num_segs() const;