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 ¶ms) { 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 }