Browse Source

joined from add-parametrics

Dave Schuyler 25 years ago
parent
commit
386cc76544

+ 35 - 0
panda/src/parametrics/Sources.pp

@@ -0,0 +1,35 @@
+#define OTHER_LIBS interrogatedb:c dconfig:c dtoolutil:c dtoolbase:c dtool:m
+
+#begin lib_target
+  #define TARGET parametrics
+  #define LOCAL_LIBS \
+    grutil linmath express putil pandabase
+
+  #define SOURCES \
+    config_parametrics.cxx config_parametrics.h \
+    curve.cxx curve.h \
+    curveDrawer.cxx curveDrawer.h \
+    nurbsCurve.cxx nurbsCurve.h \
+    nurbsCurveDrawer.cxx nurbsCurveDrawer.h
+
+  #define INSTALL_HEADERS \
+    config_parametrics.h \
+    curve.h \
+    curveDrawer.h \
+    nurbsCurve.h \
+    nurbsCurveDrawer.h
+
+  #define IGATESCAN all
+
+#end lib_target
+
+#begin test_bin_target
+  #define TARGET test_parametrics
+  #define LOCAL_LIBS \
+    parametrics
+
+  #define SOURCES \
+    test_parametrics.cxx
+
+#end test_bin_target
+

+ 34 - 0
panda/src/parametrics/config_parametrics.cxx

@@ -0,0 +1,34 @@
+// Filename: config_parametrics.cxx
+// Created by:  drose (19Mar00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "config_parametrics.h"
+#include "luse.h"
+#include "typedWriteableReferenceCount.h"
+#include "namable.h"
+#include "curve.h"
+#include "curveDrawer.h"
+#include "nurbsCurve.h"
+#include "nurbsCurveDrawer.h"
+
+#include <dconfig.h>
+#include <get_config_path.h>
+
+Configure(config_parametrics);
+NotifyCategoryDef(parametrics, "");
+
+ConfigureFn(config_parametrics) {
+  ParametricCurve::init_type();
+  PiecewiseCurve::init_type();
+  CubicCurveseg::init_type();
+  ParametricCurveDrawer::init_type();
+  NurbsCurve::init_type();
+  NurbsCurveDrawer::init_type();
+}
+
+const DSearchPath &
+get_parametrics_path() {
+  static DSearchPath *parametrics_path = NULL;
+  return get_config_path("parametrics-path", parametrics_path);
+}

+ 14 - 0
panda/src/parametrics/config_parametrics.h

@@ -0,0 +1,14 @@
+// Filename: config_parametrics.h
+// Created by:  drose (19Mar00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_PARAMETRICS_H
+#define CONFIG_PARAMETRICS_H
+
+#include <pandabase.h>
+#include <notifyCategoryProxy.h>
+
+NotifyCategoryDecl(parametrics, EXPCL_PANDA, EXPTP_PANDA);
+
+#endif

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

