From 386cc765441e4c6737cd4373533d12ecefac48ce Mon Sep 17 00:00:00 2001 From: Dave Schuyler Date: Fri, 22 Dec 2000 21:14:11 +0000 Subject: [PATCH] joined from add-parametrics --- panda/src/parametrics/Sources.pp | 35 + panda/src/parametrics/config_parametrics.cxx | 34 + panda/src/parametrics/config_parametrics.h | 14 + panda/src/parametrics/curve.cxx | 1597 ++++++++++++++++++ panda/src/parametrics/curve.h | 402 +++++ panda/src/parametrics/curveDrawer.cxx | 733 ++++++++ panda/src/parametrics/curveDrawer.h | 147 ++ panda/src/parametrics/nurbsCurve.cxx | 896 ++++++++++ panda/src/parametrics/nurbsCurve.h | 164 ++ panda/src/parametrics/nurbsCurveDrawer.cxx | 313 ++++ panda/src/parametrics/nurbsCurveDrawer.h | 96 ++ panda/src/parametrics/test_parametrics.cxx | 0 12 files changed, 4431 insertions(+) create mode 100644 panda/src/parametrics/Sources.pp create mode 100644 panda/src/parametrics/config_parametrics.cxx create mode 100644 panda/src/parametrics/config_parametrics.h create mode 100644 panda/src/parametrics/curve.cxx create mode 100644 panda/src/parametrics/curve.h create mode 100644 panda/src/parametrics/curveDrawer.cxx create mode 100644 panda/src/parametrics/curveDrawer.h create mode 100644 panda/src/parametrics/nurbsCurve.cxx create mode 100644 panda/src/parametrics/nurbsCurve.h create mode 100644 panda/src/parametrics/nurbsCurveDrawer.cxx create mode 100644 panda/src/parametrics/nurbsCurveDrawer.h create mode 100644 panda/src/parametrics/test_parametrics.cxx diff --git a/panda/src/parametrics/Sources.pp b/panda/src/parametrics/Sources.pp new file mode 100644 index 0000000000..91d2d9a59b --- /dev/null +++ b/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 + diff --git a/panda/src/parametrics/config_parametrics.cxx b/panda/src/parametrics/config_parametrics.cxx new file mode 100644 index 0000000000..f4b6e8f1ea --- /dev/null +++ b/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 +#include + +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); +} diff --git a/panda/src/parametrics/config_parametrics.h b/panda/src/parametrics/config_parametrics.h new file mode 100644 index 0000000000..0d8dbd3771 --- /dev/null +++ b/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 +#include + +NotifyCategoryDecl(parametrics, EXPCL_PANDA, EXPTP_PANDA); + +#endif diff --git a/panda/src/parametrics/curve.cxx b/panda/src/parametrics/curve.cxx new file mode 100644 index 0000000000..73d9403ab4 --- /dev/null +++ b/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 + +#include +#include + + +//////////////////////////////////////////////////////////////////// +// 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=0 && r=0 && cdisable(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 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=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 +#include +#include +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 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 _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 diff --git a/panda/src/parametrics/curveDrawer.cxx b/panda/src/parametrics/curveDrawer.cxx new file mode 100644 index 0000000000..a1ff872500 --- /dev/null +++ b/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 + +#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; iget_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; iget_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=0 && n2get_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); +} diff --git a/panda/src/parametrics/curveDrawer.h b/panda/src/parametrics/curveDrawer.h new file mode 100644 index 0000000000..51d783a88f --- /dev/null +++ b/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 + +//////////////////////////////////////////////////////////////////// +// 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 diff --git a/panda/src/parametrics/nurbsCurve.cxx b/panda/src/parametrics/nurbsCurve.cxx new file mode 100644 index 0000000000..008ba2ce3c --- /dev/null +++ b/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 +////#include +#include +////#include +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 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 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) + << " " << get_name() << ".pool {\n"; + + int cv; + for (cv = 0; cv < _cvs.size(); cv++) { + Indent(out, indent+2) << " " << cv << " { " + << _cvs[cv]._p << " }\n"; + } + Indent(out, indent) << "}\n"; + + Indent(out, indent) << " " << get_name() << " {\n"; + + if (_curve_type!=PCT_NONE) { + Indent(out, indent+2) << " 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 << " }\n"; + + Indent(out, indent+2) << " {"; + 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) << " {"; + 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) << " { " << 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; +} diff --git a/panda/src/parametrics/nurbsCurve.h b/panda/src/parametrics/nurbsCurve.h new file mode 100644 index 0000000000..74776e8cbb --- /dev/null +++ b/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 _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 diff --git a/panda/src/parametrics/nurbsCurveDrawer.cxx b/panda/src/parametrics/nurbsCurveDrawer.cxx new file mode 100644 index 0000000000..ae096ba4cd --- /dev/null +++ b/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 + + +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; +} + diff --git a/panda/src/parametrics/nurbsCurveDrawer.h b/panda/src/parametrics/nurbsCurveDrawer.h new file mode 100644 index 0000000000..52cc9c7cf8 --- /dev/null +++ b/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 _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 + + diff --git a/panda/src/parametrics/test_parametrics.cxx b/panda/src/parametrics/test_parametrics.cxx new file mode 100644 index 0000000000..e69de29bb2