Panda3D

parametricCurve.cxx

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 }
 All Classes Functions Variables Enumerations