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