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

panda/src/parametrics/parametricCurve.cxx

Go to the documentation of this file.
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 }

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