@@ -0,0 +1,1597 @@
+// 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 "parametrics.h"
+#include "typedWriteableReferenceCount.h"
+#include "namable.h"
+#include "hermiteCurve.h"
+#include "nurbsCurve.h"
+#include "curveDrawer.h"
+
+#include <DConfig.h>
+
+#include <math.h>
+#include <fstream>
+
+
+////////////////////////////////////////////////////////////////////
+// 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::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;
+  LVector3f 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_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<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 == 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<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 == 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;
+
+  LVector3f 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 = (p[0] - minx) * xscale;
+      r = (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::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::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 LVector3f &p1, const LVector3f &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;
+    LVector3f 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: Public
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void ParametricCurve::
+write_datagram(BamWriter *, Datagram &) {
+  // TODO: write the write_datagram.
+} 
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PiecewiseCurve::
+PiecewiseCurve() {
+  _last_ti = 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     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, LVector3f &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, LVector3f &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, LVector3f &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) {
+  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, LVector4f(),
+		   RT_POINT, t, LVector4f(px, py, pz, 1.0),
+		   RT_TANGENT | RT_KEEP_ORIG, t, LVector4f(),
+		   RT_CV | RT_KEEP_ORIG, 0.0, LVector4f());
+  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, LVector4f(),
+		   RT_POINT | RT_KEEP_ORIG, t, LVector4f(),
+		   RT_TANGENT, t, LVector4f(tx, ty, tz, 0.0),
+		   RT_CV | RT_KEEP_ORIG, 0.0, LVector4f());
+  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, LVector4f(),
+		   RT_POINT, t, LVector4f(px, py, pz, 1.0),
+		   RT_TANGENT, t, LVector4f(tx, ty, tz, 0.0),
+		   RT_CV | RT_KEEP_ORIG, 0.0, LVector4f());
+  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, LVector3f &point, LVector3f &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<_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>_segs.size()) {
+    return false;
+  }
+
+  if (ti==_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>=_segs.size()) {
+    return false;
+  }
+
+  int tlength = get_tlength(ti);
+  _segs.erase(_segs.begin() + ti);
+
+  // Now update the _tend figures for everything after the one we
+  // removed.
+  while (ti < _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<_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<=_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<_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>=_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 LVector4f 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 < _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 LVector4f &,
+		 int, double, const LVector4f &,
+		 int, double, const LVector4f &,
+		 int, double, const LVector4f &) {
+  cerr << "rebuild_curveseg not implemented for this curve type.\n";
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PiecewiseCurve::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+PiecewiseCurve::
+~PiecewiseCurve() {
+  remove_all_curvesegs();
+}
+
+////////////////////////////////////////////////////////////////////
+//     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<_segs.size(); ti++) {
+    if (_segs[ti]._tend+0.00001 > t) {
+      break;
+    }
+  }
+
+  if (ti < _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 >= _segs.size() || !_segs[ti]._curve->is_valid()) {
+    assert(ti <= _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 >= _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 < _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: 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 Hermite segment.
+////////////////////////////////////////////////////////////////////
+CubicCurveseg::
+CubicCurveseg(const HermiteCurveCV &cv0,
+	      const HermiteCurveCV &cv1) {
+  hermite_basis(cv0, cv1);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     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 LVector4f cvs[]) {
+  nurbs_basis(order, knots, cvs);
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     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, LVector3f &point) const {
+  evaluate_point(LVector4f(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, LVector3f &tangent) const {
+  evaluate_vector(LVector4f(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, LVector3f &point, LVector3f &tangent) const {
+  evaluate_point(LVector4f(t*t*t, t*t, t, 1.0), point);
+  evaluate_vector(LVector4f(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, LVector3f &tangent2) const {
+  evaluate_vector(LVector4f(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);
+
+  LVector4f Gx(cv0._p[0], cv1._p[0],
+	    cv0._out[0]*tlength, cv1._in[0]*tlength);
+  LVector4f Gy(cv0._p[1], cv1._p[1],
+	    cv0._out[1]*tlength, cv1._in[1]*tlength);
+  LVector4f 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);
+
+  LVector4f Gx(seg._v[0][0], seg._v[1][0], seg._v[2][0], seg._v[3][0]);
+  LVector4f Gy(seg._v[0][1], seg._v[1][1], seg._v[2][1], seg._v[3][1]);
+  LVector4f 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 LVector4f
+nurbs_blending_function(int order, int i, int j,
+			const double knots[]) {
+  // This is doubly recursive.  Ick.
+  LVector4f 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 {
+    LVector4f bi0 = nurbs_blending_function(order, i, j-1, knots);
+    LVector4f 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);
+  }
+
+
+  LVector4f 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, LVector4f::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 LVector4f 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.
+  LVector4f c[4];
+  for (int i = 0; i < 4; i++) {
+    c[i] = (i<order) ? cvs[i] : LVector4f(0.0, 0.0, 0.0, 0.0);
+  }
+
+  Bx = LVector4f(c[0][0], c[1][0], c[2][0], c[3][0]) * B;
+  By = LVector4f(c[0][1], c[1][1], c[2][1], c[3][1]) * B;
+  Bz = LVector4f(c[0][2], c[1][2], c[2][2], c[3][2]) * B;
+  Bw = LVector4f(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);
+
+  LVector4f Gx = Bx * Mbi;
+  LVector4f Gy = By * Mbi;
+  LVector4f Gz = Bz * Mbi;
+
+  if (rational) {
+    LVector4f 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 LVector4f 
+operator * (const LMatrix4f &M, const LVector4f &v) {
+  return LVector4f(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 LVector4f &v,
+		const LMatrix4f &B, 
+		const LMatrix4f &Bi,
+		const LMatrix4f &G,
+		const LMatrix4f &GB,
+		LMatrix4f &T, LMatrix4f &P) {
+  int keep_orig = (rtype & RT_KEEP_ORIG);
+
+  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, LVector4f(t*t*t, t*t, t, 1.0));
+    if (keep_orig) {
+      LVector4f ov = GB * LVector4f(t*t*t, t*t, t, 1.0);
+
+      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, LVector4f(3.0*t*t, 2.0*t, 1.0, 0.0));
+    if (keep_orig) {
+      LVector4f ov = GB * LVector4f(3.0*t*t, 2.0*t, 1.0, 0.0);
+      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) {
+      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 LVector4f &v0,
+	    int rtype1, double t1, const LVector4f &v1,
+	    int rtype2, double t2, const LVector4f &v2,
+	    int rtype3, double t3, const LVector4f &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::Destructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CubicCurveseg::
+~CubicCurveseg() {
+}

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

@@ -0,0 +1,402 @@
+// 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>
+using namespace std;
+
+#include "typedWriteableReferenceCount.h"
+#include "namable.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 LVector3f LVector3f
+//typedef LVector3f LVector3f;
+
+
+// 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 TypedWriteableReferenceCount,
+    public Namable {
+
+////////////////////////////////////////////////////////////////////
+// Member functions visible to Scheme
+////////////////////////////////////////////////////////////////////
+
+PUBLISHED:
+  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;
+
+public:
+  virtual bool get_point(double t, LVector3f &point) const=0;
+  virtual bool get_tangent(double t, LVector3f &tangent) const=0;
+  virtual bool get_pt(double t, LVector3f &point, LVector3f &tangent) const=0;
+  virtual bool get_2ndtangent(double t, LVector3f &tangent2) const=0;
+
+////////////////////////////////////////////////////////////////////
+// Member functions not visible to Scheme
+////////////////////////////////////////////////////////////////////
+public:
+
+  struct BezierSeg {
+  public:
+    LVector3f _v[4];
+    double _t;
+  };
+  typedef vector<BezierSeg> BezierSegs;
+
+  ParametricCurve();
+
+  virtual void write_datagram(BamWriter *, Datagram &); 
+
+  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:
+  virtual ~ParametricCurve();
+
+  void invalidate(double t1, double t2);
+  void invalidate_all();
+
+  float r_calc_length(double t1, double t2,
+		      const LVector3f &p1, const LVector3f &p2,
+		      float seglength) const;
+
+  typedef list< ParametricCurveDrawer * > DrawerList;
+  DrawerList _drawers;
+  int _curve_type;
+  int _num_dimensions;
+
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "ParametricCurve");
+  }
+  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 {
+public:
+
+////////////////////////////////////////////////////////////////////
+// Member functions visible to Scheme
+////////////////////////////////////////////////////////////////////
+PUBLISHED:
+  virtual bool is_valid() const;
+  virtual double get_max_t() const;
+
+  virtual bool get_point(double t, LVector3f &point) const;
+  virtual bool get_tangent(double t, LVector3f &tangent) const;
+  virtual bool get_pt(double t, LVector3f &point, LVector3f &tangent) const;
+  virtual bool get_2ndtangent(double t, LVector3f &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);
+
+////////////////////////////////////////////////////////////////////
+// Member functions not visible to Scheme
+////////////////////////////////////////////////////////////////////
+public:
+  PiecewiseCurve();
+
+  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 LVector4f cvs[]);
+
+  virtual bool GetBezierSegs(BezierSegs &bz_segs) const;
+
+  virtual bool
+  rebuild_curveseg(int rtype0, double t0, const LVector4f &v0,
+		   int rtype1, double t1, const LVector4f &v1,
+		   int rtype2, double t2, const LVector4f &v2,
+		   int rtype3, double t3, const LVector4f &v3);
+
+protected:
+  ~PiecewiseCurve();
+
+  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) {}
+
+    int descend_pfb(void *handle);
+    int store_pfb(void *handle);
+    int load_pfb(void *handle);
+
+    ParametricCurve *_curve;
+    double _tend;
+  };
+
+  vector<Curveseg> _segs;
+  int _last_ti;
+
+
+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 {
+
+////////////////////////////////////////////////////////////////////
+// Member functions visible to Scheme
+////////////////////////////////////////////////////////////////////
+
+PUBLISHED:
+  virtual bool get_point(double t, LVector3f &point) const;
+  virtual bool get_tangent(double t, LVector3f &tangent) const;
+  virtual bool get_pt(double t, LVector3f &point, LVector3f &tangent) const;
+  virtual bool get_2ndtangent(double t, LVector3f &tangent2) const;
+
+////////////////////////////////////////////////////////////////////
+// Member functions not visible to Scheme
+////////////////////////////////////////////////////////////////////
+public:
+  CubicCurveseg();
+  CubicCurveseg(const LMatrix4f &basis);
+  CubicCurveseg(const HermiteCurveCV &cv0,
+		const HermiteCurveCV &cv1);
+  CubicCurveseg(const BezierSeg &seg);
+  CubicCurveseg(int order, const double knots[], const LVector4f cvs[]);
+
+  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 LVector4f 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 LVector4f &tv, LVector3f &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 LVector4f &tv, LVector3f &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 LVector4f &v0,
+			     int rtype1, double t1, const LVector4f &v1,
+			     int rtype2, double t2, const LVector4f &v2,
+			     int rtype3, double t3, const LVector4f &v3,
+			     const LMatrix4f &B, 
+			     const LMatrix4f &Bi,
+			     LMatrix4f &G);
+
+  LVector4f Bx, By, Bz, Bw;
+  bool rational;
+
+protected:
+  virtual ~CubicCurveseg();
+
+
+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

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

@@ -0,0 +1,733 @@
+// 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 <math.h>
+
+#include "luse.h"
+#include "parametrics.h"
+#include "typedWriteableReferenceCount.h"
+#include "namable.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;
+  _num_ticks = 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(int 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().
+////////////////////////////////////////////////////////////////////
+int 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(int 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.
+////////////////////////////////////////////////////////////////////
+int 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 = floor(_curve->get_max_t() * _num_segs + 0.5);
+
+  double scale = _curve->get_max_t() / (double)(total_segs-1);
+  double t;
+  LVector3f 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);
+
+    LVector3f 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) {
+    LVector3f tangent2;
+    int total_ticks = 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);
+      
+      LVector3f pt = _mapper(point, tangent, t);
+      LVector3f 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;
+  LVector3f 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 = 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);
+
+      LVector3f p = _mapper(point, tangent, t);
+      _lines.set_vertex(i, p);
+    }
+  }
+    
+  if (_num_ticks > 0) {
+    LVector3f tangent2;
+    int total_ticks = 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);
+      
+      LVector3f pt = _mapper(point, tangent, t);
+      LVector3f 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(LVector3fMapper *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.
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+DefaultMap(const LVector3f &point, const LVector3f &, double) {
+  return LVector3f(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.
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+XvsT(const LVector3f &point, const LVector3f &, double t) {
+  return LVector3f(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.
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+iXvsT(const LVector3f &point, const LVector3f &, double t) {
+  return LVector3f(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.
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+YvsT(const LVector3f &point, const LVector3f &, double t) {
+  return LVector3f(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.
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+iYvsT(const LVector3f &point, const LVector3f &, double t) {
+  return LVector3f(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.
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+ZvsT(const LVector3f &point, const LVector3f &, double t) {
+  return LVector3f(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).
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+dXvsT(const LVector3f &, const LVector3f &tangent, double t) {
+  return LVector3f(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).
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+dYvsT(const LVector3f &, const LVector3f &tangent, double t) {
+  return LVector3f(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).
+////////////////////////////////////////////////////////////////////
+LVector3f ParametricCurveDrawer::
+dZvsT(const LVector3f &, const LVector3f &tangent, double t) {
+  return LVector3f(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 LVector3f &tangent, LVector3f &t1, LVector3f &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);
+}

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

@@ -0,0 +1,147 @@
+// 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 <Performer/pr/pfLinMath.h>
+
+////////////////////////////////////////////////////////////////////
+// Defines 
+////////////////////////////////////////////////////////////////////
+
+typedef LVector3f LVector3fMapper(const LVector3f &point, 
+			     const LVector3f &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 {
+
+////////////////////////////////////////////////////////////////////
+// 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(int num_segs);
+  int get_num_segs() const;
+
+  void set_num_ticks(int num_ticks);
+  int 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(LVector3fMapper *mapper);
+
+  static LVector3f DefaultMap(const LVector3f &point, const LVector3f &, double);
+  static LVector3f XvsT(const LVector3f &point, const LVector3f &, double t);
+  static LVector3f iXvsT(const LVector3f &point, const LVector3f &, double t);
+  static LVector3f YvsT(const LVector3f &point, const LVector3f &, double t);
+  static LVector3f iYvsT(const LVector3f &point, const LVector3f &, double t);
+  static LVector3f ZvsT(const LVector3f &point, const LVector3f &, double t);
+  static LVector3f dXvsT(const LVector3f &, const LVector3f &tangent, double t);
+  static LVector3f dYvsT(const LVector3f &, const LVector3f &tangent, double t);
+  static LVector3f dZvsT(const LVector3f &, const LVector3f &tangent, double t);
+
+protected:
+  static void get_tick_marks(const LVector3f &tangent, LVector3f &t1, LVector3f &t2);
+
+  PT(GeomNode) _geom_node;
+  int _num_segs;
+  ParametricCurve *_curve, *_time_curve;
+  LineSegs _lines, _ticks;
+  bool _drawn;
+  int _num_ticks;
+  double _tick_scale;
+  bool _frame_accurate;
+  LVector3fMapper *_mapper;
+
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "ParametricCurveDrawer");
+  }
+  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

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

@@ -0,0 +1,896 @@
+// 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 "luse.h"
+#include "parametrics.h"
+////#include <initReg.h>
+////#include <linMathOutput.h>
+#include <fstream>
+////#include <alloca.h>
+using namespace std;
+
+////////////////////////////////////////////////////////////////////
+// Statics
+////////////////////////////////////////////////////////////////////
+
+TypeHandle NurbsCurve::_type_handle;
+
+static const LVector3f zero = LVector3f(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: Indent
+//  Description: This function duplicates a similar function declared
+//               in eggBasics.C.  It prints a specified number of
+//               spaces to indent each line of output.
+////////////////////////////////////////////////////////////////////
+static ostream &
+Indent(ostream &out, int indent) {
+  for (int i=0; i<indent; i++) {
+    out << ' ';
+  }
+  return out;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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)) {
+    ///DWARNING(dnparametrics) 
+      ///<< "Cannot make a NURBS from the indicated curve."
+      ///<< dnend;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     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 LVector4f 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::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) {
+    ///DWARNING(dnparametrics) 
+      ///<< "Invalid NURBS curve order: " << order << dnend;
+    return;
+  }
+  if (!_cvs.empty()) {
+    ///DWARNING(dnparametrics) 
+      ///<< "Cannot change NURBS curve order on a nonempty curve." << dnend;
+    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 = FindCV(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].
+
+  LVector4f new_cvs[3];
+  int i;
+  for (i = 0; i < _order-1; i++) {
+    int nk = i + k - (_order-1);
+    double ti = GetKnot(nk);
+    double d = GetKnot(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(LVector4f(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 >= _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 >= _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, LVector3f &v) const {
+  if (n < 0 || n >= _cvs.size()) {
+    v = zero;
+  } else {
+    v = (const LVector3f &)_cvs[n]._p / _cvs[n]._p[3];
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurve::get_cv_point
+//       Access: Public, Scheme
+//  Description: Returns the position of the indicated CV.
+////////////////////////////////////////////////////////////////////
+const LVector3f &NurbsCurve::
+get_cv_point(int n) const {
+  if (n < 0 || n >= _cvs.size()) {
+    return zero;
+  } else {
+    static LVector3f result;
+    result = (LVector3f &)_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 >= _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 >= _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 >= _cvs.size()) {
+    return false;
+  }
+
+  _cvs[n-1]._t = t;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurve::get_knot
+//       Access: Public, Scheme
+//  Description: Returns the value of the indicated knot.  There are
+//               get_num_cvs()+_order-1 knot values.
+////////////////////////////////////////////////////////////////////
+double NurbsCurve::
+get_knot(int n) const {
+  return GetKnot(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurve::print
+//       Access: Public, Scheme
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void NurbsCurve::
+print() const {
+  switch (get_curve_type()) {
+  case PCT_T:
+    cout << "Time-warping ";
+    break;
+
+  case PCT_XYZ:
+    cout << "XYZ ";
+    break;
+
+  case PCT_HPR:
+    cout << "HPR ";
+    break;
+
+  default:
+    break;
+  }
+
+  cout << "NurbsCurve, order " << _order << ", " << get_num_cvs()
+       << " CV's.  t ranges from 0 to " << get_max_t() << ".\n";
+
+  cout << "CV's:\n";
+  int i;
+  for (i = 0; i < _cvs.size(); i++) {
+    LVector3f p = (const LVector3f &)_cvs[i]._p / _cvs[i]._p[3];
+    cout << i << ") " << p << ", weight " << _cvs[i]._p[3] << "\n";
+  }
+
+  cout << "Knots: ";
+  for (i = 0; i < _cvs.size()+_order; i++) {
+    cout << " " << GetKnot(i);
+  }
+  cout << "\n" << flush;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurve::print_cv
+//       Access: Public, Scheme
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void NurbsCurve::
+print_cv(int n) const {
+  if (n < 0 || n >= _cvs.size()) {
+    cout << "No such CV: " << n << "\n";
+  } else {
+    LVector3f p = (const LVector3f &)_cvs[n]._p / _cvs[n]._p[3];
+    cout << "CV " << n << ": " << p << ", weight " 
+	 << _cvs[n]._p[3] << "\n";
+  }
+  cout << flush;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     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];
+  LVector4f cvs[4];
+
+  if (_cvs.size() > _order-1) {
+    for (int cv = 0; cv < _cvs.size()-(_order-1); cv++) {
+      if (GetKnot(cv+_order-1) < GetKnot(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] = GetKnot(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 (GetKnot(cv+_order-1) < GetKnot(cv+_order)) {
+      if (seg == _last_ti) {
+	break;
+      }
+      seg++;
+    }
+  }
+
+  // Now copy the cvs and knots in question.
+  double knots[8];
+  LVector4f cvs[4];
+
+  int c;
+  for (c = 0; c < 4; c++) {
+    cvs[c] = (c < _order) ? _cvs[c+cv]._p : LVector4f(0.0, 0.0, 0.0, 0.0);
+  }
+  for (c = 0; c < _order+_order; c++) {
+    knots[c] = GetKnot(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++) {
+    LVector4f &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 LVector4f &v0,
+		 int rtype1, double t1, const LVector4f &v1,
+		 int rtype2, double t2, const LVector4f &v2,
+		 int rtype3, double t3, const LVector4f &v3) {
+  // 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 (GetKnot(cv+_order-1) < GetKnot(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 LVector4f zero(0.0, 0.0, 0.0, 0.0);
+      const LVector4f &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] = GetKnot(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) {
+  const char *basename = strrchr(filename, '/');
+  basename = (basename==NULL) ? filename : basename+1;
+
+  ofstream out(filename, ios::app);
+  return write_egg(out, basename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     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) {
+  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);
+  }
+
+  Output(out);
+
+  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) {
+    ///DWARNING(dnparametrics)
+      ///<< "Cannot splice NURBS curves of different orders!" << dnend;
+    return;
+  }
+
+  double old_t = get_max_t();
+
+  if (t < old_t) {
+    ///DWARNING(dnparametrics)
+      ///<< "Invalid splicing in the middle of a curve!" << dnend;
+    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 < 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::Output
+//       Access: Public
+//  Description: Formats the Nurbs curve for output to an Egg file.
+////////////////////////////////////////////////////////////////////
+void NurbsCurve::
+Output(ostream &out, int indent) const {
+  Indent(out, indent)
+    << "<VertexPool> " << get_name() << ".pool {\n";
+
+  int cv;
+  for (cv = 0; cv < _cvs.size(); cv++) {
+    Indent(out, indent+2) << "<Vertex> " << cv << " { " 
+			  << _cvs[cv]._p << " }\n";
+  }
+  Indent(out, indent) << "}\n";
+    
+  Indent(out, indent) << "<NURBSCurve> " << get_name() << " {\n";
+
+  if (_curve_type!=PCT_NONE) {
+    Indent(out, indent+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+2) << "<Order> { " << _order << " }\n";
+  
+  Indent(out, indent+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+4);
+    }
+    out << GetKnot(k) << " ";
+  }
+  out << "\n";
+  Indent(out, indent+2) << "}\n";
+
+  Indent(out, indent+2) << "<VertexRef> {";
+  for (cv = 0; cv < _cvs.size(); cv++) {
+    if (cv%10 == 1) {
+      out << "\n";
+      Indent(out, indent+3);
+    }
+    out << " " << cv;
+  }
+  out << "\n";
+  Indent(out, indent+4) << "<Ref> { " << get_name() << ".pool }\n";
+  Indent(out, indent+2) << "}\n";
+
+  Indent(out, indent) << "}\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurve::Destructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+NurbsCurve::
+~NurbsCurve() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurve::FindCV
+//       Access: Protected
+//  Description: Finds the first knot whose value is >= t, or -1 if t
+//               is beyond the end of the curve.
+////////////////////////////////////////////////////////////////////
+int NurbsCurve::
+FindCV(double t) {
+  int i;
+  for (i = _order-1; i < _cvs.size(); i++) {
+    if (_cvs[i]._t >= t) {
+      return i+1;
+    }
+  }
+
+  return -1;
+}

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

@@ -0,0 +1,164 @@
+// 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 LVector3f pfVec3
+//typedef pfVec3 LVector3f;
+
+
+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 LVector4f cvs[]);
+
+  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 LVector3f &v) {
+    return append_cv(LVector4f(v[0], v[1], v[2], 1.0));
+  }
+  inline int append_cv(const LVector4f &v) {
+    _cvs.push_back(CV(v, GetKnot(_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 LVector3f &v) {
+    return set_cv_point(n, v[0], v[1], v[2]);
+  }
+  void get_cv_point(int n, LVector3f &v) const;
+  const LVector3f &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;
+
+  void print() const;
+  void print_cv(int n) const;
+
+  bool recompute();
+
+  void normalize_tlength();
+
+  bool write_egg(const char *filename);
+  bool write_egg(ostream &out, const char *basename);
+
+  void splice(double t, const NurbsCurve &other);
+  
+////////////////////////////////////////////////////////////////////
+// Member functions not visible to Scheme
+////////////////////////////////////////////////////////////////////
+public:
+  virtual bool
+  rebuild_curveseg(int rtype0, double t0, const LVector4f &v0,
+		   int rtype1, double t1, const LVector4f &v1,
+		   int rtype2, double t2, const LVector4f &v2,
+		   int rtype3, double t3, const LVector4f &v3);
+
+  CubicCurveseg *get_curveseg(int ti) {
+    return (CubicCurveseg *)PiecewiseCurve::get_curveseg(ti);
+  }
+
+  double
+  GetKnot(int n) const {
+    if (n < _order || _cvs.empty()) {
+      return 0.0;
+    } else if (n-1 >= _cvs.size()) {
+      return _cvs.back()._t;
+    } else {
+      return _cvs[n-1]._t;
+    }
+  }
+
+  void Output(ostream &out, int indent=0) const;
+
+protected:
+  virtual ~NurbsCurve();
+
+  int FindCV(double t);
+
+  int _order;
+
+  class CV {
+  public:
+    CV() {}
+    CV(const LVector4f &p, double t) : _p(p), _t(t) {}
+    LVector4f _p;
+    double _t;
+  };
+
+  vector<CV> _cvs;
+
+
+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

+ 313 - 0
panda/src/parametrics/nurbsCurveDrawer.cxx

@@ -0,0 +1,313 @@
+// Filename: nurbsCurveDrawer.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.
+////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////
+
+#include "nurbsCurveDrawer.h"
+#include "luse.h"
+#include "parametrics.h"
+#include "typedWriteableReferenceCount.h"
+#include "namable.h"
+
+#include <math.h>
+
+
+TypeHandle NurbsCurveDrawer::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::Constructor
+//       Access: Public, Scheme
+//  Description:
+////////////////////////////////////////////////////////////////////
+NurbsCurveDrawer::
+NurbsCurveDrawer(NurbsCurve *curve) : ParametricCurveDrawer(curve) {
+  set_cv_color(1.0, 0.0, 0.0);
+  set_hull_color(1.0, 0.5, 0.5);
+  set_knot_color(0.0, 0.0, 1.0);
+  
+  _cvs.set_thickness(4.0);
+  _hull.set_thickness(1.0);
+  _knots.set_thickness(4.0);
+  
+  _show_cvs = true;
+  _show_hull = true;
+  _show_knots = true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::Destructor
+//       Access: Public, Scheme, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+NurbsCurveDrawer::
+~NurbsCurveDrawer() {
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::set_cv_color
+//       Access: Public, Scheme
+//  Description: Specifies the color of the CV's.
+////////////////////////////////////////////////////////////////////
+void NurbsCurveDrawer::
+set_cv_color(float r, float g, float b) {
+  _cv_color.set(r, g, b);
+  _cvs.set_color(r, g, b);
+
+  if (_drawn) {
+    draw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::set_knot_color
+//       Access: Public, Scheme
+//  Description: Specifies the color of the knots.
+////////////////////////////////////////////////////////////////////
+void NurbsCurveDrawer::
+set_knot_color(float r, float g, float b) {
+  _knot_color.set(r, g, b);
+  _knots.set_color(r, g, b);
+
+  if (_drawn) {
+    draw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::set_hull_color
+//       Access: Public, Scheme
+//  Description: Specifies the color of the convex hull.
+////////////////////////////////////////////////////////////////////
+void NurbsCurveDrawer::
+set_hull_color(float r, float g, float b) {
+  _hull_color.set(r, g, b);
+  _hull.set_color(r, g, b);
+
+  if (_drawn) {
+    draw();
+  }
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::draw
+//       Access: Public, Scheme, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveDrawer::
+draw() {
+  NurbsCurve *nurbs = (NurbsCurve *)_curve;
+  // Make sure the curve is fresh.
+  nurbs->recompute();
+
+  // First, draw the curve itself.
+  if (!ParametricCurveDrawer::draw()) {
+    return false;
+  }
+
+  int i;
+  if (_show_knots) {
+    _num_cvs = nurbs->get_num_cvs();
+    _knotnums.erase(_knotnums.begin(), _knotnums.end());
+
+    double lt = -1.0;
+    int ki = -1;
+    for (i = 0; i < _num_cvs; i++) {
+      double t = nurbs->GetKnot(i);
+      if (t != lt) {
+	lt = t;
+	LVector3f knot_pos, knot_tan;
+	nurbs->get_pt(nurbs->GetKnot(i), knot_pos, knot_tan);
+	_knots.move_to(_mapper(knot_pos, knot_tan, t));
+	ki++;
+      }
+      _knotnums.push_back(ki);
+    }
+
+    _knots.create(_geom_node, _frame_accurate);
+  }
+
+  if (_show_cvs) {
+    _num_cvs = nurbs->get_num_cvs();
+    for (i = 0; i < _num_cvs; i++) {
+      _cvs.move_to(_mapper(nurbs->get_cv_point(i), LVector3f(0.0, 0.0, 0.0),
+			   nurbs->GetKnot(i+1)));
+    }
+
+    _cvs.create(_geom_node, _frame_accurate);
+  }
+
+  if (_show_hull) {
+    _num_cvs = nurbs->get_num_cvs();
+    for (i = 0; i < _num_cvs; i++) {
+      _hull.draw_to(_mapper(nurbs->get_cv_point(i), LVector3f(0.0, 0.0, 0.0),
+			    nurbs->GetKnot(i+1)));
+    }
+
+    _hull.create(_geom_node, _frame_accurate);
+  }
+
+  return true;
+}
+
+    
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::recompute
+//       Access: Public, Scheme, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveDrawer::
+recompute(double t1, double t2, ParametricCurve *curve) {
+  return draw();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::set_show_cvs
+//       Access: Public, Scheme
+//  Description: Sets the flag that hides or shows the CV's.
+////////////////////////////////////////////////////////////////////
+void NurbsCurveDrawer::
+set_show_cvs(bool flag) {
+  _show_cvs = flag;
+  if (_drawn) {
+    draw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::get_show_cvs
+//       Access: Public, Scheme
+//  Description: Returns the current state of the show-CV's flag.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveDrawer::
+get_show_cvs() const {
+  return _show_cvs;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::set_show_hull
+//       Access: Public, Scheme
+//  Description: Sets the flag that hides or shows the convex hull.
+////////////////////////////////////////////////////////////////////
+void NurbsCurveDrawer::
+set_show_hull(bool flag) {
+  _show_hull = flag;
+  if (_drawn) {
+    draw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::get_show_hull
+//       Access: Public, Scheme
+//  Description: Returns the current state of the show-hull flag.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveDrawer::
+get_show_hull() const {
+  return _show_hull;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::set_show_knots
+//       Access: Public, Scheme
+//  Description: Sets the flag that hides or shows the knots.
+////////////////////////////////////////////////////////////////////
+void NurbsCurveDrawer::
+set_show_knots(bool flag) {
+  _show_knots = flag;
+  if (_drawn) {
+    draw();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::get_show_knots
+//       Access: Public, Scheme
+//  Description: Returns the current state of the show-knots flag.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveDrawer::
+get_show_knots() const {
+  return _show_knots;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::hilight
+//       Access: Public, Scheme
+//  Description: Hilights a particular CV by showing it and its knot
+//               in a different color.  Returns true if the CV exists
+//               and has been drawn, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveDrawer::
+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()) {
+    // Also return false if we're out of range.
+    return false;
+  }
+
+  NurbsCurve *nurbs = (NurbsCurve *)_curve;
+  if (_show_cvs) {
+    _cvs.set_vertex_color(n, hr, hg, hb);
+  }
+  if (_show_knots) {
+    ///dnassert(_knotnums[n] >= 0 && _knotnums[n] < _knots.get_num_vertices());
+    _knots.set_vertex_color(_knotnums[n], hr, hg, hb);
+  }
+
+  return true;
+}
+  
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveDrawer::unhilight
+//       Access: Public, Scheme
+//  Description: Removes the hilight previously set on a CV.
+////////////////////////////////////////////////////////////////////
+bool NurbsCurveDrawer::
+unhilight(int n) {
+  if (_curve==NULL || !_curve->is_valid()) {
+    return false;
+  }
+
+  if (n < 0 || n >= _cvs.get_num_vertices()) {
+    return false;
+  }
+
+  NurbsCurve *nurbs = (NurbsCurve *)_curve;
+  if (_show_cvs) {
+    _cvs.set_vertex_color(n, _cv_color[0], _cv_color[1], _cv_color[2]);
+  }
+  if (_show_knots) {
+    ///dnassert(_knotnums[n] >= 0 && _knotnums[n] < _knots.get_num_vertices());
+    _knots.set_vertex_color(_knotnums[n],
+			    _knot_color[0], _knot_color[1], _knot_color[2]);
+  }
+
+  return true;
+}
+

+ 96 - 0
panda/src/parametrics/nurbsCurveDrawer.h

@@ -0,0 +1,96 @@
+// Filename: nurbsCurveDrawer.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 NURBSCURVEDRAWER_H
+#define NURBSCURVEDRAWER_H
+//
+////////////////////////////////////////////////////////////////////
+// Includes 
+////////////////////////////////////////////////////////////////////
+
+#include "curveDrawer.h"
+#include "nurbsCurve.h"
+#include "lineSegs.h"
+
+
+////////////////////////////////////////////////////////////////////
+// Defines 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : NurbsCurveDrawer
+// Description : Draws a Nurbs curve, also drawing in the control
+//               vertices and tangent vectors.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA NurbsCurveDrawer : public ParametricCurveDrawer {
+
+////////////////////////////////////////////////////////////////////
+// Member functions visible to Scheme
+////////////////////////////////////////////////////////////////////
+
+PUBLISHED:
+  NurbsCurveDrawer(NurbsCurve *curve);
+  virtual ~NurbsCurveDrawer();
+
+  void set_cv_color(float r, float g, float b);
+  void set_hull_color(float r, float g, float b);
+  void set_knot_color(float r, float g, float b);
+
+  virtual bool draw();
+  virtual bool recompute(double t1, double t2, ParametricCurve *curve=NULL);
+
+  void set_show_cvs(bool flag);
+  bool get_show_cvs() const;
+  void set_show_hull(bool flag);
+  bool get_show_hull() const;
+  void set_show_knots(bool flag);
+  bool get_show_knots() const;
+
+  bool hilight(int n, float hr=1.0, float hg=1.0, float hb=0.0);
+  bool unhilight(int n);
+
+////////////////////////////////////////////////////////////////////
+// Member functions not visible to Scheme
+////////////////////////////////////////////////////////////////////
+protected:
+  LVector3f _cv_color, _hull_color, _knot_color;
+  int _num_cvs, _num_hull, _num_knots;
+  LineSegs _hull, _knots, _cvs;
+  vector<int> _knotnums;
+
+  bool _show_cvs, _show_hull, _show_knots;
+
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParametricCurveDrawer::init_type();
+    register_type(_type_handle, "NurbsCurveDrawer",
+                  ParametricCurveDrawer::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
+
+

+ 0 - 0
panda/src/parametrics/test_parametrics.cxx