Mark requested curve fitting

This commit is contained in:
Dave Schuyler 2001-01-12 05:25:16 +00:00
parent f450390286
commit 3f8e49bd1e
4 changed files with 1730 additions and 0 deletions

View File

@ -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 <algorithm>
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";
}
}

View File

@ -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 <typedef.h>
////#include <linMathOutput.h>
#include "luse.h"
#include <vector>
////#include <Performer/pr/pfLinMath.h>
#include <iostream>
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<DataPoint> 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

View File

@ -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 <initReg.h>
#include <math.h>
#include <fstream>
////#include <alloca.h>
////#include <Performer/pf/pfBuffer.h>
////#include <DConfig.h>
////#include <perfalloc.h>
////#include <pfb_util.h>
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<indent; i++) {
out << ' ';
}
return out;
}
////////////////////////////////////////////////////////////////////
// Function: show_vec3
// Description: This function writes a LVector3f, with a specified
// number of significant dimensions.
////////////////////////////////////////////////////////////////////
static ostream &
show_vec3(ostream &out, int indent, const LVector3f &v, int num_dimensions) {
Indent(out, indent) << v[0];
for (int i = 1; i<num_dimensions; i++) {
out << " " << v[i];
}
return out;
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
HermiteCurveCV::
HermiteCurveCV() {
_name = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::copy_constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
HermiteCurveCV::
HermiteCurveCV(const HermiteCurveCV &c) :
_p(c._p), _in(c._in), _out(c._out),
_type(c._type) {
if (c._name==NULL) {
_name = NULL;
} else {
_name = new char[strlen(c._name)+1];
strcpy(_name, c._name);
}
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
HermiteCurveCV::
~HermiteCurveCV() {
if (_name != NULL) {
delete [] _name;
}
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::set_in
// Access: Public
// Description: Sets the CV's in tangent.
////////////////////////////////////////////////////////////////////
void HermiteCurveCV::
set_in(const LVector3f &in) {
_in = in;
/*
double l;
switch (_type) {
case HC_G1:
l = _in.length();
if (l!=0.0) {
_out = _in * _out.length() / l;
}
break;
case HC_SMOOTH:
_out = _in;
break;
}
*/
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::set_out
// Access: Public
// Description: Sets the CV's out tangent.
////////////////////////////////////////////////////////////////////
void HermiteCurveCV::
set_out(const LVector3f &out) {
_out = out;
/*
double l;
switch (_type) {
case HC_G1:
l = _out.length();
if (l!=0.0) {
_in = _out * _in.length() / l;
}
break;
case HC_SMOOTH:
_in = _out;
break;
}
*/
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::set_type
// Access: Public
// Description: Sets the continuity type of the CV. Values may be
// HC_CUT, indicating a discontinous break in the curve,
// HC_FREE, for unconstrained in and out tangents,
// HC_G1, for in and out tangents constrained to be
// collinear, and HC_SMOOTH, for in and out tangents
// constrained to be equal. Other than HC_CUT, these
// are for documentation only; setting this has no
// direct effect on the tangents.
////////////////////////////////////////////////////////////////////
void HermiteCurveCV::
set_type(int type) {
_type = type;
/*
switch (_type) {
case HC_G1:
_out = _out.length() * _in;
break;
case HC_SMOOTH:
_out = _in;
break;
}
*/
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::set_name
// Access: Public
// Description: Sets the name associated with the CV.
////////////////////////////////////////////////////////////////////
void HermiteCurveCV::
set_name(const char *name) {
if (_name != NULL) {
delete [] _name;
_name = NULL;
}
if (name != NULL) {
_name = new char[strlen(name)+1];
strcpy(_name, name);
}
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurveCV::Output
// Access: Public
// Description: Formats the CV for output to an egg file.
////////////////////////////////////////////////////////////////////
void HermiteCurveCV::
Output(ostream &out, int indent, int num_dimensions,
bool show_in, bool show_out,
double scale_in, double scale_out) const {
if (show_in) {
Indent(out, indent) << "<Vertex> {\n";
show_vec3(out, indent+2, _p - scale_in * _in / 3.0,
num_dimensions) << "\n";
Indent(out, indent) << "}\n";
}
Indent(out, indent) << "<Vertex> {\n";
show_vec3(out, indent+2, _p, num_dimensions) << "\n";
Indent(out, indent+2) << "<Char*> 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) << "<Vertex> {\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+1<get_num_cvs(), 0);
HermiteCurveCV cv;
LVector3f tan;
cv._type = HC_SMOOTH;
get_pt(t, cv._p, tan);
cv._out = cv._in = tan / 2.0;
_points.insert(_points.begin() + n + 1, cv);
bool result =
insert_curveseg(n, new CubicCurveseg, t - get_cv_tstart(n));
nassertr(result, 0);
recompute_basis();
invalidate_all();
return n + 1;
}
////////////////////////////////////////////////////////////////////
// Function: HermiteCurve::append_cv
// Access: Public, Scheme
// Description: Adds a new CV to the end of the curve. The new CV is
// given initial in/out tangents of 0. The return value
// is the index of the new CV.
////////////////////////////////////////////////////////////////////
int HermiteCurve::
append_cv(int type, float x, float y, float z) {
HermiteCurveCV cv;
cv.set_type(type);
cv.set_point(LVector3f(x, y, z));
cv.set_in(zero);
cv.set_out(zero);
_points.push_back(cv);
if (_points.size()>1) {
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)
<< "<VertexPool> " << 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) << "<BezierCurve> " << get_name() << " {\n";
if (_curve_type!=PCT_NONE) {
Indent(out, indent+2) << "<Char*> type { ";
switch (_curve_type) {
case PCT_XYZ:
out << "XYZ";
break;
case PCT_HPR:
out << "HPR";
break;
case PCT_T:
out << "T";
break;
};
out << " }\n";
}
Indent(out, indent+2) << "<TLengths> {";
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) << "<VertexRef> {";
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) << "<Ref> { " << 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));
}
}
}

View File

@ -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<HermiteCurveCV> _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