00001 // Filename: parametricCurve.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 "parametricCurve.h" 00020 #include "config_parametrics.h" 00021 #include "hermiteCurve.h" 00022 #include "classicNurbsCurve.h" 00023 #include "parametricCurveDrawer.h" 00024 00025 #include "datagram.h" 00026 #include "datagramIterator.h" 00027 #include "bamWriter.h" 00028 #include "bamReader.h" 00029 #include "omniBoundingVolume.h" 00030 00031 static const float tolerance_divisor = 100000.0f; 00032 00033 TypeHandle ParametricCurve::_type_handle; 00034 00035 00036 //////////////////////////////////////////////////////////////////// 00037 // Function: ParametricCurve::Constructor 00038 // Access: Public 00039 // Description: This is a virtual base class. Don't try to construct 00040 // one from Scheme. 00041 //////////////////////////////////////////////////////////////////// 00042 ParametricCurve:: 00043 ParametricCurve() : PandaNode("curve") { 00044 _curve_type = PCT_NONE; 00045 _num_dimensions = 3; 00046 } 00047 00048 //////////////////////////////////////////////////////////////////// 00049 // Function: ParametricCurve::Destructor 00050 // Access: Protected 00051 // Description: 00052 //////////////////////////////////////////////////////////////////// 00053 ParametricCurve:: 00054 ~ParametricCurve() { 00055 // Our drawer list must be empty by the time we destruct, since our 00056 // drawers all maintain reference-counting pointers to us! If this 00057 // is not so, we have lost a reference count somewhere, or we have 00058 // gotten confused about which drawers we're registered to. 00059 nassertv(_drawers.empty()); 00060 } 00061 00062 //////////////////////////////////////////////////////////////////// 00063 // Function: ParametricCurve::safe_to_flatten 00064 // Access: Public, Virtual 00065 // Description: Returns true if it is generally safe to flatten out 00066 // this particular kind of PandaNode by duplicating 00067 // instances, false otherwise (for instance, a Camera 00068 // cannot be safely flattened, because the Camera 00069 // pointer itself is meaningful). 00070 //////////////////////////////////////////////////////////////////// 00071 bool ParametricCurve:: 00072 safe_to_flatten() const { 00073 return false; 00074 } 00075 00076 //////////////////////////////////////////////////////////////////// 00077 // Function: ParametricCurve::safe_to_transform 00078 // Access: Public, Virtual 00079 // Description: Returns true if it is generally safe to transform 00080 // this particular kind of PandaNode by calling the 00081 // xform() method, false otherwise. For instance, it's 00082 // usually a bad idea to attempt to xform a Character. 00083 //////////////////////////////////////////////////////////////////// 00084 bool ParametricCurve:: 00085 safe_to_transform() const { 00086 return false; 00087 } 00088 00089 //////////////////////////////////////////////////////////////////// 00090 // Function: ParametricCurve::is_valid 00091 // Access: Published, Virtual 00092 // Description: Returns true if the curve is defined. This base 00093 // class function always returns true; derived classes 00094 // might override this to sometimes return false. 00095 //////////////////////////////////////////////////////////////////// 00096 bool ParametricCurve:: 00097 is_valid() const { 00098 return true; 00099 } 00100 00101 00102 //////////////////////////////////////////////////////////////////// 00103 // Function: ParametricCurve::get_max_t 00104 // Access: Published, Virtual 00105 // Description: Returns the upper bound of t for the entire curve. 00106 // The curve is defined in the range 0.0f <= t <= 00107 // get_max_t(). This base class function always returns 00108 // 1.0f; derived classes might override this to return 00109 // something else. 00110 //////////////////////////////////////////////////////////////////// 00111 float ParametricCurve:: 00112 get_max_t() const { 00113 return 1.0f; 00114 } 00115 00116 00117 //////////////////////////////////////////////////////////////////// 00118 // Function: ParametricCurve::set_curve_type 00119 // Access: Published 00120 // Description: Sets the flag indicating the use to which the curve 00121 // is intended to be put. This flag is optional and 00122 // only serves to provide a hint to the egg reader and 00123 // writer code; it has no effect on the curve's 00124 // behavior. 00125 // 00126 // Setting the curve type also sets the num_dimensions 00127 // to 3 or 1 according to the type. 00128 // 00129 // THis flag may have one of the values PCT_XYZ, 00130 // PCT_HPR, or PCT_T. 00131 //////////////////////////////////////////////////////////////////// 00132 void ParametricCurve:: 00133 set_curve_type(int type) { 00134 _curve_type = type; 00135 switch (_curve_type) { 00136 case PCT_XYZ: 00137 case PCT_HPR: 00138 case PCT_NONE: 00139 _num_dimensions = 3; 00140 break; 00141 00142 case PCT_T: 00143 _num_dimensions = 1; 00144 break; 00145 00146 default: 00147 assert(0); 00148 } 00149 } 00150 00151 //////////////////////////////////////////////////////////////////// 00152 // Function: ParametricCurve::get_curve_type 00153 // Access: Published 00154 // Description: Returns the flag indicating the use to which the curve 00155 // is intended to be put. 00156 //////////////////////////////////////////////////////////////////// 00157 int ParametricCurve:: 00158 get_curve_type() const { 00159 return _curve_type; 00160 } 00161 00162 //////////////////////////////////////////////////////////////////// 00163 // Function: ParametricCurve::set_num_dimensions 00164 // Access: Published 00165 // Description: Specifies the number of significant dimensions in the 00166 // curve's vertices. This should be one of 1, 2, or 3. 00167 // Normally, XYZ and HPR curves have three dimensions; 00168 // time curves should always have one dimension. This 00169 // only serves as a hint to the mopath editor, and also 00170 // controls how the curve is written out. 00171 //////////////////////////////////////////////////////////////////// 00172 void ParametricCurve:: 00173 set_num_dimensions(int num) { 00174 _num_dimensions = num; 00175 } 00176 00177 //////////////////////////////////////////////////////////////////// 00178 // Function: ParametricCurve::get_num_dimensions 00179 // Access: Published 00180 // Description: Returns the number of significant dimensions in the 00181 // curve's vertices, as set by a previous call to 00182 // set_num_dimensions(). This is only a hint as to how 00183 // the curve is intended to be used; the actual number 00184 // of dimensions of any curve is always three. 00185 //////////////////////////////////////////////////////////////////// 00186 int ParametricCurve:: 00187 get_num_dimensions() const { 00188 return _num_dimensions; 00189 } 00190 00191 00192 //////////////////////////////////////////////////////////////////// 00193 // Function: ParametricCurve::calc_length 00194 // Access: Published 00195 // Description: Approximates the length of the entire curve to within 00196 // a few decimal places. 00197 //////////////////////////////////////////////////////////////////// 00198 float ParametricCurve:: 00199 calc_length() const { 00200 return calc_length(0.0f, get_max_t()); 00201 } 00202 00203 //////////////////////////////////////////////////////////////////// 00204 // Function: ParametricCurve::calc_length 00205 // Access: Published 00206 // Description: Approximates the length of the curve segment from 00207 // parametric time 'from' to time 'to'. 00208 //////////////////////////////////////////////////////////////////// 00209 float ParametricCurve:: 00210 calc_length(float from, float to) const { 00211 float t1, t2; 00212 LPoint3f p1, p2; 00213 00214 // Normally we expect from < to. If they came in backwards, reverse 00215 // them. 00216 float to_minus_from = to - from; 00217 00218 if (to_minus_from < 0.0f) { 00219 float temp = to; 00220 to = from; 00221 from = temp; 00222 to_minus_from=-to_minus_from; 00223 } 00224 00225 // Start with a segment for each unit of t. 00226 int num_segs = (int)(to_minus_from) + 1; 00227 t2 = from; 00228 get_point(t2, p2); 00229 float net = 0.0f; 00230 00231 for (int i = 1; i <= num_segs; i++) { 00232 t1 = t2; 00233 p1 = p2; 00234 00235 t2 = (to - from) * (float)i / (float)num_segs + from; 00236 get_point(t2, p2); 00237 00238 net += r_calc_length(t1, t2, p1, p2, (p1 - p2).length()); 00239 } 00240 return net; 00241 } 00242 00243 //////////////////////////////////////////////////////////////////// 00244 // Function: ParametricCurve::find_length 00245 // Access: Published 00246 // Description: Returns the parametric value corresponding to the 00247 // indicated distance along the curve from the starting 00248 // parametric value. 00249 // 00250 // This is the inverse of calc_length(): rather than 00251 // determining the length along the curve between two 00252 // parametric points, it determines the position in 00253 // parametric time of a point n units along the curve. 00254 // 00255 // The search distance must not be negative. 00256 //////////////////////////////////////////////////////////////////// 00257 float ParametricCurve:: 00258 find_length(float start_t, float length_offset) const { 00259 nassertr(length_offset >= 0.0f, start_t); 00260 nassertr(start_t >= 0.0f && start_t <= get_max_t(), start_t); 00261 00262 float t1, t2; 00263 LPoint3f p1, p2; 00264 00265 // Start with a segment for each unit of t. 00266 float max_t = get_max_t(); 00267 int num_segs = (int)cfloor(max_t - start_t + 1); 00268 t2 = start_t; 00269 get_point(t2, p2); 00270 float net = 0.0f; 00271 00272 for (int i = 1; i <= num_segs; i++) { 00273 assert(net <= length_offset); 00274 00275 t1 = t2; 00276 p1 = p2; 00277 00278 t2 = start_t + (((max_t - start_t) * (float)i) / (float)num_segs); 00279 get_point(t2, p2); 00280 00281 float seglength = (p1 - p2).length(); 00282 float result; 00283 00284 if (r_find_length(length_offset - net, result, 00285 t1, t2, p1, p2, seglength)) { 00286 // Found it! 00287 return result; 00288 } 00289 00290 net += seglength; 00291 } 00292 00293 // Not on the curve? Huh. 00294 return max_t; 00295 } 00296 00297 //////////////////////////////////////////////////////////////////// 00298 // Function: ParametricCurve::adjust_point 00299 // Access: Published, Virtual 00300 // Description: Recomputes the curve such that it passes through the 00301 // point (px, py, pz) at time t, but keeps the same 00302 // tangent value at that point. 00303 //////////////////////////////////////////////////////////////////// 00304 bool ParametricCurve:: 00305 adjust_point(float, float, float, float) { 00306 return false; 00307 } 00308 00309 //////////////////////////////////////////////////////////////////// 00310 // Function: ParametricCurve::adjust_tangent 00311 // Access: Published, Virtual 00312 // Description: Recomputes the curve such that it has the tangent 00313 // (tx, ty, tz) at time t, but keeps the same position 00314 // at the point. 00315 //////////////////////////////////////////////////////////////////// 00316 bool ParametricCurve:: 00317 adjust_tangent(float, float, float, float) { 00318 return false; 00319 } 00320 00321 //////////////////////////////////////////////////////////////////// 00322 // Function: ParametricCurve::adjust_pt 00323 // Access: Published, Virtual 00324 // Description: Recomputes the curve such that it passes through the 00325 // point (px, py, pz) with the tangent (tx, ty, tz). 00326 //////////////////////////////////////////////////////////////////// 00327 bool ParametricCurve:: 00328 adjust_pt(float, float, float, float, float, float, float) { 00329 return false; 00330 } 00331 00332 //////////////////////////////////////////////////////////////////// 00333 // Function: ParametricCurve::recompute 00334 // Access: Published, Virtual 00335 // Description: Recalculates the curve, if necessary. Returns 00336 // true if the resulting curve is valid, false 00337 // otherwise. 00338 //////////////////////////////////////////////////////////////////// 00339 bool ParametricCurve:: 00340 recompute() { 00341 return is_valid(); 00342 } 00343 00344 //////////////////////////////////////////////////////////////////// 00345 // Function: ParametricCurve::stitch 00346 // Access: Published, Virtual 00347 // Description: Regenerates this curve as one long curve: the first 00348 // curve connected end-to-end with the second one. 00349 // Either a or b may be the same as 'this'. 00350 // 00351 // Returns true if successful, false on failure or if 00352 // the curve type does not support stitching. 00353 //////////////////////////////////////////////////////////////////// 00354 bool ParametricCurve:: 00355 stitch(const ParametricCurve *, const ParametricCurve *) { 00356 parametrics_cat.error() 00357 << get_type() << " does not support stitching.\n"; 00358 return false; 00359 } 00360 00361 00362 //////////////////////////////////////////////////////////////////// 00363 // Function: ParametricCurve::write_egg 00364 // Access: Published 00365 // Description: Writes an egg description of the nurbs curve to the 00366 // specified output file. Returns true if the file is 00367 // successfully written. 00368 //////////////////////////////////////////////////////////////////// 00369 bool ParametricCurve:: 00370 write_egg(Filename filename, CoordinateSystem cs) { 00371 ofstream out; 00372 filename.set_text(); 00373 00374 if (!filename.open_write(out)) { 00375 parametrics_cat.error() 00376 << "Unable to write to " << filename << "\n"; 00377 return false; 00378 } 00379 return write_egg(out, filename, cs); 00380 } 00381 00382 //////////////////////////////////////////////////////////////////// 00383 // Function: ParametricCurve::write_egg 00384 // Access: Published 00385 // Description: Writes an egg description of the nurbs curve to the 00386 // specified output stream. Returns true if the file is 00387 // successfully written. 00388 //////////////////////////////////////////////////////////////////// 00389 bool ParametricCurve:: 00390 write_egg(ostream &out, const Filename &filename, CoordinateSystem cs) { 00391 string curve_type; 00392 switch (get_curve_type()) { 00393 case PCT_XYZ: 00394 curve_type = "xyz"; 00395 break; 00396 00397 case PCT_HPR: 00398 curve_type = "hpr"; 00399 break; 00400 00401 case PCT_T: 00402 curve_type = "t"; 00403 break; 00404 } 00405 00406 if (!has_name()) { 00407 // If we don't have a name, come up with one. 00408 string name = filename.get_basename_wo_extension(); 00409 00410 if (!curve_type.empty()) { 00411 name += "_"; 00412 name += curve_type; 00413 } 00414 00415 set_name(name); 00416 } 00417 00418 if (cs == CS_default) { 00419 cs = default_coordinate_system; 00420 } 00421 00422 if (cs != CS_invalid) { 00423 out << "<CoordinateSystem> { "; 00424 switch (cs) { 00425 case CS_zup_right: 00426 out << "Z-Up"; 00427 break; 00428 00429 case CS_yup_right: 00430 out << "Y-Up"; 00431 break; 00432 00433 case CS_zup_left: 00434 out << "Z-Up-Left"; 00435 break; 00436 00437 case CS_yup_left: 00438 out << "Y-Up-Left"; 00439 break; 00440 00441 default: 00442 break; 00443 } 00444 out << " }\n\n"; 00445 } 00446 00447 00448 if (!format_egg(out, get_name(), curve_type, 0)) { 00449 return false; 00450 } 00451 00452 if (out) { 00453 return true; 00454 } else { 00455 return false; 00456 } 00457 } 00458 00459 00460 00461 //////////////////////////////////////////////////////////////////// 00462 // Function: ParametricCurve::get_bezier_segs 00463 // Access: Public, Virtual 00464 // Description: Fills up the indicated vector with a list of 00465 // BezierSeg structs that describe the curve. This 00466 // assumes the curve is a PiecewiseCurve of 00467 // CubicCurvesegs. Returns true if successful, false 00468 // otherwise. 00469 //////////////////////////////////////////////////////////////////// 00470 bool ParametricCurve:: 00471 get_bezier_segs(ParametricCurve::BezierSegs &) const { 00472 return false; 00473 } 00474 00475 //////////////////////////////////////////////////////////////////// 00476 // Function: ParametricCurve::get_bezier_seg 00477 // Access: Public, Virtual 00478 // Description: Fills the BezierSeg structure with a description of 00479 // the curve segment as a Bezier, if possible, but does 00480 // not change the _t member of the structure. Returns 00481 // true if successful, false otherwise. 00482 //////////////////////////////////////////////////////////////////// 00483 bool ParametricCurve:: 00484 get_bezier_seg(ParametricCurve::BezierSeg &) const { 00485 return false; 00486 } 00487 00488 //////////////////////////////////////////////////////////////////// 00489 // Function: ParametricCurve::get_nurbs_interface 00490 // Access: Public, Virtual 00491 // Description: Returns a pointer to the object as a 00492 // NurbsCurveInterface object if it happens to be a 00493 // NURBS-style curve; otherwise, returns NULL. 00494 //////////////////////////////////////////////////////////////////// 00495 NurbsCurveInterface *ParametricCurve:: 00496 get_nurbs_interface() { 00497 return (NurbsCurveInterface *)NULL; 00498 } 00499 00500 //////////////////////////////////////////////////////////////////// 00501 // Function: ParametricCurve::convert_to_hermite 00502 // Access: Public, Virtual 00503 // Description: Stores an equivalent curve representation in the 00504 // indicated Hermite curve, if possible. Returns true 00505 // if successful, false otherwise. 00506 //////////////////////////////////////////////////////////////////// 00507 bool ParametricCurve:: 00508 convert_to_hermite(HermiteCurve *hc) const { 00509 BezierSegs bz_segs; 00510 if (!get_bezier_segs(bz_segs)) { 00511 return false; 00512 } 00513 00514 hc->set_curve_type(_curve_type); 00515 00516 // Now convert the Bezier segments to a Hermite. Normally, the 00517 // Beziers will match up head-to-tail, but if they don't, that's a 00518 // cut. 00519 hc->remove_all_cvs(); 00520 00521 int i, n; 00522 if (!bz_segs.empty()) { 00523 float scale_in = 0.0f; 00524 float scale_out = bz_segs[0]._t; 00525 n = hc->append_cv(HC_SMOOTH, bz_segs[0]._v[0]); 00526 hc->set_cv_out(n, 3.0f * (bz_segs[0]._v[1] - bz_segs[0]._v[0]) / scale_out); 00527 00528 for (i = 0; i < (int)bz_segs.size()-1; i++) { 00529 scale_in = scale_out; 00530 scale_out = bz_segs[i+1]._t - bz_segs[i]._t; 00531 00532 if (!bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001f)) { 00533 // Oops, we have a cut. 00534 hc->set_cv_type(n, HC_CUT); 00535 } 00536 00537 n = hc->append_cv(HC_FREE, bz_segs[i+1]._v[0]); 00538 hc->set_cv_in(n, 3.0f * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in); 00539 hc->set_cv_tstart(n, bz_segs[i]._t); 00540 00541 hc->set_cv_out(n, 3.0f * (bz_segs[i+1]._v[1] - bz_segs[i+1]._v[0]) / scale_out); 00542 } 00543 00544 // Now the last CV. 00545 scale_in = scale_out; 00546 i = bz_segs.size()-1; 00547 n = hc->append_cv(HC_SMOOTH, bz_segs[i]._v[3]); 00548 hc->set_cv_in(n, 3.0f * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in); 00549 hc->set_cv_tstart(n, bz_segs[i]._t); 00550 } 00551 00552 // Finally, go through and figure out which CV's are smooth or G1. 00553 int num_cvs = hc->get_num_cvs(); 00554 for (n = 1; n < num_cvs-1; n++) { 00555 if (hc->get_cv_type(n)!=HC_CUT) { 00556 LVector3f in = hc->get_cv_in(n); 00557 LVector3f out = hc->get_cv_out(n); 00558 00559 if (in.almost_equal(out, 0.0001f)) { 00560 hc->set_cv_type(n, HC_SMOOTH); 00561 } else { 00562 in.normalize(); 00563 out.normalize(); 00564 if (in.almost_equal(out, 0.0001f)) { 00565 hc->set_cv_type(n, HC_G1); 00566 } 00567 } 00568 } 00569 } 00570 return true; 00571 } 00572 00573 //////////////////////////////////////////////////////////////////// 00574 // Function: ParametricCurve::convert_to_nurbs 00575 // Access: Public, Virtual 00576 // Description: Stores in the indicated NurbsCurve a NURBS 00577 // representation of an equivalent curve. Returns true 00578 // if successful, false otherwise. 00579 //////////////////////////////////////////////////////////////////// 00580 bool ParametricCurve:: 00581 convert_to_nurbs(ParametricCurve *nc) const { 00582 NurbsCurveInterface *nurbs = nc->get_nurbs_interface(); 00583 nassertr(nurbs != (NurbsCurveInterface *)NULL, false); 00584 00585 BezierSegs bz_segs; 00586 if (!get_bezier_segs(bz_segs)) { 00587 return false; 00588 } 00589 00590 nc->set_curve_type(_curve_type); 00591 00592 nurbs->remove_all_cvs(); 00593 nurbs->set_order(4); 00594 if (!bz_segs.empty()) { 00595 int i; 00596 for (i = 0; i < (int)bz_segs.size(); i++) { 00597 nurbs->append_cv(bz_segs[i]._v[0]); 00598 nurbs->append_cv(bz_segs[i]._v[1]); 00599 nurbs->append_cv(bz_segs[i]._v[2]); 00600 if (i == (int)bz_segs.size()-1 || 00601 !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001f)) { 00602 nurbs->append_cv(bz_segs[i]._v[3]); 00603 } 00604 } 00605 00606 float t; 00607 int ki = 4; 00608 nurbs->set_knot(0, 0.0f); 00609 nurbs->set_knot(1, 0.0f); 00610 nurbs->set_knot(2, 0.0f); 00611 nurbs->set_knot(3, 0.0f); 00612 00613 for (i = 0; i < (int)bz_segs.size(); i++) { 00614 t = bz_segs[i]._t; 00615 00616 nurbs->set_knot(ki, t); 00617 nurbs->set_knot(ki+1, t); 00618 nurbs->set_knot(ki+2, t); 00619 ki += 3; 00620 if (i == ((int)bz_segs.size())-1 || 00621 !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001f)) { 00622 nurbs->set_knot(ki, t); 00623 ki++; 00624 } 00625 } 00626 } 00627 00628 return nc->recompute(); 00629 } 00630 00631 00632 //////////////////////////////////////////////////////////////////// 00633 // Function: ParametricCurve::register_drawer 00634 // Access: Public 00635 // Description: Registers a Drawer with this curve that will 00636 // automatically be updated whenever the curve is 00637 // modified, so that the visible representation of the 00638 // curve is kept up to date. This is called 00639 // automatically by the ParametricCurveDrawer. 00640 // 00641 // Any number of Drawers may be registered with a 00642 // particular curve. 00643 //////////////////////////////////////////////////////////////////// 00644 void ParametricCurve:: 00645 register_drawer(ParametricCurveDrawer *drawer) { 00646 _drawers.push_back(drawer); 00647 } 00648 00649 //////////////////////////////////////////////////////////////////// 00650 // Function: ParametricCurve::unregister_drawer 00651 // Access: Public 00652 // Description: Removes a previously registered drawer from the list 00653 // of automatically-refreshed drawers. This is called 00654 // automatically by the ParametricCurveDrawer. 00655 //////////////////////////////////////////////////////////////////// 00656 void ParametricCurve:: 00657 unregister_drawer(ParametricCurveDrawer *drawer) { 00658 _drawers.remove(drawer); 00659 } 00660 00661 00662 00663 00664 //////////////////////////////////////////////////////////////////// 00665 // Function: ParametricCurve::invalidate 00666 // Access: Protected 00667 // Description: Called from a base class to mark a section of the 00668 // curve that has been modified and must be redrawn or 00669 // recomputed in some way. 00670 //////////////////////////////////////////////////////////////////// 00671 void ParametricCurve:: 00672 invalidate(float, float) { 00673 invalidate_all(); 00674 } 00675 00676 //////////////////////////////////////////////////////////////////// 00677 // Function: ParametricCurve::invalidate_all 00678 // Access: Protected 00679 // Description: Called from a base class to indicate that the curve 00680 // has changed in some substantial way and must be 00681 // entirely redrawn. 00682 //////////////////////////////////////////////////////////////////// 00683 void ParametricCurve:: 00684 invalidate_all() { 00685 DrawerList::iterator n; 00686 for (n = _drawers.begin(); 00687 n != _drawers.end(); 00688 ++n) { 00689 (*n)->redraw(); 00690 } 00691 } 00692 00693 //////////////////////////////////////////////////////////////////// 00694 // Function: ParametricCurve::format_egg 00695 // Access: Protected, Virtual 00696 // Description: Formats the curve as an egg structure to write to the 00697 // indicated stream. Returns true on success, false on 00698 // failure. 00699 //////////////////////////////////////////////////////////////////// 00700 bool ParametricCurve:: 00701 format_egg(ostream &, const string &, const string &, int) const { 00702 return false; 00703 } 00704 00705 00706 //////////////////////////////////////////////////////////////////// 00707 // Function: ParametricCurve::r_calc_length 00708 // Access: Private 00709 // Description: The recursive implementation of calc_length. This 00710 // function calculates the length of a segment of the 00711 // curve between points t1 and t2, which presumably 00712 // evaluate to the endpoints p1 and p2, and the segment 00713 // has the length seglength. 00714 //////////////////////////////////////////////////////////////////// 00715 float ParametricCurve:: 00716 r_calc_length(float t1, float t2, const LPoint3f &p1, const LPoint3f &p2, 00717 float seglength) const { 00718 static const float length_tolerance = 0.0000001f; 00719 static const float t_tolerance = 0.000001f; 00720 00721 if (t2 - t1 < t_tolerance) { 00722 // Stop recursing--we've just walked off the limit for 00723 // representing smaller values of t. 00724 return 0.0f; 00725 } 00726 00727 float tmid; 00728 LPoint3f pmid; 00729 float left, right; 00730 00731 // Calculate the point on the curve midway between the two 00732 // endpoints. 00733 tmid = (t1+t2)*0.5f; 00734 get_point(tmid, pmid); 00735 00736 // Did we increase the length of the segment measurably? 00737 left = (p1 - pmid).length(); 00738 right = (pmid - p2).length(); 00739 00740 if ((left + right) - seglength < length_tolerance) { 00741 // No. We're done. 00742 return seglength; 00743 } else { 00744 // Yes. Keep going. 00745 return r_calc_length(t1, tmid, p1, pmid, left) + 00746 r_calc_length(tmid, t2, pmid, p2, right); 00747 } 00748 } 00749 00750 //////////////////////////////////////////////////////////////////// 00751 // Function: ParametricCurve::r_find_length 00752 // Access: Private 00753 // Description: The recursive implementation of find_length. This is 00754 // similar to r_calc_length, above. target_length is 00755 // the length along the curve past t1 that we hope to 00756 // find. If the indicated target_length falls within 00757 // this segment, returns true and sets found_t to the 00758 // point along the segment. Otherwise, updates 00759 // seglength with the accurate calculated length of the 00760 // segment and returns false. 00761 //////////////////////////////////////////////////////////////////// 00762 bool ParametricCurve:: 00763 r_find_length(float target_length, float &found_t, 00764 float t1, float t2, 00765 const LPoint3f &p1, const LPoint3f &p2, 00766 float &seglength) const { 00767 static const float length_tolerance = 0.0000001f; 00768 static const float t_tolerance = 0.000001f; 00769 00770 if (target_length < t_tolerance) { 00771 // Stop recursing--we've just walked off the limit for 00772 // representing smaller values of t. 00773 found_t = t1; 00774 return true; 00775 00776 } 00777 00778 float tmid; 00779 LPoint3f pmid; 00780 float left, right; 00781 00782 // Calculate the point on the curve midway between the two 00783 // endpoints. 00784 tmid = (t1+t2)*0.5f; 00785 get_point(tmid, pmid); 00786 00787 // Did we increase the length of the segment measurably? 00788 left = (p1 - pmid).length(); 00789 right = (pmid - p2).length(); 00790 00791 if ((left + right) - seglength < length_tolerance) { 00792 // No. Curve is relatively straight over this interval. 00793 return find_t_linear(target_length, found_t, t1, t2, p1, p2); 00794 /* 00795 if (target_length <= seglength) { 00796 // Compute t value that corresponds to target_length 00797 // Maybe the point is in the left half of the segment? 00798 if (r_find_t(target_length, found_t, t1, tmid, p1, pmid)) { 00799 return true; 00800 } 00801 // Maybe it's on the right half? 00802 if (r_find_t(target_length - left, found_t, tmid, t2, pmid, p2)) { 00803 return true; 00804 } 00805 } 00806 return false; 00807 */ 00808 } else { 00809 // Yes. Keep going. 00810 00811 // Maybe the point is in the left half of the segment? 00812 if (r_find_length(target_length, found_t, t1, tmid, p1, pmid, left)) { 00813 return true; 00814 } 00815 00816 // Maybe it's on the right half? 00817 if (r_find_length(target_length - left, found_t, tmid, t2, pmid, p2, right)) { 00818 return true; 00819 } 00820 00821 // Neither. Keep going. 00822 seglength = left + right; 00823 return false; 00824 } 00825 } 00826 00827 00828 00829 //////////////////////////////////////////////////////////////////// 00830 // Function: ParametricCurve::r_find_t 00831 // Access: Private 00832 // Description: computes the t value in the parametric domain of a 00833 // target point along a straight section of a curve. 00834 // This is similar to r_calc_length, above. 00835 // target_length is the length along the curve past t1 00836 // that we hope to find. If the indicated target_length 00837 // falls within this segment, returns true and sets 00838 // found_t to the point along the segment. 00839 //////////////////////////////////////////////////////////////////// 00840 bool ParametricCurve:: 00841 r_find_t(float target_length, float &found_t, 00842 float t1, float t2, 00843 const LPoint3f &p1, const LPoint3f &p2) const { 00844 static const float length_tolerance = 0.0001f; 00845 static const float t_tolerance = 0.0001f; 00846 00847 if (parametrics_cat.is_spam()) { 00848 parametrics_cat.spam() 00849 << "target_length " << target_length << " t1 " << t1 << " t2 " << t2 << "\n"; 00850 } 00851 00852 // Is the target point close to the near endpoint 00853 if (target_length < length_tolerance) { 00854 found_t = t1; 00855 return true; 00856 } 00857 00858 // No, compute distance between two endpoints 00859 float point_dist; 00860 point_dist = (p2 - p1).length(); 00861 00862 // Is the target point past the far endpoint? 00863 if (point_dist < target_length) { 00864 return false; 00865 } 00866 00867 // Is the target point close to far endpoint? 00868 if ( (point_dist - target_length ) < length_tolerance ) { 00869 found_t = t2; 00870 return true; 00871 } 00872 00873 // are we running out of parametric precision? 00874 if ((t2 - t1) < t_tolerance) { 00875 found_t = t1; 00876 return true; 00877 } 00878 00879 // No, subdivide and continue 00880 float tmid; 00881 LPoint3f pmid; 00882 float left; 00883 00884 // Calculate the point on the curve midway between the two 00885 // endpoints. 00886 tmid = (t1+t2)*0.5f; 00887 get_point(tmid, pmid); 00888 00889 // Maybe the point is in the left half of the segment? 00890 if (r_find_t(target_length, found_t, t1, tmid, p1, pmid)) { 00891 return true; 00892 } 00893 // Nope, must be in the right half 00894 left = (p1 - pmid).length(); 00895 if (r_find_t(target_length - left, found_t, tmid, t2, pmid, p2)) { 00896 return true; 00897 } 00898 00899 // not found in either half, keep looking 00900 return false; 00901 } 00902 00903 00904 //////////////////////////////////////////////////////////////////// 00905 // Function: ParametricCurve::find_t_linear 00906 // Access: Private 00907 // Description: non-recursive version of r_find_t (see above) 00908 //////////////////////////////////////////////////////////////////// 00909 bool ParametricCurve:: 00910 find_t_linear(float target_length, float &found_t, 00911 float t1, float t2, 00912 const LPoint3f &p1, const LPoint3f &p2) const { 00913 const float length_tolerance = (p1-p2).length()/tolerance_divisor; 00914 const float t_tolerance = (t1+t2)/tolerance_divisor; 00915 00916 if (parametrics_cat.is_spam()) { 00917 parametrics_cat.spam() 00918 << "target_length " << target_length << " t1 " << t1 << " t2 " << t2 << "\n"; 00919 } 00920 00921 // first, check to make sure this segment contains the point 00922 // we're looking for 00923 if (target_length > (p1 - p2).length()) { 00924 // segment is too short 00925 return false; 00926 } 00927 00928 float tleft = t1; 00929 float tright = t2; 00930 float tmid; 00931 LPoint3f pmid; 00932 float len; 00933 00934 while (1) { 00935 tmid = (tleft + tright) * 0.5f; 00936 get_point(tmid, pmid); 00937 len = (pmid - p1).length(); 00938 00939 /* 00940 if (parametrics_cat.is_spam()) { 00941 parametrics_cat.spam() 00942 << "tleft " << tleft << " tright " << tright << 00943 " tmid " << tmid << " len " << len << endl; 00944 } 00945 */ 00946 00947 // is our midpoint at the right distance? 00948 if (fabs(len - target_length) < length_tolerance) { 00949 found_t = tmid; 00950 return true; 00951 } 00952 00953 /* 00954 if (parametrics_cat.is_spam()) { 00955 parametrics_cat.spam() 00956 << "tright-tleft " << tright-tleft << " t_tolerance " << t_tolerance << endl; 00957 } 00958 */ 00959 00960 // are we out of parametric precision? 00961 if ((tright - tleft) < t_tolerance) { 00962 // unfortunately, we can't get any closer in parametric space 00963 found_t = tmid; 00964 return true; 00965 } 00966 00967 // should we look closer or farther? 00968 if (len > target_length) { 00969 // look closer 00970 tright = tmid; 00971 } else { 00972 // look farther 00973 tleft = tmid; 00974 } 00975 } 00976 } 00977 00978 00979 //////////////////////////////////////////////////////////////////// 00980 // Function: ParametricCurve::write_datagram 00981 // Access: Protected, Virtual 00982 // Description: Function to write the important information in 00983 // the particular object to a Datagram 00984 //////////////////////////////////////////////////////////////////// 00985 void ParametricCurve:: 00986 write_datagram(BamWriter *manager, Datagram &me) { 00987 PandaNode::write_datagram(manager, me); 00988 00989 me.add_int8(_curve_type); 00990 me.add_int8(_num_dimensions); 00991 } 00992 00993 //////////////////////////////////////////////////////////////////// 00994 // Function: ParametricCurve::fillin 00995 // Access: Protected 00996 // Description: Function that reads out of the datagram (or asks 00997 // manager to read) all of the data that is needed to 00998 // re-create this object and stores it in the appropiate 00999 // place 01000 //////////////////////////////////////////////////////////////////// 01001 void ParametricCurve:: 01002 fillin(DatagramIterator &scan, BamReader *manager) { 01003 PandaNode::fillin(scan, manager); 01004 01005 _curve_type = scan.get_int8(); 01006 _num_dimensions = scan.get_int8(); 01007 }