Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/parametrics/classicNurbsCurve.cxx

Go to the documentation of this file.
00001 // Filename: classicNurbsCurve.cxx
00002 // Created by:  drose (27Feb98)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "classicNurbsCurve.h"
00020 #include "config_parametrics.h"
00021 
00022 #include <indent.h>
00023 #include <datagram.h>
00024 #include <datagramIterator.h>
00025 #include <bamWriter.h>
00026 #include <bamReader.h>
00027 
00028 ////////////////////////////////////////////////////////////////////
00029 // Statics
00030 ////////////////////////////////////////////////////////////////////
00031 
00032 TypeHandle ClassicNurbsCurve::_type_handle;
00033 TypeHandle ClassicNurbsCurve::_orig_type_handle;
00034 
00035 static const LVecBase3f zero = LVecBase3f(0.0f, 0.0f, 0.0f);
00036 // This is returned occasionally from some of the functions, and is
00037 // used from time to time as an initializer.
00038 
00039 
00040 ////////////////////////////////////////////////////////////////////
00041 //     Function: ClassicNurbsCurve::Constructor
00042 //       Access: Published
00043 //  Description:
00044 ////////////////////////////////////////////////////////////////////
00045 ClassicNurbsCurve::
00046 ClassicNurbsCurve() {
00047   _order = 4;
00048 }
00049 
00050 ////////////////////////////////////////////////////////////////////
00051 //     Function: ClassicNurbsCurve::Copy Constructor
00052 //       Access: Published
00053 //  Description: Constructs a NURBS curve equivalent to the indicated
00054 //               (possibly non-NURBS) curve.
00055 ////////////////////////////////////////////////////////////////////
00056 ClassicNurbsCurve::
00057 ClassicNurbsCurve(const ParametricCurve &pc) {
00058   _order = 4;
00059 
00060   if (!pc.convert_to_nurbs(this)) {
00061     parametrics_cat->warning()
00062       << "Cannot make a NURBS from the indicated curve.\n";
00063   }
00064 }
00065 
00066 ////////////////////////////////////////////////////////////////////
00067 //     Function: ClassicNurbsCurve::Constructor
00068 //       Access: Published
00069 //  Description: Constructs a NURBS curve according to the indicated
00070 //               NURBS parameters.
00071 ////////////////////////////////////////////////////////////////////
00072 ClassicNurbsCurve::
00073 ClassicNurbsCurve(int order, int num_cvs,
00074                   const float knots[], const LVecBase4f cvs[]) {
00075   _order = order;
00076 
00077   int i;
00078   _cvs.reserve(num_cvs);
00079   for (i = 0; i < num_cvs; i++) {
00080     append_cv(cvs[i]);
00081   }
00082 
00083   int num_knots = num_cvs + order;
00084   for (i = 0; i < num_knots; i++) {
00085     set_knot(i, knots[i]);
00086   }
00087 
00088   recompute();
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: ClassicNurbsCurve::Destructor
00093 //       Access: Published, Virtual
00094 //  Description:
00095 ////////////////////////////////////////////////////////////////////
00096 ClassicNurbsCurve::
00097 ~ClassicNurbsCurve() {
00098 }
00099 
00100 
00101 ////////////////////////////////////////////////////////////////////
00102 //     Function: ClassicNurbsCurve::set_order
00103 //       Access: Published, Virtual
00104 //  Description: Changes the order of the curve.  Must be a value from
00105 //               1 to 4.  Can only be done when there are no cv's.
00106 ////////////////////////////////////////////////////////////////////
00107 void ClassicNurbsCurve::
00108 set_order(int order) {
00109   nassertv(order >= 1 && order <= 4);
00110   nassertv(_cvs.empty());
00111 
00112   _order = order;
00113 }
00114 
00115 ////////////////////////////////////////////////////////////////////
00116 //     Function: ClassicNurbsCurve::get_order
00117 //       Access: Published, Virtual
00118 //  Description:
00119 ////////////////////////////////////////////////////////////////////
00120 int ClassicNurbsCurve::
00121 get_order() const {
00122   return _order;
00123 }
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: ClassicNurbsCurve::get_num_cvs
00127 //       Access: Published, Virtual
00128 //  Description:
00129 ////////////////////////////////////////////////////////////////////
00130 int ClassicNurbsCurve::
00131 get_num_cvs() const {
00132   return _cvs.size();
00133 }
00134 
00135 ////////////////////////////////////////////////////////////////////
00136 //     Function: ClassicNurbsCurve::get_num_knots
00137 //       Access: Published, Virtual
00138 //  Description: Returns the number of knots on the curve.
00139 ////////////////////////////////////////////////////////////////////
00140 int ClassicNurbsCurve::
00141 get_num_knots() const {
00142   return _cvs.size() + _order;
00143 }
00144 
00145 
00146 
00147 ////////////////////////////////////////////////////////////////////
00148 //     Function: ClassicNurbsCurve::insert_cv
00149 //       Access: Published, Virtual
00150 //  Description: Inserts a new CV into the middle of the curve at the
00151 //               indicated parametric value.  This doesn't change the
00152 //               shape or timing of the curve; however, it is
00153 //               irreversible: if the new CV is immediately removed,
00154 //               the curve will be changed.  Returns true if
00155 //               successful, false otherwise.
00156 ////////////////////////////////////////////////////////////////////
00157 bool ClassicNurbsCurve::
00158 insert_cv(float t) {
00159   if (_cvs.empty()) {
00160     append_cv(0.0f, 0.0f, 0.0f);
00161     return true;
00162   }
00163 
00164   if (t <= 0) {
00165     t = 0.0f;
00166   }
00167 
00168   int k = find_cv(t);
00169   if (k < 0) {
00170     append_cv(_cvs.back()._p);
00171     return true;
00172   }
00173 
00174   // Now we are inserting a knot between k-1 and k.  We'll adjust the
00175   // CV's according to Bohm's rule.
00176 
00177   // First, get the new values of all the CV's that will change.
00178   // These are the CV's in the range [k - (_order-1), k-1].
00179 
00180   LVecBase4f new_cvs[3];
00181   int i;
00182   for (i = 0; i < _order-1; i++) {
00183     int nk = i + k - (_order-1);
00184     float ti = get_knot(nk);
00185     float d = get_knot(nk + _order-1) - ti;
00186     if (d == 0.0f) {
00187       new_cvs[i] = _cvs[nk-1]._p;
00188     } else {
00189       float a = (t - ti) / d;
00190       new_cvs[i] = (1.0f-a)*_cvs[nk-1]._p + a*_cvs[nk]._p;
00191     }
00192   }
00193 
00194   // Now insert the new CV
00195   _cvs.insert(_cvs.begin() + k-1, CV());
00196 
00197   // Set all the new position values
00198   for (i = 0; i < _order-1; i++) {
00199     int nk = i + k - (_order-1);
00200     _cvs[nk]._p = new_cvs[i];
00201   }
00202 
00203   // And set the new knot value.
00204   _cvs[k-1]._t = t;
00205 
00206   return true;
00207 }
00208 
00209 ////////////////////////////////////////////////////////////////////
00210 //     Function: ClassicNurbsCurve::remove_cv
00211 //       Access: Published, Virtual
00212 //  Description: Removes the indicated CV from the curve.  Returns
00213 //               true if the CV index was valid, false otherwise.
00214 ////////////////////////////////////////////////////////////////////
00215 bool ClassicNurbsCurve::
00216 remove_cv(int n) {
00217   if (n < 0 || n >= (int)_cvs.size()) {
00218     return false;
00219   }
00220 
00221   _cvs.erase(_cvs.begin() + n);
00222   return true;
00223 }
00224 
00225 ////////////////////////////////////////////////////////////////////
00226 //     Function: ClassicNurbsCurve::remove_all_cvs
00227 //       Access: Published, Virtual
00228 //  Description: Removes all CV's from the curve.
00229 ////////////////////////////////////////////////////////////////////
00230 void ClassicNurbsCurve::
00231 remove_all_cvs() {
00232   _cvs.erase(_cvs.begin(), _cvs.end());
00233 }
00234 
00235 
00236 ////////////////////////////////////////////////////////////////////
00237 //     Function: ClassicNurbsCurve::set_cv
00238 //       Access: Published, Virtual
00239 //  Description: Repositions the indicated CV.  Returns true if
00240 //               successful, false otherwise.
00241 ////////////////////////////////////////////////////////////////////
00242 bool ClassicNurbsCurve::
00243 set_cv(int n, const LVecBase4f &v) {
00244   nassertr(n >= 0 && n < get_num_cvs(), false);
00245 
00246   _cvs[n]._p = v;
00247   return true;
00248 }
00249 
00250 ////////////////////////////////////////////////////////////////////
00251 //     Function: ClassicNurbsCurve::get_cv
00252 //       Access: Published, Virtual
00253 //  Description: Returns the position in homogeneous space of the
00254 //               indicated CV.
00255 ////////////////////////////////////////////////////////////////////
00256 LVecBase4f ClassicNurbsCurve::
00257 get_cv(int n) const {
00258   nassertr(n >= 0 && n < get_num_cvs(), LVecBase4f::zero());
00259 
00260   return _cvs[n]._p;
00261 }
00262 
00263 
00264 ////////////////////////////////////////////////////////////////////
00265 //     Function: ClassicNurbsCurve::set_knot
00266 //       Access: Published, Virtual
00267 //  Description: Sets the value of the indicated knot.  There are
00268 //               get_num_cvs() + _order knot values, but the first
00269 //               _order - 1 and the last 1 knot values cannot be
00270 //               changed.  It is also an error to set a knot value
00271 //               outside the range of its neighbors.
00272 ////////////////////////////////////////////////////////////////////
00273 bool ClassicNurbsCurve::
00274 set_knot(int n, float t) {
00275   nassertr(n >= 0 && n < get_num_knots(), false);
00276 
00277   if (n < _order || n-1 >= (int)_cvs.size()) {
00278     return false;
00279   }
00280   _cvs[n-1]._t = t;
00281   return true;
00282 }
00283 
00284 ////////////////////////////////////////////////////////////////////
00285 //     Function: ClassicNurbsCurve::get_knot
00286 //       Access: Published, Virtual
00287 //  Description: Retrieves the value of the indicated knot.
00288 ////////////////////////////////////////////////////////////////////
00289 float ClassicNurbsCurve::
00290 get_knot(int n) const {
00291   if (n < _order || _cvs.empty()) {
00292     return 0.0f;
00293   } else if (n-1 >= (int)_cvs.size()) {
00294     return _cvs.back()._t;
00295   } else {
00296     return _cvs[n-1]._t;
00297   }
00298 }
00299 
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: ClassicNurbsCurve::recompute
00303 //       Access: Published, Virtual
00304 //  Description: Recalculates the curve basis according to the latest
00305 //               position of the CV's, knots, etc.  Until this
00306 //               function is called, adjusting the NURBS parameters
00307 //               will have no visible effect on the curve.  Returns
00308 //               true if the resulting curve is valid, false
00309 //               otherwise.
00310 ////////////////////////////////////////////////////////////////////
00311 bool ClassicNurbsCurve::
00312 recompute() {
00313   _segs.erase(_segs.begin(), _segs.end());
00314 
00315   float knots[8];
00316   LVecBase4f cvs[4];
00317 
00318   if ((int)_cvs.size() > _order-1) {
00319     for (int cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
00320       if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
00321         // There are _order consecutive CV's that define each segment,
00322         // beginning at cv.  Collect the CV's and knot values that define
00323         // this segment.
00324         int c;
00325         for (c = 0; c < _order; c++) {
00326           cvs[c] = _cvs[c+cv]._p;
00327         }
00328         for (c = 0; c < _order+_order; c++) {
00329           knots[c] = get_knot(c+cv);
00330         }
00331 
00332         insert_curveseg(_segs.size(), new CubicCurveseg(_order, knots, cvs),
00333                         knots[_order] - knots[_order-1]);
00334       }
00335     }
00336   }
00337 
00338   return !_segs.empty();
00339 }
00340 
00341 ////////////////////////////////////////////////////////////////////
00342 //     Function: ClassicNurbsCurve::rebuild_curveseg
00343 //       Access: Public, Virtual
00344 //  Description: Rebuilds the current curve segment (as selected by
00345 //               the most recent call to find_curve()) according to
00346 //               the specified properties (see
00347 //               CubicCurveseg::compute_seg).  Returns true if
00348 //               possible, false if something goes horribly wrong.
00349 ////////////////////////////////////////////////////////////////////
00350 bool ClassicNurbsCurve::
00351 rebuild_curveseg(int rtype0, float t0, const LVecBase4f &v0,
00352                  int rtype1, float t1, const LVecBase4f &v1,
00353                  int rtype2, float t2, const LVecBase4f &v2,
00354                  int rtype3, float t3, const LVecBase4f &v3) {
00355   // Figure out which CV's contributed to this segment.
00356   int seg = 0;
00357 
00358   nassertr((int)_cvs.size() > _order-1, false);
00359 
00360   int cv = 0;
00361   for (cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
00362     if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
00363       if (seg == _last_ti) {
00364         break;
00365       }
00366       seg++;
00367     }
00368   }
00369 
00370   // Now copy the cvs and knots in question.
00371   LMatrix4f G;
00372   float knots[8];
00373 
00374   int c;
00375 
00376   // We only need to build the geometry matrix if at least one of the
00377   // properties depends on the original value.
00378   if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
00379     for (c = 0; c < 4; c++) {
00380       static const LVecBase4f zero_vec(0.0f, 0.0f, 0.0f, 0.0f);
00381       const LVecBase4f &s = (c < _order) ? _cvs[c+cv]._p : zero_vec;
00382 
00383       G.set_col(c, s);
00384     }
00385   }
00386 
00387   // But we always need the knot vector to determine the basis matrix.
00388   for (c = 0; c < _order+_order; c++) {
00389     knots[c] = get_knot(c+cv);
00390   }
00391 
00392   LMatrix4f B;
00393   compute_nurbs_basis(_order, knots, B);
00394 
00395   LMatrix4f Bi;
00396   Bi = invert(B);
00397 
00398   if (!CubicCurveseg::compute_seg(rtype0, t0, v0,
00399                                   rtype1, t1, v1,
00400                                   rtype2, t2, v2,
00401                                   rtype3, t3, v3,
00402                                   B, Bi, G)) {
00403     return false;
00404   }
00405 
00406   // Now extract the new CV's from the new G matrix, and restore them
00407   // to the curve.
00408   for (c = 0; c < _order; c++) {
00409     _cvs[c+cv]._p = G.get_col(c);
00410   }
00411 
00412   return true;
00413 }
00414 
00415 ////////////////////////////////////////////////////////////////////
00416 //     Function: ClassicNurbsCurve::stitch
00417 //       Access: Published, Virtual
00418 //  Description: Regenerates this curve as one long curve: the first
00419 //               curve connected end-to-end with the second one.
00420 //               Either a or b may be the same as 'this'.
00421 //
00422 //               Returns true if successful, false on failure or if
00423 //               the curve type does not support stitching.
00424 ////////////////////////////////////////////////////////////////////
00425 bool ClassicNurbsCurve::
00426 stitch(const ParametricCurve *a, const ParametricCurve *b) {
00427   // First, make a copy of both of our curves.  This ensures they are
00428   // of the correct type, and also protects us in case one of them is
00429   // the same as 'this'.
00430   PT(ClassicNurbsCurve) na = new ClassicNurbsCurve(*a);
00431   PT(ClassicNurbsCurve) nb = new ClassicNurbsCurve(*b);
00432 
00433   if (na->get_num_cvs() == 0 || nb->get_num_cvs() == 0) {
00434     return false;
00435   }
00436 
00437   if (na->get_order() != nb->get_order()) {
00438     parametrics_cat->error()
00439       << "Cannot stitch NURBS curves of different orders!\n";
00440     return false;
00441   }
00442 
00443   // First, translate curve B to move its first CV to curve A's last
00444   // CV.
00445   LVecBase3f point_offset =
00446     na->get_cv_point(na->get_num_cvs() - 1) - nb->get_cv_point(0);
00447   int num_b_cvs = nb->get_num_cvs();
00448   for (int i = 0; i < num_b_cvs; i++) {
00449     nb->set_cv_point(i, nb->get_cv_point(i) + point_offset);
00450   }
00451 
00452   // Now define a vector of all of A's CV's except the last one.
00453   _cvs = na->_cvs;
00454   if (!_cvs.empty()) {
00455     _cvs.pop_back();
00456   }
00457 
00458   float t = na->get_max_t();
00459 
00460   // Now add all the new CV's.
00461   pvector<CV>::iterator ci;
00462   for (ci = nb->_cvs.begin(); ci != nb->_cvs.end(); ++ci) {
00463     CV new_cv = (*ci);
00464     new_cv._t += t;
00465     _cvs.push_back(new_cv);
00466   }
00467 
00468   recompute();
00469   return true;
00470 }
00471 
00472 
00473 ////////////////////////////////////////////////////////////////////
00474 //     Function: ClassicNurbsCurve::get_nurbs_interface
00475 //       Access: Public, Virtual
00476 //  Description: Returns a pointer to the object as a
00477 //               NurbsCurveInterface object if it happens to be a
00478 //               NURBS-style curve; otherwise, returns NULL.
00479 ////////////////////////////////////////////////////////////////////
00480 NurbsCurveInterface *ClassicNurbsCurve::
00481 get_nurbs_interface() {
00482   return this;
00483 }
00484 
00485 ////////////////////////////////////////////////////////////////////
00486 //     Function: ClassicNurbsCurve::convert_to_nurbs
00487 //       Access: Public, Virtual
00488 //  Description: Stores in the indicated NurbsCurve a NURBS
00489 //               representation of an equivalent curve.  Returns true
00490 //               if successful, false otherwise.
00491 ////////////////////////////////////////////////////////////////////
00492 bool ClassicNurbsCurve::
00493 convert_to_nurbs(ParametricCurve *nc) const {
00494   nc->set_curve_type(_curve_type);
00495   return NurbsCurveInterface::convert_to_nurbs(nc);
00496 }
00497 
00498 ////////////////////////////////////////////////////////////////////
00499 //     Function: ClassicNurbsCurve::write
00500 //       Access: Public, Virtual
00501 //  Description:
00502 ////////////////////////////////////////////////////////////////////
00503 void ClassicNurbsCurve::
00504 write(ostream &out, int indent_level) const {
00505   NurbsCurveInterface::write(out, indent_level);
00506 }
00507 
00508 ////////////////////////////////////////////////////////////////////
00509 //     Function: ClassicNurbsCurve::append_cv_impl
00510 //       Access: Protected, Virtual
00511 //  Description: Adds a new CV to the end of the curve.  Creates a new
00512 //               knot value by adding 1 to the last knot value.
00513 //               Returns the index of the new CV.
00514 ////////////////////////////////////////////////////////////////////
00515 int ClassicNurbsCurve::
00516 append_cv_impl(const LVecBase4f &v) {
00517   _cvs.push_back(CV(v, get_knot(_cvs.size())+1.0f));
00518   return _cvs.size()-1;
00519 }
00520 
00521 ////////////////////////////////////////////////////////////////////
00522 //     Function: ClassicNurbsCurve::format_egg
00523 //       Access: Protected, Virtual
00524 //  Description: Formats the curve as an egg structure to write to the
00525 //               indicated stream.  Returns true on success, false on
00526 //               failure.
00527 ////////////////////////////////////////////////////////////////////
00528 bool ClassicNurbsCurve::
00529 format_egg(ostream &out, const string &name, const string &curve_type,
00530            int indent_level) const {
00531   return NurbsCurveInterface::format_egg(out, name, curve_type, indent_level);
00532 }
00533 
00534 ////////////////////////////////////////////////////////////////////
00535 //     Function: ClassicNurbsCurve::find_cv
00536 //       Access: Protected
00537 //  Description: Finds the first knot whose value is >= t, or -1 if t
00538 //               is beyond the end of the curve.
00539 ////////////////////////////////////////////////////////////////////
00540 int ClassicNurbsCurve::
00541 find_cv(float t) {
00542   int i;
00543   for (i = _order-1; i < (int)_cvs.size(); i++) {
00544     if (_cvs[i]._t >= t) {
00545       return i+1;
00546     }
00547   }
00548 
00549   return -1;
00550 }
00551 
00552 ////////////////////////////////////////////////////////////////////
00553 //     Function: ClassicNurbsCurve::register_with_factory
00554 //       Access: Public, Static
00555 //  Description: Initializes the factory for reading these things from
00556 //               Bam files.
00557 ////////////////////////////////////////////////////////////////////
00558 void ClassicNurbsCurve::
00559 register_with_read_factory() {
00560   BamReader::get_factory()->register_factory(get_class_type(), make_ClassicNurbsCurve);
00561   BamReader::get_factory()->register_factory(_orig_type_handle, make_ClassicNurbsCurve);
00562 }
00563 
00564 ////////////////////////////////////////////////////////////////////
00565 //     Function: ClassicNurbsCurve::make_ClassicNurbsCurve
00566 //       Access: Protected
00567 //  Description: Factory method to generate an object of this type.
00568 ////////////////////////////////////////////////////////////////////
00569 TypedWritable *ClassicNurbsCurve::
00570 make_ClassicNurbsCurve(const FactoryParams &params) {
00571   ClassicNurbsCurve *me = new ClassicNurbsCurve;
00572   DatagramIterator scan;
00573   BamReader *manager;
00574 
00575   parse_params(params, scan, manager);
00576   me->fillin(scan, manager);
00577   return me;
00578 }
00579 
00580 ////////////////////////////////////////////////////////////////////
00581 //     Function: ClassicNurbsCurve::write_datagram
00582 //       Access: Protected, Virtual
00583 //  Description: Function to write the important information in
00584 //               the particular object to a Datagram
00585 ////////////////////////////////////////////////////////////////////
00586 void ClassicNurbsCurve::
00587 write_datagram(BamWriter *manager, Datagram &me) {
00588   PiecewiseCurve::write_datagram(manager, me);
00589 
00590   me.add_int8(_order);
00591 
00592   me.add_uint32(_cvs.size());
00593   size_t i;
00594   for (i = 0; i < _cvs.size(); i++) {
00595     const CV &cv = _cvs[i];
00596     cv._p.write_datagram(me);
00597     me.add_float64(cv._t);
00598   }
00599 }
00600 
00601 ////////////////////////////////////////////////////////////////////
00602 //     Function: ClassicNurbsCurve::fillin
00603 //       Access: Protected
00604 //  Description: Function that reads out of the datagram (or asks
00605 //               manager to read) all of the data that is needed to
00606 //               re-create this object and stores it in the appropiate
00607 //               place
00608 ////////////////////////////////////////////////////////////////////
00609 void ClassicNurbsCurve::
00610 fillin(DatagramIterator &scan, BamReader *manager) {
00611   PiecewiseCurve::fillin(scan, manager);
00612 
00613   _order = scan.get_int8();
00614 
00615   size_t num_cvs = scan.get_uint32();
00616 
00617   _cvs.reserve(num_cvs);
00618   size_t i;
00619   for (i = 0; i < num_cvs; i++) {
00620     CV cv;
00621     cv._p.read_datagram(scan);
00622     cv._t = scan.get_float64();
00623     _cvs.push_back(cv);
00624   }
00625 }

Generated on Fri May 2 00:40:38 2003 for Panda by doxygen1.3