00001 // Filename: piecewiseCurve.cxx 00002 // Created by: drose (04Mar01) 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 "piecewiseCurve.h" 00020 #include "cubicCurveseg.h" 00021 #include "config_parametrics.h" 00022 00023 #include <datagram.h> 00024 #include <datagramIterator.h> 00025 #include <bamWriter.h> 00026 #include <bamReader.h> 00027 00028 TypeHandle PiecewiseCurve::_type_handle; 00029 00030 //////////////////////////////////////////////////////////////////// 00031 // Function: PiecewiseCurve::Constructor 00032 // Access: Public 00033 // Description: 00034 //////////////////////////////////////////////////////////////////// 00035 PiecewiseCurve:: 00036 PiecewiseCurve() { 00037 _last_ti = 0; 00038 } 00039 00040 //////////////////////////////////////////////////////////////////// 00041 // Function: PiecewiseCurve::Destructor 00042 // Access: Protected 00043 // Description: 00044 //////////////////////////////////////////////////////////////////// 00045 PiecewiseCurve:: 00046 ~PiecewiseCurve() { 00047 remove_all_curvesegs(); 00048 } 00049 00050 00051 //////////////////////////////////////////////////////////////////// 00052 // Function: PiecewiseCurve::is_valid 00053 // Access: Published, Virtual 00054 // Description: Returns true if the curve is defined. In the case of 00055 // a PiecewiseCurve, this means we have at least one 00056 // segment. 00057 //////////////////////////////////////////////////////////////////// 00058 bool PiecewiseCurve:: 00059 is_valid() const { 00060 return !_segs.empty(); 00061 } 00062 00063 //////////////////////////////////////////////////////////////////// 00064 // Function: PiecewiseCurve::get_max_t 00065 // Access: Published, Virtual 00066 // Description: Returns the upper bound of t for the entire curve. 00067 // The curve is defined in the range 0.0f <= t <= 00068 // get_max_t(). 00069 //////////////////////////////////////////////////////////////////// 00070 float PiecewiseCurve:: 00071 get_max_t() const { 00072 return _segs.empty() ? 0.0f : _segs.back()._tend; 00073 } 00074 00075 00076 //////////////////////////////////////////////////////////////////// 00077 // Function: PiecewiseCurve::get_point 00078 // Access: Published, Virtual 00079 // Description: Returns the point of the curve at a given parametric 00080 // point t. Returns true if t is in the valid range 0.0f 00081 // <= t <= get_max_t(); if t is outside this range, sets 00082 // point to the value of the curve at the beginning or 00083 // end (whichever is nearer) and returns false. 00084 //////////////////////////////////////////////////////////////////// 00085 bool PiecewiseCurve:: 00086 get_point(float t, LVecBase3f &point) const { 00087 const ParametricCurve *curve; 00088 bool result = find_curve(curve, t); 00089 00090 // We use | instead of || so we won't short-circuit this calculation. 00091 return result | curve->get_point(t, point); 00092 } 00093 00094 00095 //////////////////////////////////////////////////////////////////// 00096 // Function: PiecewiseCurve::get_tangent 00097 // Access: Published, Virtual 00098 // Description: Returns the tangent of the curve at a given parametric 00099 // point t. 00100 //////////////////////////////////////////////////////////////////// 00101 bool PiecewiseCurve:: 00102 get_tangent(float t, LVecBase3f &tangent) const { 00103 const ParametricCurve *curve; 00104 bool result = find_curve(curve, t); 00105 00106 // We use | instead of || so we won't short-circuit this calculation. 00107 return result | curve->get_tangent(t, tangent); 00108 } 00109 00110 00111 //////////////////////////////////////////////////////////////////// 00112 // Function: PiecewiseCurve::get_2ndtangent 00113 // Access: Published, Virtual 00114 // Description: Returns the tangent of the first derivative of the 00115 // curve at the point t. 00116 //////////////////////////////////////////////////////////////////// 00117 bool PiecewiseCurve:: 00118 get_2ndtangent(float t, LVecBase3f &tangent2) const { 00119 const ParametricCurve *curve; 00120 bool result = find_curve(curve, t); 00121 00122 // We use | instead of || so we won't short-circuit this calculation. 00123 return result | curve->get_2ndtangent(t, tangent2); 00124 } 00125 00126 //////////////////////////////////////////////////////////////////// 00127 // Function: PiecewiseCurve::adjust_point 00128 // Access: Published, Virtual 00129 // Description: Recomputes the curve such that it passes through the 00130 // point (px, py, pz) at time t, but keeps the same 00131 // tangent value at that point. 00132 //////////////////////////////////////////////////////////////////// 00133 bool PiecewiseCurve:: 00134 adjust_point(float t, 00135 float px, float py, float pz) { 00136 if (parametrics_cat.is_debug()) { 00137 parametrics_cat.debug() 00138 << "Adjusting point at " << t << " to " << px << " " << py << " " 00139 << pz << "\n"; 00140 } 00141 00142 const ParametricCurve *curve; 00143 bool result = find_curve(curve, t); 00144 00145 if (!result) { 00146 cerr << "No curve segment at t = " << t << "\n"; 00147 return false; 00148 } 00149 00150 rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4f(), 00151 RT_POINT, t, LVecBase4f(px, py, pz, 1.0f), 00152 RT_TANGENT | RT_KEEP_ORIG, t, LVecBase4f(), 00153 RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4f()); 00154 return true; 00155 } 00156 00157 //////////////////////////////////////////////////////////////////// 00158 // Function: PiecewiseCurve::adjust_tangent 00159 // Access: Published, Virtual 00160 // Description: Recomputes the curve such that it has the tangent 00161 // (tx, ty, tz) at time t, but keeps the same position 00162 // at the point. 00163 //////////////////////////////////////////////////////////////////// 00164 bool PiecewiseCurve:: 00165 adjust_tangent(float t, 00166 float tx, float ty, float tz) { 00167 const ParametricCurve *curve; 00168 bool result = find_curve(curve, t); 00169 00170 if (!result) { 00171 cerr << "No curve segment at t = " << t << "\n"; 00172 return false; 00173 } 00174 00175 rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4f(), 00176 RT_POINT | RT_KEEP_ORIG, t, LVecBase4f(), 00177 RT_TANGENT, t, LVecBase4f(tx, ty, tz, 0.0f), 00178 RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4f()); 00179 return true; 00180 } 00181 00182 //////////////////////////////////////////////////////////////////// 00183 // Function: PiecewiseCurve::adjust_pt 00184 // Access: Published, Virtual 00185 // Description: Recomputes the curve such that it passes through the 00186 // point (px, py, pz) with the tangent (tx, ty, tz). 00187 //////////////////////////////////////////////////////////////////// 00188 bool PiecewiseCurve:: 00189 adjust_pt(float t, 00190 float px, float py, float pz, 00191 float tx, float ty, float tz) { 00192 const ParametricCurve *curve; 00193 bool result = find_curve(curve, t); 00194 00195 if (!result) { 00196 cerr << "No curve segment at t = " << t << "\n"; 00197 return false; 00198 } 00199 00200 rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4f(), 00201 RT_POINT, t, LVecBase4f(px, py, pz, 1.0f), 00202 RT_TANGENT, t, LVecBase4f(tx, ty, tz, 0.0f), 00203 RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4f()); 00204 return true; 00205 } 00206 00207 00208 //////////////////////////////////////////////////////////////////// 00209 // Function: PiecewiseCurve::get_pt 00210 // Access: Published, Virtual 00211 // Description: Simultaneously returns the point and tangent of the 00212 // curve at a given parametric point t. 00213 //////////////////////////////////////////////////////////////////// 00214 bool PiecewiseCurve:: 00215 get_pt(float t, LVecBase3f &point, LVecBase3f &tangent) const { 00216 const ParametricCurve *curve; 00217 bool result = find_curve(curve, t); 00218 00219 // We use | instead of || so we won't short-circuit this calculation. 00220 return result | curve->get_pt(t, point, tangent); 00221 } 00222 00223 00224 //////////////////////////////////////////////////////////////////// 00225 // Function: PiecewiseCurve::get_num_segs 00226 // Access: Public 00227 // Description: Returns the number of curve segments that make up the 00228 // Piecewise curve. 00229 //////////////////////////////////////////////////////////////////// 00230 int PiecewiseCurve:: 00231 get_num_segs() const { 00232 return _segs.size(); 00233 } 00234 00235 //////////////////////////////////////////////////////////////////// 00236 // Function: PiecewiseCurve::get_curveseg 00237 // Access: Public 00238 // Description: Returns the curve segment corresponding to the given 00239 // index. 00240 //////////////////////////////////////////////////////////////////// 00241 ParametricCurve *PiecewiseCurve:: 00242 get_curveseg(int ti) { 00243 assert(ti >= 0 && ti < (int)_segs.size()); 00244 return _segs[ti]._curve; 00245 } 00246 00247 00248 //////////////////////////////////////////////////////////////////// 00249 // Function: PiecewiseCurve::insert_curveseg 00250 // Access: Public 00251 // Description: Inserts a new curve segment at the indicated index. 00252 // The curve segment must have been allocated via 00253 // new; it will be freed using delete when it is removed 00254 // or the PiecewiseCurve destructs. 00255 // 00256 // If the curve segment is not inserted at the end, its 00257 // tlength is subtracted from that of the following 00258 // segment, so that the overall length of the curve is 00259 // not changed. 00260 //////////////////////////////////////////////////////////////////// 00261 bool PiecewiseCurve:: 00262 insert_curveseg(int ti, ParametricCurve *seg, float tlength) { 00263 if (ti < 0 || ti > (int)_segs.size()) { 00264 return false; 00265 } 00266 00267 if (ti == (int)_segs.size()) { 00268 _segs.push_back(Curveseg(seg, get_max_t() + tlength)); 00269 00270 } else if (ti==0) { 00271 _segs.insert(_segs.begin(), 00272 Curveseg(seg, tlength)); 00273 00274 } else { 00275 _segs.insert(_segs.begin() + ti, 00276 Curveseg(seg, _segs[ti-1]._tend + tlength)); 00277 } 00278 00279 return true; 00280 } 00281 00282 00283 //////////////////////////////////////////////////////////////////// 00284 // Function: PiecewiseCurve::remove_curveseg 00285 // Access: Public 00286 // Description: Removes the given curve segment from the curve and 00287 // frees it. Returns true if the segment was defined, 00288 // false otherwise. 00289 //////////////////////////////////////////////////////////////////// 00290 bool PiecewiseCurve:: 00291 remove_curveseg(int ti) { 00292 if (ti < 0 || ti >= (int)_segs.size()) { 00293 return false; 00294 } 00295 00296 float tlength = get_tlength(ti); 00297 _segs.erase(_segs.begin() + ti); 00298 00299 // Now update the _tend figures for everything after the one we 00300 // removed. 00301 while (ti < (int)_segs.size()) { 00302 _segs[ti]._tend -= tlength; 00303 ti++; 00304 } 00305 00306 _last_ti = 0; 00307 return true; 00308 } 00309 00310 //////////////////////////////////////////////////////////////////// 00311 // Function: PiecewiseCurve::remove_all_curvesegs 00312 // Access: Public 00313 // Description: Removes all curve segments from the curve. 00314 //////////////////////////////////////////////////////////////////// 00315 void PiecewiseCurve:: 00316 remove_all_curvesegs() { 00317 _segs.erase(_segs.begin(), _segs.end()); 00318 _last_ti = 0; 00319 } 00320 00321 //////////////////////////////////////////////////////////////////// 00322 // Function: PiecewiseCurve::get_tlength 00323 // Access: Public 00324 // Description: Returns the parametric length of the given segment of 00325 // the curve. 00326 //////////////////////////////////////////////////////////////////// 00327 float PiecewiseCurve:: 00328 get_tlength(int ti) const { 00329 assert(ti >= 0 && ti < (int)_segs.size()); 00330 return (ti==0) ? _segs[ti]._tend : _segs[ti]._tend - _segs[ti-1]._tend; 00331 } 00332 00333 //////////////////////////////////////////////////////////////////// 00334 // Function: PiecewiseCurve::get_tstart 00335 // Access: Public 00336 // Description: Returns the parametric start of the given segment of 00337 // the curve. 00338 //////////////////////////////////////////////////////////////////// 00339 float PiecewiseCurve:: 00340 get_tstart(int ti) const { 00341 assert(ti >= 0 && ti <= (int)_segs.size()); 00342 return (ti==0) ? 0.0f : _segs[ti-1]._tend; 00343 } 00344 00345 //////////////////////////////////////////////////////////////////// 00346 // Function: PiecewiseCurve::get_tend 00347 // Access: Public 00348 // Description: Returns the parametric end of the given segment of 00349 // the curve. 00350 //////////////////////////////////////////////////////////////////// 00351 float PiecewiseCurve:: 00352 get_tend(int ti) const { 00353 assert(ti >= 0 && ti < (int)_segs.size()); 00354 return _segs[ti]._tend; 00355 } 00356 00357 00358 //////////////////////////////////////////////////////////////////// 00359 // Function: PiecewiseCurve::set_tlength 00360 // Access: Public 00361 // Description: Sets the parametric length of the given segment of 00362 // the curve. The length of the following segment is 00363 // lengthened by the corresponding amount to keep the 00364 // overall length of the curve the same. 00365 //////////////////////////////////////////////////////////////////// 00366 bool PiecewiseCurve:: 00367 set_tlength(int ti, float tlength) { 00368 if (ti < 0 || ti >= (int)_segs.size()) { 00369 return false; 00370 } 00371 00372 _segs[ti]._tend += tlength - get_tlength(ti); 00373 return true; 00374 } 00375 00376 00377 00378 //////////////////////////////////////////////////////////////////// 00379 // Function: PiecewiseCurve::make_nurbs 00380 // Access: Public 00381 // Description: Defines the curve as a general NURBS curve. The 00382 // order is the degree plus one and must be 1, 2, 3, or 00383 // 4; cvs is an array of num_cvs points each with a 00384 // homogeneous coordinate; knots is an array of 00385 // num_cvs+order knot values. 00386 // 00387 // This creates the individual curve segments and sets 00388 // up the basis matrices, but does not store the CV's or 00389 // knot values so the curve shape is not later 00390 // modifiable. 00391 //////////////////////////////////////////////////////////////////// 00392 void PiecewiseCurve:: 00393 make_nurbs(int order, int num_cvs, 00394 const float knots[], const LVecBase4f cvs[]) { 00395 remove_all_curvesegs(); 00396 00397 for (int i=0; i<num_cvs - order + 1; i++) { 00398 if (knots[i+order] > knots[i+order-1]) { 00399 int ti = get_num_segs(); 00400 bool result = 00401 insert_curveseg(ti, new CubicCurveseg(order, knots+i, cvs+i), 00402 knots[i+order] - knots[i+order-1]); 00403 assert(result); 00404 } 00405 } 00406 } 00407 00408 00409 //////////////////////////////////////////////////////////////////// 00410 // Function: PiecewiseCurve::get_bezier_segs 00411 // Access: Public, Virtual 00412 // Description: Fills up the indicated vector with a list of 00413 // BezierSeg structs that describe the curve. This 00414 // assumes the curve is a PiecewiseCurve of 00415 // CubicCurvesegs. Returns true if successful, false 00416 // otherwise. 00417 //////////////////////////////////////////////////////////////////// 00418 bool PiecewiseCurve:: 00419 get_bezier_segs(BezierSegs &bz_segs) const { 00420 bz_segs.erase(bz_segs.begin(), bz_segs.end()); 00421 int i; 00422 BezierSeg seg; 00423 for (i = 0; i < (int)_segs.size(); i++) { 00424 if (!_segs[i]._curve->get_bezier_seg(seg)) { 00425 return false; 00426 } 00427 seg._t = _segs[i]._tend; 00428 bz_segs.push_back(seg); 00429 } 00430 00431 return true; 00432 } 00433 00434 //////////////////////////////////////////////////////////////////// 00435 // Function: PiecewiseCurve::rebuild_curveseg 00436 // Access: Public, Virtual 00437 // Description: Rebuilds the current curve segment (as selected by 00438 // the most recent call to find_curve()) according to 00439 // the specified properties (see 00440 // CubicCurveseg::compute_seg). Returns true if 00441 // possible, false if something goes horribly wrong. 00442 //////////////////////////////////////////////////////////////////// 00443 bool PiecewiseCurve:: 00444 rebuild_curveseg(int, float, const LVecBase4f &, 00445 int, float, const LVecBase4f &, 00446 int, float, const LVecBase4f &, 00447 int, float, const LVecBase4f &) { 00448 cerr << "rebuild_curveseg not implemented for this curve type.\n"; 00449 return false; 00450 } 00451 00452 //////////////////////////////////////////////////////////////////// 00453 // Function: PiecewiseCurve::find_curve 00454 // Access: Protected 00455 // Description: Finds the curve corresponding to the given value of 00456 // t. If t is inside the curve's defined range, sets 00457 // curve to the appropriate segment, translates t to 00458 // [0,1] to index into the segment's coordinate system, 00459 // and returns true. If t is outside the curve's 00460 // defined range, sets curve to the nearest segment and 00461 // t to the nearest point on this segment, and returns 00462 // false. 00463 //////////////////////////////////////////////////////////////////// 00464 bool PiecewiseCurve:: 00465 find_curve(const ParametricCurve *&curve, float &t) const { 00466 // Check the index computed by the last call to find_curve(). If 00467 // it's still a reasonable starting value, start searching from 00468 // there. This way, we take advantage of locality of reference: the 00469 // search is trivial it is the same segment as last time, or the 00470 // next segment after the last one. 00471 if (_last_ti>0 && _segs[_last_ti-1]._tend>=t) { 00472 // However, if the new t value precedes that of last time, we'll 00473 // have to start over. 00474 00475 // We do some messy casting so we can get away with assigning a 00476 // value to a member within a const function. This assignment 00477 // doesn't really count as a const violation since we're just 00478 // updating a cached value, not changing any real data of the 00479 // class. 00480 ((PiecewiseCurve *)this)->_last_ti = 0; 00481 } 00482 00483 int ti; 00484 for (ti = _last_ti; ti < (int)_segs.size(); ti++) { 00485 if (_segs[ti]._tend+0.00001f > t) { 00486 break; 00487 } 00488 } 00489 00490 if (ti < (int)_segs.size()) { 00491 // Adjust t to the range [0,1). 00492 if (ti > 0) { 00493 t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend); 00494 } else { 00495 t /= _segs[0]._tend; 00496 } 00497 } 00498 00499 if (t < 0) { 00500 // Oops. 00501 curve = _segs[0]._curve; 00502 t = 0.0f; 00503 return false; 00504 } 00505 00506 if (ti >= (int)_segs.size() || !_segs[ti]._curve->is_valid()) { 00507 assert(ti <= (int)_segs.size()); 00508 00509 // If we're out of bounds, or the curve is undefined, we're probably 00510 // screwed. There's one exception: if we were right on a border between 00511 // curves, try the curve before. 00512 00513 if (ti > 0 && t < _segs[ti-1]._tend+0.0001f) { 00514 ti--; 00515 t = 1.0f; 00516 } 00517 00518 if (ti >= (int)_segs.size()) { 00519 if (_segs.empty()) { 00520 curve = NULL; 00521 t = 0.0f; 00522 return false; 00523 } else { 00524 curve = _segs.back()._curve; 00525 t = 1.0f; 00526 return false; 00527 } 00528 } else if (!_segs[ti]._curve->is_valid()) { 00529 curve = _segs[ti]._curve; 00530 return false; 00531 } 00532 } 00533 00534 // Again, some messy casting so we can get away with updating the 00535 // cached index value for next time. 00536 ((PiecewiseCurve *)this)->_last_ti = ti; 00537 00538 // Now scale t back into the curve's own valid range. 00539 t *= _segs[ti]._curve->get_max_t(); 00540 curve = _segs[ti]._curve; 00541 return true; 00542 } 00543 00544 00545 //////////////////////////////////////////////////////////////////// 00546 // Function: PiecewiseCurve::current_seg_range 00547 // Access: Protected 00548 // Description: Returns a number in the range [0,1], representing the 00549 // conversion of t into the current segment's coordinate 00550 // system (the segment last returned by find_curve). 00551 // This operation is already performed automatically on 00552 // the t passed into find_seg; this function is useful 00553 // only to adjust a different value into the same range. 00554 // 00555 // It is an error to call this function if find_curve() 00556 // has not yet been called, or if find_curve() returned 00557 // false from its previous call. 00558 //////////////////////////////////////////////////////////////////// 00559 float PiecewiseCurve:: 00560 current_seg_range(float t) const { 00561 int ti = _last_ti; 00562 00563 assert(ti < (int)_segs.size()); 00564 00565 // Adjust t to the range [0,1). 00566 if (ti > 0) { 00567 t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend); 00568 } else { 00569 t /= _segs[0]._tend; 00570 } 00571 00572 return t; 00573 } 00574 00575 //////////////////////////////////////////////////////////////////// 00576 // Function: PiecewiseCurve::write_datagram 00577 // Access: Protected, Virtual 00578 // Description: Function to write the important information in 00579 // the particular object to a Datagram 00580 //////////////////////////////////////////////////////////////////// 00581 void PiecewiseCurve:: 00582 write_datagram(BamWriter *manager, Datagram &me) { 00583 ParametricCurve::write_datagram(manager, me); 00584 00585 me.add_uint32(_segs.size()); 00586 size_t i; 00587 for (i = 0; i < _segs.size(); i++) { 00588 const Curveseg &seg = _segs[i]; 00589 manager->write_pointer(me, seg._curve); 00590 me.add_float64(seg._tend); 00591 } 00592 00593 _last_ti = 0; 00594 } 00595 00596 //////////////////////////////////////////////////////////////////// 00597 // Function: PiecewiseCurve::fillin 00598 // Access: Protected 00599 // Description: Function that reads out of the datagram (or asks 00600 // manager to read) all of the data that is needed to 00601 // re-create this object and stores it in the appropiate 00602 // place 00603 //////////////////////////////////////////////////////////////////// 00604 void PiecewiseCurve:: 00605 fillin(DatagramIterator &scan, BamReader *manager) { 00606 ParametricCurve::fillin(scan, manager); 00607 00608 size_t num_segs = scan.get_uint32(); 00609 _segs.reserve(num_segs); 00610 size_t i; 00611 for (i = 0; i < num_segs; i++) { 00612 Curveseg seg; 00613 manager->read_pointer(scan); 00614 seg._curve = (ParametricCurve *)NULL; 00615 seg._tend = scan.get_float64(); 00616 _segs.push_back(seg); 00617 } 00618 } 00619 00620 //////////////////////////////////////////////////////////////////// 00621 // Function: PiecewiseCurve::complete_pointers 00622 // Access: Protected, Virtual 00623 // Description: Takes in a vector of pointes to TypedWritable 00624 // objects that correspond to all the requests for 00625 // pointers that this object made to BamReader. 00626 //////////////////////////////////////////////////////////////////// 00627 int PiecewiseCurve:: 00628 complete_pointers(TypedWritable **p_list, BamReader *manager) { 00629 int used = ParametricCurve::complete_pointers(p_list, manager); 00630 00631 size_t i; 00632 for (i = 0; i < _segs.size(); i++) { 00633 _segs[i]._curve = DCAST(ParametricCurve, p_list[used + i]); 00634 } 00635 00636 return used + _segs.size(); 00637 }