From 3f8e49bd1e51202ee2e63fa0127a0d2b2ec0751a Mon Sep 17 00:00:00 2001 From: Dave Schuyler Date: Fri, 12 Jan 2001 05:25:16 +0000 Subject: [PATCH] Mark requested curve fitting --- panda/src/parametrics/curveFitter.cxx | 460 ++++++++++++ panda/src/parametrics/curveFitter.h | 101 +++ panda/src/parametrics/hermiteCurve.cxx | 987 +++++++++++++++++++++++++ panda/src/parametrics/hermiteCurve.h | 182 +++++ 4 files changed, 1730 insertions(+) create mode 100644 panda/src/parametrics/curveFitter.cxx create mode 100644 panda/src/parametrics/curveFitter.h create mode 100644 panda/src/parametrics/hermiteCurve.cxx create mode 100644 panda/src/parametrics/hermiteCurve.h diff --git a/panda/src/parametrics/curveFitter.cxx b/panda/src/parametrics/curveFitter.cxx new file mode 100644 index 0000000000..f8afde8eef --- /dev/null +++ b/panda/src/parametrics/curveFitter.cxx @@ -0,0 +1,460 @@ +// Filename: curveFitter.C +// Created by: drose (17Sep98) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// +#include "pointerTo.h" +////#include "pandabase.h" + +#include "curveFitter.h" +#include "config_parametrics.h" +#include "curve.h" +#include "nurbsCurve.h" +#include "hermiteCurve.h" +#include +using namespace std; + +TypeHandle CurveFitter::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::reset +// Access: Public +// Description: Removes all the data points previously added to the +// CurveFitter, and initializes it for a new curve. +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +reset() { + _data.erase(_data.begin(), _data.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::add_point +// Access: Public +// Description: Adds a single sample point. +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +add_point(double t, const LVector3f &point) { + DataPoint dp; + dp._t = t; + dp._point = point; + _data.push_back(dp); +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::sample +// Access: Public +// Description: Generates a series of data points by sampling the +// given curve the indicated number of times. If even +// is true, the sampled t value is taken from the actual +// curve length, as opposed to the parametric length. +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +sample(ParametricCurve *curve, int count, bool even) { + double max_t = curve->get_max_t(); + double t, last_t, d; + DataPoint dp; + + last_t = 0.0; + d = 0.0; + int i; + for (i = 0; i < count; i++) { + t = max_t * (double)i / (double)(count-1); + curve->get_point(t, dp._point); + + if (even) { + d += curve->calc_length(last_t, t); + dp._t = d; + } else { + dp._t = t; + } + + _data.push_back(dp); + last_t = t; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::generate_even +// Access: Public +// Description: Generates a set of data points whose x coordinate is +// evenly distributed across the indicated distance and +// parametric time. Useful before a call to +// compute_timewarp(). +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +generate_even(int count, double net_distance, double net_time) { + double t, d; + DataPoint dp; + int i; + for (i = 0; i < count; i++) { + t = net_time * (double)i / (double)(count-1); + d = net_distance * (double)i / (double)(count-1); + + dp._point.set(d, 0.0, 0.0); + dp._t = t; + _data.push_back(dp); + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::wrap_hpr +// Access: Public +// Description: Assumes the data points collected represent a set of +// HPR coordinates. Resets each data point so that the +// maximum delta between any two consecutive points is +// 180 degrees, which should prevent incorrect HPR +// wrapping. +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +wrap_hpr() { + Data::iterator di; + LVector3f last(0.0, 0.0, 0.0); + LVector3f net(0.0, 0.0, 0.0); + + for (di = _data.begin(); di != _data.end(); ++di) { + int i; + for (i = 0; i < 3; i++) { + (*di)._point[i] += net[i]; + + while (((*di)._point[i] - last[i]) > 180.0) { + (*di)._point[i] -= 360.0; + net[i] -= 360.0; + } + + while (((*di)._point[i] - last[i]) < -180.0) { + (*di)._point[i] += 360.0; + net[i] += 360.0; + } + + last[i] = (*di)._point[i]; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::compute_timewarp +// Access: Public +// Description: Assumes the data points already collected represent +// the distance along a given curve at each unit of +// parametric time (only the X coordinate is used). +// Computes a new set of data points based on the +// indicated curve that will serve as a timewarp to +// produce this effect. +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +compute_timewarp(const ParametricCurve *xyz) { + Data::iterator di; + double last_t = 0.0; + double last_d = 0.0; + double last_ratio = 1.0; + + for (di = _data.begin(); di != _data.end(); ++di) { + double d = (*di)._point[0]; + double t = xyz->compute_t(last_t, d - last_d, + last_t + last_ratio * (d - last_d), + 0.001); + (*di)._point.set(t, 0.0, 0.0); + + /* + // Special HPR computation + { + LVector3f tangent; + LMatrix4f mat; + pfCoord c; + static double last_h = 0.0; + static double h_net = 0.0; + + xyz->get_tangent(t, tangent); + look_at(mat, tangent, LVector3f(0.0, 0.0, 1.0)); + mat.getOrthoCoord(&c); + cerr << "Replacing R " << c.hpr[2] << " with " << (*di)._point[1] << "\n"; + c.hpr[2] = (*di)._point[1]; + c.hpr[0] += h_net; + + // Check the wrap on the heading + if ((c.hpr[0] - last_h) > 180.0) { + c.hpr[0] -= 360.0; + h_net -= 360.0; + } + + if ((c.hpr[0] - last_h) < -180.0) { + c.hpr[0] += 360.0; + h_net += 360.0; + } + + cerr << "H is " << c.hpr[0] << " h_net is " << h_net << "\n"; + last_h = c.hpr[0]; + + (*di)._point = c.hpr; + (*di)._t = t; + } + */ + + if (d != last_d) { + last_ratio = (t - last_t) / (d - last_d); + } + last_t = t; + last_d = d; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::sort_points +// Access: Public +// Description: Sorts all the data points in order by parametric +// time, in case they were added in an incorrect order. +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +sort_points() { + sort(_data.begin(), _data.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::desample +// Access: Public +// Description: Removes sample points in order to reduce the +// complexity of a sampled curve. Keeps one out of +// every factor samples. Also keeps the first and the +// last samples. +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +desample(double factor) { + int in, out; + double count = factor; + + out = 0; + for (in = 0; in < _data.size()-1; in++) { + if (count >= factor) { + _data[out] = _data[in]; + out++; + count -= factor; + } + count += 1.0; + } + + _data[out] = _data.back(); + out++; + + _data.erase(_data.begin() + out, _data.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::compute_tangents +// Access: Public +// Description: Once a set of points has been built, and prior to +// calling MakeHermite() or MakeNurbs(), +// ComputeTangents() must be called to set up the +// tangents correctly (unless the tangents were defined +// as the points were added). +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +compute_tangents(double scale) { + // If the head and tail points match up, close the curve. + bool closed = + (_data.front()._point.almost_equal(_data.back()._point, 0.001)); + + int i; + int len = _data.size(); + + // First, get all the points in the middle, excluding endpoints. + // These are handled the same whether we are closing the curve or + // not. + for (i = 1; i < len-1; i++) { + _data[i]._tangent = + (_data[i+1]._point - _data[i-1]._point) * scale / + (_data[i+1]._t - _data[i-1]._t); + } + + // Now handle the endpoints. + if (closed) { + _data[0]._tangent = _data[len-1]._tangent = + (_data[1]._point - _data[len-2]._point) * scale / + ((_data[1]._t - _data[0]._t) + (_data[len-1]._t - _data[len-2]._t)); + + } else { + _data[0]._tangent = + (_data[1]._point - _data[0]._point) * scale / + ((_data[1]._t - _data[0]._t) * 2.0); + _data[len-1]._tangent = + (_data[len-1]._point - _data[len-2]._point) * scale / + ((_data[len-1]._t - _data[len-2]._t) * 2.0); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::make_hermite +// Access: Public +// Description: Converts the current set of data points into a +// Hermite curve. +//////////////////////////////////////////////////////////////////// +HermiteCurve *CurveFitter:: +make_hermite() const { + HermiteCurve *hc = new HermiteCurve; + + Data::const_iterator di; + for (di = _data.begin(); di != _data.end(); ++di) { + int n = hc->insert_cv((*di)._t); + hc->set_cv_type(n, HC_SMOOTH); + hc->set_cv_point(n, (*di)._point); + hc->set_cv_in(n, (*di)._tangent); + hc->set_cv_out(n, (*di)._tangent); + } + + return hc; +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::make_nurbs +// Access: Public +// Description: Converts the current set of data points into a +// NURBS curve. This gives a smoother curve than +// produced by MakeHermite(). +//////////////////////////////////////////////////////////////////// +NurbsCurve *CurveFitter:: +make_nurbs() const { + if (_data.size() < 2) { + return NULL; + } + +#if 0 + NurbsCurve *nc = new NurbsCurve; + nc->set_order(4); + + // First, we need four CV's to get started. + nc->append_cv(LVector3f(0.0, 0.0, 0.0)); + nc->append_cv(LVector3f(0.0, 0.0, 0.0)); + nc->append_cv(LVector3f(0.0, 0.0, 0.0)); + nc->append_cv(LVector3f(0.0, 0.0, 0.0)); + nc->set_knot(4, _data[1]._t); + + nc->recompute(); + LVector3f junk; + nc->get_point(nc->get_max_t(), junk); // Reference the last segment. + const LVector3f &p0 = _data[0]._point; + LVector3f t0 = _data[0]._tangent * 2.0; + LVector3f t1 = _data[1]._tangent * 2.0; + const LVector3f &p1 = _data[1]._point; + + nc->rebuild_curveseg(RT_POINT, 0.0, pfVec4(p0[0], p0[1], p0[2], 1.0), + RT_TANGENT, 0.0, pfVec4(t0[0], t0[1], t0[2], 0.0), + RT_TANGENT, 1.0, pfVec4(t1[0], t1[1], t1[2], 0.0), + RT_POINT, 1.0, pfVec4(p1[0], p1[1], p1[2], 1.0)); + + int i; + for (i = 2; i < _data.size(); i++) { + cerr << "Adding point " << i << "\n"; + nc->append_cv(LVector3f(0.0, 0.0, 0.0)); + nc->set_knot(i + 3, _data[i]._t); + nc->recompute(); + nc->get_point(nc->get_max_t(), junk); + + /* + const LVector3f &p0 = _data[i-1]._point; + const LVector3f &t0 = _data[i-1]._tangent; + const LVector3f &p1 = _data[i]._point; + const LVector3f &t1 = _data[i]._tangent; + + nc->rebuild_curveseg(RT_POINT, 0.0, pfVec4(p0[0], p0[1], p0[2], 1.0), + RT_TANGENT, 0.0, pfVec4(t0[0], t0[1], t0[2], 0.0), + RT_TANGENT, 1.0, pfVec4(t1[0], t1[1], t1[2], 0.0), + RT_POINT, 1.0, pfVec4(p1[0], p1[1], p1[2], 1.0)); + */ + + const LVector3f &pi = _data[i]._point; + nc->rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(), + RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(), + RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(), + RT_POINT, 1.0, pfVec4(pi[0], pi[1], pi[2], 1.0)); + } + + /* + nc->append_cv(LVector3f(0.0, 0.0, 0.0)); + nc->recompute(); + nc->get_point(nc->get_max_t(), junk); + const LVector3f &pi = _data[_data.size()-1]._point; + nc->rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(), + RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(), + RT_CV | RT_KEEP_ORIG, 0.0, pfVec4(), + RT_CV, 0.0, pfVec4(pi[0], pi[1], pi[2], 1.0)); + */ + + nc->recompute(); + return nc; + +#else + + // We start with the HermiteCurve produced above, then convert it to + // NURBS form. + PT(HermiteCurve) hc = new HermiteCurve(); + NurbsCurve *nc = new NurbsCurve(*hc); + + // Now we even out the knots to smooth out the curve and make + // everything c2 continuous. + + int num_knots = nc->get_num_knots(); + + // We expect this to be a 4th order curve, since we just converted + // it from a Hermite. + assert(nc->get_order() == 4); + assert(num_knots > 0); + + // Now the knot sequence goes something like this: + // 0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 4 + + // We'll consider pairs of knot values beginning at position 3 and + // every third position thereafter. We just even out these values + // between their two neighbors. + + int i; + double k1, k2 = nc->get_knot(num_knots-1); + for (i = 3; i < num_knots - 4; i += 3) { + k1 = nc->get_knot(i-1); + k2 = nc->get_knot(i+2); + nc->set_knot(i, (k1 + k1 + k2) / 3.0); + nc->set_knot(i+1, (k1 + k2 + k2) / 3.0); + } + + // The last knot must have the terminal value. + nc->set_knot(num_knots-4, k2); + + // Finally, recompute the curve. + nc->recompute(); + + return nc; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::print +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +print() const { + output(cerr); +} + +//////////////////////////////////////////////////////////////////// +// Function: CurveFitter::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CurveFitter:: +output(ostream &out) const { + Data::const_iterator di; + + for (di = _data.begin(); di != _data.end(); ++di) { + out << (*di) << "\n"; + } +} + diff --git a/panda/src/parametrics/curveFitter.h b/panda/src/parametrics/curveFitter.h new file mode 100644 index 0000000000..32d18ec56e --- /dev/null +++ b/panda/src/parametrics/curveFitter.h @@ -0,0 +1,101 @@ +// Filename: curveFitter.h +// Created by: drose (17Sep98) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + +#ifndef CURVEFITTER_H +#define CURVEFITTER_H + +#include +////#include +#include "luse.h" +#include +////#include +#include +using namespace std; + +class HermiteCurve; +class NurbsCurve; +class ParametricCurve; + +//////////////////////////////////////////////////////////////////// +// Class : CurveFitter +// Description : +//////////////////////////////////////////////////////////////////// +class CurveFitter { +PUBLISHED: + void reset(); + void add_point(double t, const LVector3f &point); + + void sample(ParametricCurve *curve, int count, bool even); + void generate_even(int count, double net_distance, double net_time); + + void wrap_hpr(); + void compute_timewarp(const ParametricCurve *xyz); + void sort_points(); + void desample(double factor); + + void compute_tangents(double scale); + HermiteCurve *make_hermite() const; + NurbsCurve *make_nurbs() const; + + void print() const; + +public: + void output(ostream &out) const; + + class DataPoint { + public: + DataPoint() : _t(0.0), _point(0.0, 0.0, 0.0), _tangent(0.0, 0.0, 0.0) { } + void output(ostream &out) const { + out << "Time " << _t << " point " << _point << " tan " << _tangent; + } + + int operator < (const DataPoint &other) const { + return _t < other._t; + } + + double _t; + LVector3f _point; + LVector3f _tangent; + }; + + typedef vector Data; + Data _data; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "CurveFitter"); + } + 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; +}; + +inline ostream &operator << (ostream &out, const CurveFitter::DataPoint &dp) { + dp.output(out); + return out; +} + +inline ostream &operator << (ostream &out, const CurveFitter &cf) { + cf.output(out); + return out; +} + +#endif diff --git a/panda/src/parametrics/hermiteCurve.cxx b/panda/src/parametrics/hermiteCurve.cxx new file mode 100644 index 0000000000..e9dca25783 --- /dev/null +++ b/panda/src/parametrics/hermiteCurve.cxx @@ -0,0 +1,987 @@ +// Filename: hermiteCurve.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 "hermiteCurve.h" +#include "config_parametrics.h" +#include "luse.h" + +////#include +#include +#include +////#include +////#include +////#include +////#include +////#include +using namespace std; + +//////////////////////////////////////////////////////////////////// +// Statics +//////////////////////////////////////////////////////////////////// +TypeHandle HermiteCurve::_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 {\n"; + show_vec3(out, indent+2, _p - scale_in * _in / 3.0, + num_dimensions) << "\n"; + Indent(out, indent) << "}\n"; + } + + Indent(out, indent) << " {\n"; + show_vec3(out, indent+2, _p, num_dimensions) << "\n"; + + Indent(out, indent+2) << " continuity-type { "; + switch (_type) { + case HC_CUT: + out << "Cut"; + break; + + case HC_FREE: + out << "Free"; + break; + + case HC_G1: + out << "G1"; + break; + + case HC_SMOOTH: + out << "Smooth"; + break; + }; + out << " }\n"; + + Indent(out, indent) << "}\n"; + + if (show_out) { + Indent(out, indent) << " {\n"; + show_vec3(out, indent+2, _p + scale_out * _out / 3.0, + num_dimensions) << "\n"; + Indent(out, indent) << "}\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::Constructor +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +HermiteCurve:: +HermiteCurve() { +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::Constructor +// Access: Public, Scheme +// Description: Constructs a Hermite from the indicated (possibly +// non-hermite) curve. +//////////////////////////////////////////////////////////////////// +HermiteCurve:: +HermiteCurve(const ParametricCurve &nc) { + if (!nc.convert_to_hermite(*this)) { + parametrics_cat->warning() + << "Cannot make a Hermite from the indicated curve." + << endl; + } +} + + + + + + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::get_num_cvs +// Access: Public, Scheme +// Description: Returns the number of CV's in the curve. +//////////////////////////////////////////////////////////////////// +int HermiteCurve:: +get_num_cvs() const { + return _points.size(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::insert_cv +// Access: Public, Scheme +// Description: Inserts a new CV at the given parametric point along +// the curve. If this parametric point is already on +// the curve, the CV is assigned an index between its +// two neighbors and the indices of all following CV's +// are incremented by 1; its in and out tangents are +// chosen to keep the curve consistent. If the new +// parametric point is beyond the end of the existing +// curve, the curve is extended to meet it and the new +// CV's position, in tangent, and out tangent are set to +// zero. +// +// The index number of the new CV is returned. +//////////////////////////////////////////////////////////////////// +int HermiteCurve:: +insert_cv(double t) { + if (!is_valid() || t >= get_max_t()) { + int n = append_cv(HC_SMOOTH, 0.0, 0.0, 0.0); + set_cv_tstart(n, t); + return n; + } + + t = min(max(t, 0.0), get_max_t()); + + int n = find_cv(t); + nassertr(n+11) { + bool result = + insert_curveseg(_segs.size(), new CubicCurveseg, 1.0); + nassertr(result, 0); + } + + recompute_basis(); + invalidate_all(); + + return _points.size()-1; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::remove_cv +// Access: Public, Scheme +// Description: Removes the given CV from the curve. Returns true if +// the CV existed, false otherwise. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +remove_cv(int n) { + if (n<0 || n>=_points.size()) { + return false; + } + + _points.erase(_points.begin() + n); + if (_segs.size()>0) { + remove_curveseg(_segs.size()-1); + } + + recompute_basis(); + invalidate_all(); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::remove_all_cvs +// Access: Public, Scheme +// Description: Removes all CV's from the curve. +//////////////////////////////////////////////////////////////////// +void HermiteCurve:: +remove_all_cvs() { + _points.erase(_points.begin(), _points.end()); + remove_all_curvesegs(); + + invalidate_all(); +} + + + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::set_cv_type +// Access: Public, Scheme +// Description: Changes the given CV's continuity type. Legal values +// are HC_CUT, HC_FREE, HC_G1, or HC_SMOOTH. +// +// Other than HC_CUT, these have no effect on the actual +// curve; it remains up to user software to impose the +// constraints these imply. +// +// HC_CUT implies a disconnection of the curve; HC_FREE +// imposes no constraints on the tangents; HC_G1 +// forces the tangents to be collinear, and HC_SMOOTH +// forces the tangents to be identical. Setting type +// type to HC_G1 or HC_SMOOTH may adjust the out +// tangent to match the in tangent. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +set_cv_type(int n, int type) { + if (n<0 || n>=_points.size()) { + return false; + } + + bool changed_cut = false; + + if (type!=_points[n]._type) { + changed_cut = (type==HC_CUT || _points[n]._type==HC_CUT); + _points[n].set_type(type); + } + + invalidate_cv(n, changed_cut); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::set_cv_point +// Access: Public, Scheme +// Description: Changes the given CV's position. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +set_cv_point(int n, float x, float y, float z) { + if (n<0 || n>=_points.size()) { + return false; + } + _points[n].set_point(LVector3f(x, y, z)); + invalidate_cv(n, false); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::set_cv_in +// Access: Public, Scheme +// Description: Changes the given CV's in tangent. Depending on the +// continuity type, this may also adjust the out +// tangent. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +set_cv_in(int n, float x, float y, float z) { + if (n<0 || n>=_points.size()) { + return false; + } + _points[n].set_in(LVector3f(x, y, z)); + invalidate_cv(n, false); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::set_cv_out +// Access: Public, Scheme +// Description: Changes the given CV's out tangent. Depending on the +// continuity type, this may also adjust the in +// tangent. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +set_cv_out(int n, float x, float y, float z) { + if (n<0 || n>=_points.size()) { + return false; + } + _points[n].set_out(LVector3f(x, y, z)); + invalidate_cv(n, false); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::set_cv_tstart +// Access: Public, Scheme +// Description: Changes the given CV's parametric starting time. +// This may affect the shape of the curve. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +set_cv_tstart(int n, double tstart) { + if (n<=0 || n>=_points.size()) { + return false; + } + if (fabs(tstart - get_cv_tstart(n)) > 0.0001) { + set_tlength(n-1, tstart - get_tstart(n-1)); + recompute_basis(); + invalidate_all(); + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::set_cv_name +// Access: Public, Scheme +// Description: Changes the name associated with a particular CV. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +set_cv_name(int n, const char *name) { + if (n<0 || n>=_points.size()) { + return false; + } + _points[n].set_name(name); + return true; +} + + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::get_cv_type +// Access: Public, Scheme +// Description: Returns the given CV's continuity type, HC_CUT, +// HC_FREE, HC_G1, or HC_SMOOTH, or 0 if there is +// no such CV. +//////////////////////////////////////////////////////////////////// +int HermiteCurve:: +get_cv_type(int n) const { + if (n<0 || n>=_points.size()) { + return 0; + } + + return _points[n]._type; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::get_cv_point +// Access: Public, Scheme +// Description: Returns the position of the given CV. +//////////////////////////////////////////////////////////////////// +const LVector3f &HermiteCurve:: +get_cv_point(int n) const { + if (n<0 || n>=_points.size()) { + return zero; + } + + return _points[n]._p; +} +void HermiteCurve:: +get_cv_point(int n, LVector3f &v) const { + v = get_cv_point(n); +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::get_cv_in +// Access: Public, Scheme +// Description: Returns the in tangent of the given CV. +//////////////////////////////////////////////////////////////////// +const LVector3f &HermiteCurve:: +get_cv_in(int n) const { + if (n<0 || n>=_points.size() || _points[n-1]._type==HC_CUT) { + return zero; + } + + return _points[n]._in; +} +void HermiteCurve:: +get_cv_in(int n, LVector3f &v) const { + v = get_cv_in(n); +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::get_cv_out +// Access: Public, Scheme +// Description: Returns the out tangent of the given CV. +//////////////////////////////////////////////////////////////////// +const LVector3f &HermiteCurve:: +get_cv_out(int n) const { + if (n<0 || n>=_points.size() || _points[n]._type==HC_CUT) { + return zero; + } + + return _points[n]._out; +} +void HermiteCurve:: +get_cv_out(int n, LVector3f &v) const { + v = get_cv_out(n); +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::get_cv_tstart +// Access: Public, Scheme +// Description: Returns the starting point in parametric space of the +// given CV. +//////////////////////////////////////////////////////////////////// +double HermiteCurve:: +get_cv_tstart(int n) const { + if (n<0) { + return 0.0; + } else if (n>=_points.size()) { + return get_max_t(); + } + + return get_tstart(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::get_cv_name +// Access: Public, Scheme +// Description: Returns the name of the given CV, or NULL. +//////////////////////////////////////////////////////////////////// +const char *HermiteCurve:: +get_cv_name(int n) const { + if (n<0 || n>=_points.size()) { + return NULL; + } + + return _points[n]._name; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::Print +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +void HermiteCurve:: +Print() const { + ostream& out = parametrics_cat->info(); + + switch (get_curve_type()) { + case PCT_T: + out << "Time-warping "; + break; + + case PCT_XYZ: + out << "XYZ "; + break; + + case PCT_HPR: + out << "HPR "; + break; + + default: + break; + } + + out + << "HermiteCurve, " << get_num_cvs() << " CV's. t ranges from 0 to " + << get_max_t() + << endl; +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::print_cv +// Access: Public, Scheme +// Description: +//////////////////////////////////////////////////////////////////// +void HermiteCurve:: +print_cv(int n) const { + ostream& out = parametrics_cat->info(); + out << "CV"; + if (get_cv_name(n)!=NULL) { + out << " " << get_cv_name(n); + } + + out << " at t = " << get_cv_tstart(n) + << "\npoint = " << get_cv_point(n) + << "\nin = " << get_cv_in(n) << " out = " << get_cv_out(n) + << "\ncontinuity type = "; + + switch (get_cv_type(n)) { + case HC_CUT: + out << "Cut"; + break; + + case HC_FREE: + out << "Free"; + break; + + case HC_G1: + out << "G1"; + break; + + case HC_SMOOTH: + out << "Smooth"; + break; + + default: + break; + } + + out << "\n" << endl; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::write_egg +// Access: Public, Scheme +// Description: Writes an egg description of the hermite curve to the +// specified output file. Creates the file if it does +// not exist; appends to the end of it if it does. +// Returns true if the file is successfully written. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +write_egg(const char *filename) { + const char *basename = strrchr(filename, '/'); + basename = (basename==NULL) ? filename : basename+1; + + ofstream out(filename, ios::app); + return write_egg(out, basename); +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::write_egg +// Access: Public, Scheme +// Description: Writes an egg description of the hermite curve to the +// specified output stream. Returns true if the file is +// successfully written. +//////////////////////////////////////////////////////////////////// +bool HermiteCurve:: +write_egg(ostream &out, const char *basename) { + if (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: HermiteCurve::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 HermiteCurve:: +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: HermiteCurve::Output +// Access: Public +// Description: Formats the Hermite curve for output to an Egg file. +//////////////////////////////////////////////////////////////////// +void HermiteCurve:: +Output(ostream &out, int indent) const { + Indent(out, indent) + << " " << get_name() << ".pool {\n"; + + int i; + for (i = 0; i<_points.size(); i++) { + bool show_in = (i!=0); + bool show_out = (i!=_points.size()-1); + _points[i].Output(out, indent+2, _num_dimensions, + show_in, show_out, + show_in ? get_tlength(i-1) : 0.0, + show_out ? get_tlength(i) : 0.0); + } + 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) << " {"; + if (_points.size() > 1) { + for (i = 0; i<_segs.size(); i++) { + if (i%10 == 1) { + out << "\n"; + Indent(out, indent+3); + } + out << " " << get_tlength(i); + } + } + out << "\n"; + Indent(out, indent+2) << "}\n"; + + Indent(out, indent+2) << " {"; + for (i = 1; i<=_points.size() * 3 - 2; i++) { + if (i%10 == 1) { + out << "\n"; + Indent(out, indent+3); + } + out << " " << i; + } + out << "\n"; + Indent(out, indent+4) << " { " << get_name() << ".pool }\n"; + Indent(out, indent+2) << "}\n"; + + Indent(out, indent) << "}\n"; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::Destructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +HermiteCurve:: +~HermiteCurve() { +} + +static void +wrap_hpr(const LVector3f &hpr1, LVector3f &hpr2) { + for (int i = 0; i < 3; i++) { + while ((hpr2[i] - hpr1[i]) > 180.0) { + hpr2[i] -= 360.0; + } + + while ((hpr2[i] - hpr1[i]) < -180.0) { + hpr2[i] += 360.0; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::invalidate_cv +// Access: Protected +// Description: Recomputes the CV and its neighbors appropriately +// after a change. +//////////////////////////////////////////////////////////////////// +void HermiteCurve:: +invalidate_cv(int n, bool redo_all) { + double t1 = 0.0, t2 = get_max_t(); + if (n>0 && _points[n-1]._type!=HC_CUT) { + const HermiteCurveCV &p1 = _points[n-1]; + HermiteCurveCV p2(_points[n]); + if (_curve_type == PCT_HPR) { + wrap_hpr(p1._p, p2._p); + } + get_curveseg(n-1)->hermite_basis(p1, p2, get_tlength(n-1)); + t1 = get_cv_tstart(n-1); + } + + if (n+1 < _points.size()) { + if (_points[n]._type==HC_CUT) { + BezierSeg seg; + seg._v[0] = seg._v[1] = seg._v[2] = seg._v[3] = _points[n]._p; + get_curveseg(n)->bezier_basis(seg); + } else { + const HermiteCurveCV &p1 = _points[n]; + HermiteCurveCV p2(_points[n+1]); + if (_curve_type == PCT_HPR) { + wrap_hpr(p1._p, p2._p); + } + get_curveseg(n)->hermite_basis(p1, p2, get_tlength(n)); + t2 = get_cv_tstart(n+2); + } + } + + if (is_valid()) { + if (redo_all) { + invalidate_all(); + } else { + invalidate(t1, t2); + } + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::find_cv +// Access: Protected +// Description: Finds the CV immediately preceding the given value of +// t. +//////////////////////////////////////////////////////////////////// +int HermiteCurve:: +find_cv(double t) { + nassertr(is_valid(), 0); + + int n; + for (n = 0; n<_segs.size(); n++) { + if (_segs[n]._tend+0.00001 > t) { + break; + } + } + + return n; +} + + +//////////////////////////////////////////////////////////////////// +// Function: HermiteCurve::recompute_basis +// Access: Protected +// Description: Recomputes the coefficients for all the CV's in the +// curve. This is intended to be called whenever the +// CV's have been changed in some drastic way, and it's +// safest just to recompute everything. +//////////////////////////////////////////////////////////////////// +void HermiteCurve:: +recompute_basis() { + int n; + for (n = 0; n<_segs.size(); n++) { + if (_points[n]._type==HC_CUT) { + BezierSeg seg; + seg._v[0] = seg._v[1] = seg._v[2] = seg._v[3] = _points[n]._p; + get_curveseg(n)->bezier_basis(seg); + } else { + const HermiteCurveCV &p1 = _points[n]; + HermiteCurveCV p2(_points[n+1]); + if (_curve_type == PCT_HPR) { + wrap_hpr(p1._p, p2._p); + } + get_curveseg(n)->hermite_basis(p1, p2, get_tlength(n)); + } + } +} + diff --git a/panda/src/parametrics/hermiteCurve.h b/panda/src/parametrics/hermiteCurve.h new file mode 100644 index 0000000000..d457349e48 --- /dev/null +++ b/panda/src/parametrics/hermiteCurve.h @@ -0,0 +1,182 @@ +// Filename: hermiteCurve.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 HERMITECURVE_H +#define HERMITECURVE_H + +//////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////// + +#include "curve.h" + +//////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////// + + +BEGIN_PUBLISH //[ +// Hermite curve continuity types. +#define HC_CUT 1 +// The curve is disconnected at this point. All points between +// this and the following CV are not part of the curve. + +#define HC_FREE 2 +// Tangents are unconstrained. The curve is continuous, but its first +// derivative is not. This is G0 geometric continuity. + +#define HC_G1 3 +// Tangents are constrained to be collinear. The curve's derivative +// is not continuous in parametric space, but its geometric slope is. +// The distinction is mainly relevant in the context of animation +// along the curve--when crossing the join point, direction of motion +// will change continuously, but the speed of motion may change +// suddenly. This is G1 geometric continuity. + +#define HC_SMOOTH 4 +// Tangents are constrained to be identical. The curve and its first +// derivative are continuous in parametric space. When animating +// motion across the join point, speed and direction of motion will +// change continuously. This is C1 parametric continuity. +END_PUBLISH //] + +class NurbsCurve; + +//////////////////////////////////////////////////////////////////// +// Class : HermiteCurveCV +// Description : A single CV of a Hermite curve. Hermite curve CV's +// include an in and out tangent, as well as a position. +//////////////////////////////////////////////////////////////////// +class HermiteCurveCV { +public: + HermiteCurveCV(); + HermiteCurveCV(const HermiteCurveCV &c); + ~HermiteCurveCV(); + + void set_point(const LVector3f &point) { _p = point; } + void set_in(const LVector3f &in); + void set_out(const LVector3f &out); + void set_type(int type); + void set_name(const char *name); + + void Output(ostream &out, int indent, int num_dimensions, + bool show_in, bool show_out, + double scale_in, double scale_out) const; + + LVector3f _p, _in, _out; + int _type; + char *_name; +}; + +//////////////////////////////////////////////////////////////////// +// Class : HermiteCurve +// Description : A parametric curve defined by a sequence of control +// vertices, each with an in and out tangent. +// +// This class is actually implemented as a +// PiecewiseCurve made up of several CubicCurvesegs, +// each of which is created using the hermite_basis() +// method. The HermiteCurve class itself keeps its own +// list of the CV's that are used to define the curve +// (since the CubicCurveseg class doesn't retain these). +//////////////////////////////////////////////////////////////////// +class HermiteCurve : public PiecewiseCurve { +PUBLISHED: + HermiteCurve(); + HermiteCurve(const ParametricCurve &pc); + + int get_num_cvs() const; + + int insert_cv(double t); + int append_cv(int type, float x, float y, float z); + inline int append_cv(int type, const LVector3f &v) { + return append_cv(type, v[0], v[1], v[2]); + } + + bool remove_cv(int n); + void remove_all_cvs(); + + bool set_cv_type(int n, int type); + 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]); + } + bool set_cv_in(int n, float x, float y, float z); + inline bool set_cv_in(int n, const LVector3f &v) { + return set_cv_in(n, v[0], v[1], v[2]); + } + bool set_cv_out(int n, float x, float y, float z); + inline bool set_cv_out(int n, const LVector3f &v) { + return set_cv_out(n, v[0], v[1], v[2]); + } + bool set_cv_tstart(int n, double tstart); + bool set_cv_name(int n, const char *name); + + + int get_cv_type(int n) const; + const LVector3f &get_cv_point(int n) const; + void get_cv_point(int n, LVector3f &v) const; + const LVector3f &get_cv_in(int n) const; + void get_cv_in(int n, LVector3f &v) const; + const LVector3f &get_cv_out(int n) const; + void get_cv_out(int n, LVector3f &v) const; + double get_cv_tstart(int n) const; + const char *get_cv_name(int n) const; + + void Print() const; + void print_cv(int n) const; + + bool write_egg(const char *filename); + bool write_egg(ostream &out, const char *basename); + +public: + + CubicCurveseg *get_curveseg(int ti) { + return (CubicCurveseg *)PiecewiseCurve::get_curveseg(ti); + } + + 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); + + void Output(ostream &out, int indent=0) const; + + virtual ~HermiteCurve();//// +protected: + + void invalidate_cv(int n, bool redo_all); + int find_cv(double t); + void recompute_basis(); + + vector _points; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "HermiteCurve"); + } + 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