Panda3D
parametricCurve.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file parametricCurve.cxx
10  * @author drose
11  * @date 2001-03-04
12  */
13 
14 #include "parametricCurve.h"
15 #include "config_parametrics.h"
16 #include "hermiteCurve.h"
17 #include "nurbsCurve.h"
18 
19 #include "datagram.h"
20 #include "datagramIterator.h"
21 #include "bamWriter.h"
22 #include "bamReader.h"
23 #include "omniBoundingVolume.h"
24 
25 static const PN_stdfloat tolerance_divisor = 100000.0f;
26 
27 TypeHandle ParametricCurve::_type_handle;
28 
29 
30 /**
31  * This is a virtual base class. Don't try to construct one from Scheme.
32  */
34 ParametricCurve() : PandaNode("curve") {
35  _curve_type = PCT_NONE;
36  _num_dimensions = 3;
37 }
38 
39 /**
40  *
41  */
42 ParametricCurve::
43 ~ParametricCurve() {
44  // Our drawer list must be empty by the time we destruct, since our drawers
45  // all maintain reference-counting pointers to us! If this is not so, we
46  // have lost a reference count somewhere, or we have gotten confused about
47  // which drawers we're registered to.
48  nassertv(_drawers.empty());
49 }
50 
51 /**
52  * Returns true if it is generally safe to flatten out this particular kind of
53  * PandaNode by duplicating instances, false otherwise (for instance, a Camera
54  * cannot be safely flattened, because the Camera pointer itself is
55  * meaningful).
56  */
58 safe_to_flatten() const {
59  return false;
60 }
61 
62 /**
63  * Returns true if it is generally safe to transform this particular kind of
64  * PandaNode by calling the xform() method, false otherwise. For instance,
65  * it's usually a bad idea to attempt to xform a Character.
66  */
69  return false;
70 }
71 
72 /**
73  * Returns true if the curve is defined. This base class function always
74  * returns true; derived classes might override this to sometimes return
75  * false.
76  */
78 is_valid() const {
79  return true;
80 }
81 
82 
83 /**
84  * Returns the upper bound of t for the entire curve. The curve is defined in
85  * the range 0.0f <= t <= get_max_t(). This base class function always
86  * returns 1.0f; derived classes might override this to return something else.
87  */
88 PN_stdfloat ParametricCurve::
89 get_max_t() const {
90  return 1.0f;
91 }
92 
93 
94 /**
95  * Sets the flag indicating the use to which the curve is intended to be put.
96  * This flag is optional and only serves to provide a hint to the egg reader
97  * and writer code; it has no effect on the curve's behavior.
98  *
99  * Setting the curve type also sets the num_dimensions to 3 or 1 according to
100  * the type.
101  *
102  * THis flag may have one of the values PCT_XYZ, PCT_HPR, or PCT_T.
103  */
105 set_curve_type(int type) {
106  _curve_type = type;
107  switch (_curve_type) {
108  case PCT_XYZ:
109  case PCT_HPR:
110  case PCT_NONE:
111  _num_dimensions = 3;
112  break;
113 
114  case PCT_T:
115  _num_dimensions = 1;
116  break;
117 
118  default:
119  assert(0);
120  }
121 }
122 
123 /**
124  * Returns the flag indicating the use to which the curve is intended to be
125  * put.
126  */
128 get_curve_type() const {
129  return _curve_type;
130 }
131 
132 /**
133  * Specifies the number of significant dimensions in the curve's vertices.
134  * This should be one of 1, 2, or 3. Normally, XYZ and HPR curves have three
135  * dimensions; time curves should always have one dimension. This only serves
136  * as a hint to the mopath editor, and also controls how the curve is written
137  * out.
138  */
141  _num_dimensions = num;
142 }
143 
144 /**
145  * Returns the number of significant dimensions in the curve's vertices, as
146  * set by a previous call to set_num_dimensions(). This is only a hint as to
147  * how the curve is intended to be used; the actual number of dimensions of
148  * any curve is always three.
149  */
152  return _num_dimensions;
153 }
154 
155 
156 /**
157  * Approximates the length of the entire curve to within a few decimal places.
158  */
159 PN_stdfloat ParametricCurve::
160 calc_length() const {
161  return calc_length(0.0f, get_max_t());
162 }
163 
164 /**
165  * Approximates the length of the curve segment from parametric time 'from' to
166  * time 'to'.
167  */
168 PN_stdfloat ParametricCurve::
169 calc_length(PN_stdfloat from, PN_stdfloat to) const {
170  PN_stdfloat t1, t2;
171  LPoint3 p1, p2;
172 
173  // Normally we expect from < to. If they came in backwards, reverse them.
174  PN_stdfloat to_minus_from = to - from;
175 
176  if (to_minus_from < 0.0f) {
177  PN_stdfloat temp = to;
178  to = from;
179  from = temp;
180  to_minus_from=-to_minus_from;
181  }
182 
183  // Start with a segment for each unit of t.
184  int num_segs = (int)(to_minus_from) + 1;
185  t2 = from;
186  get_point(t2, p2);
187  PN_stdfloat net = 0.0f;
188 
189  for (int i = 1; i <= num_segs; i++) {
190  t1 = t2;
191  p1 = p2;
192 
193  t2 = (to - from) * (PN_stdfloat)i / (PN_stdfloat)num_segs + from;
194  get_point(t2, p2);
195 
196  net += r_calc_length(t1, t2, p1, p2, (p1 - p2).length());
197  }
198  return net;
199 }
200 
201 /**
202  * Returns the parametric value corresponding to the indicated distance along
203  * the curve from the starting parametric value.
204  *
205  * This is the inverse of calc_length(): rather than determining the length
206  * along the curve between two parametric points, it determines the position
207  * in parametric time of a point n units along the curve.
208  *
209  * The search distance must not be negative.
210  */
211 PN_stdfloat ParametricCurve::
212 find_length(PN_stdfloat start_t, PN_stdfloat length_offset) const {
213  nassertr(length_offset >= 0.0f, start_t);
214  nassertr(start_t >= 0.0f && start_t <= get_max_t(), start_t);
215 
216  PN_stdfloat t1, t2;
217  LPoint3 p1, p2;
218 
219  // Start with a segment for each unit of t.
220  PN_stdfloat max_t = get_max_t();
221  int num_segs = (int)cfloor(max_t - start_t + 1);
222  t2 = start_t;
223  get_point(t2, p2);
224  PN_stdfloat net = 0.0f;
225 
226  for (int i = 1; i <= num_segs; i++) {
227  assert(net <= length_offset);
228 
229  t1 = t2;
230  p1 = p2;
231 
232  t2 = start_t + (((max_t - start_t) * (PN_stdfloat)i) / (PN_stdfloat)num_segs);
233  get_point(t2, p2);
234 
235  PN_stdfloat seglength = (p1 - p2).length();
236  PN_stdfloat result;
237 
238  if (r_find_length(length_offset - net, result,
239  t1, t2, p1, p2, seglength)) {
240  // Found it!
241  return result;
242  }
243 
244  net += seglength;
245  }
246 
247  // Not on the curve? Huh.
248  return max_t;
249 }
250 
251 /**
252  * Recomputes the curve such that it passes through the point (px, py, pz) at
253  * time t, but keeps the same tangent value at that point.
254  */
256 adjust_point(PN_stdfloat, PN_stdfloat, PN_stdfloat, PN_stdfloat) {
257  return false;
258 }
259 
260 /**
261  * Recomputes the curve such that it has the tangent (tx, ty, tz) at time t,
262  * but keeps the same position at the point.
263  */
265 adjust_tangent(PN_stdfloat, PN_stdfloat, PN_stdfloat, PN_stdfloat) {
266  return false;
267 }
268 
269 /**
270  * Recomputes the curve such that it passes through the point (px, py, pz)
271  * with the tangent (tx, ty, tz).
272  */
274 adjust_pt(PN_stdfloat, PN_stdfloat, PN_stdfloat, PN_stdfloat, PN_stdfloat, PN_stdfloat, PN_stdfloat) {
275  return false;
276 }
277 
278 /**
279  * Recalculates the curve, if necessary. Returns true if the resulting curve
280  * is valid, false otherwise.
281  */
284  return is_valid();
285 }
286 
287 /**
288  * Regenerates this curve as one long curve: the first curve connected end-to-
289  * end with the second one. Either a or b may be the same as 'this'.
290  *
291  * Returns true if successful, false on failure or if the curve type does not
292  * support stitching.
293  */
296  parametrics_cat.error()
297  << get_type() << " does not support stitching.\n";
298  return false;
299 }
300 
301 
302 /**
303  * Writes an egg description of the nurbs curve to the specified output file.
304  * Returns true if the file is successfully written.
305  */
307 write_egg(Filename filename, CoordinateSystem cs) {
308  pofstream out;
309  filename.set_text();
310 
311  if (!filename.open_write(out)) {
312  parametrics_cat.error()
313  << "Unable to write to " << filename << "\n";
314  return false;
315  }
316  return write_egg(out, filename, cs);
317 }
318 
319 /**
320  * Writes an egg description of the nurbs curve to the specified output
321  * stream. Returns true if the file is successfully written.
322  */
324 write_egg(std::ostream &out, const Filename &filename, CoordinateSystem cs) {
325  std::string curve_type;
326  switch (get_curve_type()) {
327  case PCT_XYZ:
328  curve_type = "xyz";
329  break;
330 
331  case PCT_HPR:
332  curve_type = "hpr";
333  break;
334 
335  case PCT_T:
336  curve_type = "t";
337  break;
338  }
339 
340  if (!has_name()) {
341  // If we don't have a name, come up with one.
342  std::string name = filename.get_basename_wo_extension();
343 
344  if (!curve_type.empty()) {
345  name += "_";
346  name += curve_type;
347  }
348 
349  set_name(name);
350  }
351 
352  if (cs == CS_default) {
353  cs = get_default_coordinate_system();
354  }
355 
356  if (cs != CS_invalid) {
357  out << "<CoordinateSystem> { ";
358  switch (cs) {
359  case CS_zup_right:
360  out << "Z-Up";
361  break;
362 
363  case CS_yup_right:
364  out << "Y-Up";
365  break;
366 
367  case CS_zup_left:
368  out << "Z-Up-Left";
369  break;
370 
371  case CS_yup_left:
372  out << "Y-Up-Left";
373  break;
374 
375  default:
376  break;
377  }
378  out << " }\n\n";
379  }
380 
381 
382  if (!format_egg(out, get_name(), curve_type, 0)) {
383  return false;
384  }
385 
386  if (out) {
387  return true;
388  } else {
389  return false;
390  }
391 }
392 
393 
394 
395 /**
396  * Fills up the indicated vector with a list of BezierSeg structs that
397  * describe the curve. This assumes the curve is a PiecewiseCurve of
398  * CubicCurvesegs. Returns true if successful, false otherwise.
399  */
402  return false;
403 }
404 
405 /**
406  * Fills the BezierSeg structure with a description of the curve segment as a
407  * Bezier, if possible, but does not change the _t member of the structure.
408  * Returns true if successful, false otherwise.
409  */
412  return false;
413 }
414 
415 /**
416  * Returns a pointer to the object as a NurbsCurveInterface object if it
417  * happens to be a NURBS-style curve; otherwise, returns NULL.
418  */
421  return nullptr;
422 }
423 
424 /**
425  * Stores an equivalent curve representation in the indicated Hermite curve,
426  * if possible. Returns true if successful, false otherwise.
427  */
430  BezierSegs bz_segs;
431  if (!get_bezier_segs(bz_segs)) {
432  return false;
433  }
434 
435  hc->set_curve_type(_curve_type);
436 
437  // Now convert the Bezier segments to a Hermite. Normally, the Beziers will
438  // match up head-to-tail, but if they don't, that's a cut.
439  hc->remove_all_cvs();
440 
441  int i, n;
442  if (!bz_segs.empty()) {
443  PN_stdfloat scale_in = 0.0f;
444  PN_stdfloat scale_out = bz_segs[0]._t;
445  n = hc->append_cv(HC_SMOOTH, bz_segs[0]._v[0]);
446  hc->set_cv_out(n, 3.0f * (bz_segs[0]._v[1] - bz_segs[0]._v[0]) / scale_out);
447 
448  for (i = 0; i < (int)bz_segs.size()-1; i++) {
449  scale_in = scale_out;
450  scale_out = bz_segs[i+1]._t - bz_segs[i]._t;
451 
452  if (!bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001f)) {
453  // Oops, we have a cut.
454  hc->set_cv_type(n, HC_CUT);
455  }
456 
457  n = hc->append_cv(HC_FREE, bz_segs[i+1]._v[0]);
458  hc->set_cv_in(n, 3.0f * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in);
459  hc->set_cv_tstart(n, bz_segs[i]._t);
460 
461  hc->set_cv_out(n, 3.0f * (bz_segs[i+1]._v[1] - bz_segs[i+1]._v[0]) / scale_out);
462  }
463 
464  // Now the last CV.
465  scale_in = scale_out;
466  i = bz_segs.size()-1;
467  n = hc->append_cv(HC_SMOOTH, bz_segs[i]._v[3]);
468  hc->set_cv_in(n, 3.0f * (bz_segs[i]._v[3] - bz_segs[i]._v[2]) / scale_in);
469  hc->set_cv_tstart(n, bz_segs[i]._t);
470  }
471 
472  // Finally, go through and figure out which CV's are smooth or G1.
473  int num_cvs = hc->get_num_cvs();
474  for (n = 1; n < num_cvs-1; n++) {
475  if (hc->get_cv_type(n)!=HC_CUT) {
476  LVector3 in = hc->get_cv_in(n);
477  LVector3 out = hc->get_cv_out(n);
478 
479  if (in.almost_equal(out, 0.0001f)) {
480  hc->set_cv_type(n, HC_SMOOTH);
481  } else {
482  in.normalize();
483  out.normalize();
484  if (in.almost_equal(out, 0.0001f)) {
485  hc->set_cv_type(n, HC_G1);
486  }
487  }
488  }
489  }
490  return true;
491 }
492 
493 /**
494  * Stores in the indicated NurbsCurve a NURBS representation of an equivalent
495  * curve. Returns true if successful, false otherwise.
496  */
500  nassertr(nurbs != nullptr, false);
501 
502  BezierSegs bz_segs;
503  if (!get_bezier_segs(bz_segs)) {
504  return false;
505  }
506 
507  nc->set_curve_type(_curve_type);
508 
509  nurbs->remove_all_cvs();
510  nurbs->set_order(4);
511  if (!bz_segs.empty()) {
512  int i;
513  for (i = 0; i < (int)bz_segs.size(); i++) {
514  nurbs->append_cv(bz_segs[i]._v[0]);
515  nurbs->append_cv(bz_segs[i]._v[1]);
516  nurbs->append_cv(bz_segs[i]._v[2]);
517  if (i == (int)bz_segs.size()-1 ||
518  !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001f)) {
519  nurbs->append_cv(bz_segs[i]._v[3]);
520  }
521  }
522 
523  PN_stdfloat t;
524  int ki = 4;
525  nurbs->set_knot(0, 0.0f);
526  nurbs->set_knot(1, 0.0f);
527  nurbs->set_knot(2, 0.0f);
528  nurbs->set_knot(3, 0.0f);
529 
530  for (i = 0; i < (int)bz_segs.size(); i++) {
531  t = bz_segs[i]._t;
532 
533  nurbs->set_knot(ki, t);
534  nurbs->set_knot(ki+1, t);
535  nurbs->set_knot(ki+2, t);
536  ki += 3;
537  if (i == ((int)bz_segs.size())-1 ||
538  !bz_segs[i]._v[3].almost_equal(bz_segs[i+1]._v[0], 0.0001f)) {
539  nurbs->set_knot(ki, t);
540  ki++;
541  }
542  }
543  }
544 
545  return nc->recompute();
546 }
547 
548 
549 /**
550  * Registers a Drawer with this curve that will automatically be updated
551  * whenever the curve is modified, so that the visible representation of the
552  * curve is kept up to date. This is called automatically by the
553  * ParametricCurveDrawer.
554  *
555  * Any number of Drawers may be registered with a particular curve.
556  */
558 register_drawer(ParametricCurveDrawer *drawer) {
559  _drawers.push_back(drawer);
560 }
561 
562 /**
563  * Removes a previously registered drawer from the list of automatically-
564  * refreshed drawers. This is called automatically by the
565  * ParametricCurveDrawer.
566  */
568 unregister_drawer(ParametricCurveDrawer *drawer) {
569  _drawers.remove(drawer);
570 }
571 
572 
573 
574 
575 /**
576  * Called from a base class to mark a section of the curve that has been
577  * modified and must be redrawn or recomputed in some way.
578  */
579 void ParametricCurve::
580 invalidate(PN_stdfloat, PN_stdfloat) {
581  invalidate_all();
582 }
583 
584 /**
585  * Called from a base class to indicate that the curve has changed in some
586  * substantial way and must be entirely redrawn.
587  */
588 void ParametricCurve::
589 invalidate_all() {
590  /*
591  DrawerList::iterator n;
592  for (n = _drawers.begin();
593  n != _drawers.end();
594  ++n) {
595  (*n)->redraw();
596  }
597  */
598 }
599 
600 /**
601  * Formats the curve as an egg structure to write to the indicated stream.
602  * Returns true on success, false on failure.
603  */
604 bool ParametricCurve::
605 format_egg(std::ostream &, const std::string &, const std::string &, int) const {
606  return false;
607 }
608 
609 
610 /**
611  * The recursive implementation of calc_length. This function calculates the
612  * length of a segment of the curve between points t1 and t2, which presumably
613  * evaluate to the endpoints p1 and p2, and the segment has the length
614  * seglength.
615  */
616 PN_stdfloat ParametricCurve::
617 r_calc_length(PN_stdfloat t1, PN_stdfloat t2, const LPoint3 &p1, const LPoint3 &p2,
618  PN_stdfloat seglength) const {
619  static const PN_stdfloat length_tolerance = 0.0000001f;
620  static const PN_stdfloat t_tolerance = 0.000001f;
621 
622  if (t2 - t1 < t_tolerance) {
623  // Stop recursing--we've just walked off the limit for representing
624  // smaller values of t.
625  return 0.0f;
626  }
627 
628  PN_stdfloat tmid;
629  LPoint3 pmid;
630  PN_stdfloat left, right;
631 
632  // Calculate the point on the curve midway between the two endpoints.
633  tmid = (t1+t2)*0.5f;
634  get_point(tmid, pmid);
635 
636  // Did we increase the length of the segment measurably?
637  left = (p1 - pmid).length();
638  right = (pmid - p2).length();
639 
640  if ((left + right) - seglength < length_tolerance) {
641  // No. We're done.
642  return seglength;
643  } else {
644  // Yes. Keep going.
645  return r_calc_length(t1, tmid, p1, pmid, left) +
646  r_calc_length(tmid, t2, pmid, p2, right);
647  }
648 }
649 
650 /**
651  * The recursive implementation of find_length. This is similar to
652  * r_calc_length, above. target_length is the length along the curve past t1
653  * that we hope to find. If the indicated target_length falls within this
654  * segment, returns true and sets found_t to the point along the segment.
655  * Otherwise, updates seglength with the accurate calculated length of the
656  * segment and returns false.
657  */
658 bool ParametricCurve::
659 r_find_length(PN_stdfloat target_length, PN_stdfloat &found_t,
660  PN_stdfloat t1, PN_stdfloat t2,
661  const LPoint3 &p1, const LPoint3 &p2,
662  PN_stdfloat &seglength) const {
663  static const PN_stdfloat length_tolerance = 0.0000001f;
664  static const PN_stdfloat t_tolerance = 0.000001f;
665 
666  if (target_length < t_tolerance) {
667  // Stop recursing--we've just walked off the limit for representing
668  // smaller values of t.
669  found_t = t1;
670  return true;
671 
672  }
673 
674  PN_stdfloat tmid;
675  LPoint3 pmid;
676  PN_stdfloat left, right;
677 
678  // Calculate the point on the curve midway between the two endpoints.
679  tmid = (t1+t2)*0.5f;
680  get_point(tmid, pmid);
681 
682  // Did we increase the length of the segment measurably?
683  left = (p1 - pmid).length();
684  right = (pmid - p2).length();
685 
686  if ((left + right) - seglength < length_tolerance) {
687  // No. Curve is relatively straight over this interval.
688  return find_t_linear(target_length, found_t, t1, t2, p1, p2);
689  /*
690  if (target_length <= seglength) {
691  // Compute t value that corresponds to target_length Maybe the point is
692  // in the left half of the segment?
693  if (r_find_t(target_length, found_t, t1, tmid, p1, pmid)) {
694  return true;
695  }
696  // Maybe it's on the right half?
697  if (r_find_t(target_length - left, found_t, tmid, t2, pmid, p2)) {
698  return true;
699  }
700  }
701  return false;
702  */
703  } else {
704  // Yes. Keep going.
705 
706  // Maybe the point is in the left half of the segment?
707  if (r_find_length(target_length, found_t, t1, tmid, p1, pmid, left)) {
708  return true;
709  }
710 
711  // Maybe it's on the right half?
712  if (r_find_length(target_length - left, found_t, tmid, t2, pmid, p2, right)) {
713  return true;
714  }
715 
716  // Neither. Keep going.
717  seglength = left + right;
718  return false;
719  }
720 }
721 
722 
723 
724 /**
725  * computes the t value in the parametric domain of a target point along a
726  * straight section of a curve. This is similar to r_calc_length, above.
727  * target_length is the length along the curve past t1 that we hope to find.
728  * If the indicated target_length falls within this segment, returns true and
729  * sets found_t to the point along the segment.
730  */
731 bool ParametricCurve::
732 r_find_t(PN_stdfloat target_length, PN_stdfloat &found_t,
733  PN_stdfloat t1, PN_stdfloat t2,
734  const LPoint3 &p1, const LPoint3 &p2) const {
735  static const PN_stdfloat length_tolerance = 0.0001f;
736  static const PN_stdfloat t_tolerance = 0.0001f;
737 
738  if (parametrics_cat.is_spam()) {
739  parametrics_cat.spam()
740  << "target_length " << target_length << " t1 " << t1 << " t2 " << t2 << "\n";
741  }
742 
743  // Is the target point close to the near endpoint
744  if (target_length < length_tolerance) {
745  found_t = t1;
746  return true;
747  }
748 
749  // No, compute distance between two endpoints
750  PN_stdfloat point_dist;
751  point_dist = (p2 - p1).length();
752 
753  // Is the target point past the far endpoint?
754  if (point_dist < target_length) {
755  return false;
756  }
757 
758  // Is the target point close to far endpoint?
759  if ( (point_dist - target_length ) < length_tolerance ) {
760  found_t = t2;
761  return true;
762  }
763 
764  // are we running out of parametric precision?
765  if ((t2 - t1) < t_tolerance) {
766  found_t = t1;
767  return true;
768  }
769 
770  // No, subdivide and continue
771  PN_stdfloat tmid;
772  LPoint3 pmid;
773  PN_stdfloat left;
774 
775  // Calculate the point on the curve midway between the two endpoints.
776  tmid = (t1+t2)*0.5f;
777  get_point(tmid, pmid);
778 
779  // Maybe the point is in the left half of the segment?
780  if (r_find_t(target_length, found_t, t1, tmid, p1, pmid)) {
781  return true;
782  }
783  // Nope, must be in the right half
784  left = (p1 - pmid).length();
785  if (r_find_t(target_length - left, found_t, tmid, t2, pmid, p2)) {
786  return true;
787  }
788 
789  // not found in either half, keep looking
790  return false;
791 }
792 
793 
794 /**
795  * non-recursive version of r_find_t (see above)
796  */
797 bool ParametricCurve::
798 find_t_linear(PN_stdfloat target_length, PN_stdfloat &found_t,
799  PN_stdfloat t1, PN_stdfloat t2,
800  const LPoint3 &p1, const LPoint3 &p2) const {
801  const PN_stdfloat length_tolerance = (p1-p2).length()/tolerance_divisor;
802  const PN_stdfloat t_tolerance = (t1+t2)/tolerance_divisor;
803 
804  if (parametrics_cat.is_spam()) {
805  parametrics_cat.spam()
806  << "target_length " << target_length << " t1 " << t1 << " t2 " << t2 << "\n";
807  }
808 
809  // first, check to make sure this segment contains the point we're looking
810  // for
811  if (target_length > (p1 - p2).length()) {
812  // segment is too short
813  return false;
814  }
815 
816  PN_stdfloat tleft = t1;
817  PN_stdfloat tright = t2;
818  PN_stdfloat tmid;
819  LPoint3 pmid;
820  PN_stdfloat len;
821 
822  while (1) {
823  tmid = (tleft + tright) * 0.5f;
824  get_point(tmid, pmid);
825  len = (pmid - p1).length();
826 
827  /*
828  if (parametrics_cat.is_spam()) {
829  parametrics_cat.spam()
830  << "tleft " << tleft << " tright " << tright <<
831  " tmid " << tmid << " len " << len << endl;
832  }
833  */
834 
835  // is our midpoint at the right distance?
836  if (fabs(len - target_length) < length_tolerance) {
837  found_t = tmid;
838  return true;
839  }
840 
841  /*
842  if (parametrics_cat.is_spam()) {
843  parametrics_cat.spam()
844  << "tright-tleft " << tright-tleft << " t_tolerance " << t_tolerance << endl;
845  }
846  */
847 
848  // are we out of parametric precision?
849  if ((tright - tleft) < t_tolerance) {
850  // unfortunately, we can't get any closer in parametric space
851  found_t = tmid;
852  return true;
853  }
854 
855  // should we look closer or farther?
856  if (len > target_length) {
857  // look closer
858  tright = tmid;
859  } else {
860  // look farther
861  tleft = tmid;
862  }
863  }
864 }
865 
866 
867 /**
868  * Function to write the important information in the particular object to a
869  * Datagram
870  */
871 void ParametricCurve::
872 write_datagram(BamWriter *manager, Datagram &me) {
873  PandaNode::write_datagram(manager, me);
874 
875  me.add_int8(_curve_type);
876  me.add_int8(_num_dimensions);
877 }
878 
879 /**
880  * Function that reads out of the datagram (or asks manager to read) all of
881  * the data that is needed to re-create this object and stores it in the
882  * appropiate place
883  */
884 void ParametricCurve::
885 fillin(DatagramIterator &scan, BamReader *manager) {
886  PandaNode::fillin(scan, manager);
887 
888  _curve_type = scan.get_int8();
889  _num_dimensions = scan.get_int8();
890 }
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
virtual bool adjust_point(PN_stdfloat t, PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz)
Recomputes the curve such that it passes through the point (px, py, pz) at time t,...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: pandaNode.cxx:3589
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
A virtual base class for parametric curves.
int get_cv_type(int n) const
Returns the given CV's continuity type, HC_CUT, HC_FREE, HC_G1, or HC_SMOOTH, or 0 if there is no suc...
int get_num_dimensions() const
Returns the number of significant dimensions in the curve's vertices, as set by a previous call to se...
bool set_cv_out(int n, PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Changes the given CV's out tangent.
virtual bool recompute()
Recalculates the curve, if necessary.
ParametricCurve()
This is a virtual base class.
void register_drawer(ParametricCurveDrawer *drawer)
Registers a Drawer with this curve that will automatically be updated whenever the curve is modified,...
int append_cv(int type, PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Adds a new CV to the end of the curve.
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
PN_stdfloat find_length(PN_stdfloat start_t, PN_stdfloat length_offset) const
Returns the parametric value corresponding to the indicated distance along the curve from the startin...
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
bool write_egg(Filename filename, CoordinateSystem cs=CS_default)
Writes an egg description of the nurbs curve to the specified output file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool convert_to_hermite(HermiteCurve *hc) const
Stores an equivalent curve representation in the indicated Hermite curve, if possible.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool safe_to_transform() const
Returns true if it is generally safe to transform this particular kind of PandaNode by calling the xf...
void set_num_dimensions(int num)
Specifies the number of significant dimensions in the curve's vertices.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
This abstract class defines the interface only for a Nurbs-style curve, with knots and coordinates in...
int get_num_cvs() const
Returns the number of CV's in the curve.
virtual NurbsCurveInterface * get_nurbs_interface()
Returns a pointer to the object as a NurbsCurveInterface object if it happens to be a NURBS-style cur...
A parametric curve defined by a sequence of control vertices, each with an in and out tangent.
Definition: hermiteCurve.h:83
virtual bool stitch(const ParametricCurve *a, const ParametricCurve *b)
Regenerates this curve as one long curve: the first curve connected end-to- end with the second one.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
virtual bool is_valid() const
Returns true if the curve is defined.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_int8(int8_t value)
Adds a signed 8-bit integer to the datagram.
Definition: datagram.I:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void remove_all_cvs()
Removes all CV's from the curve.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const LVecBase3 & get_cv_in(int n) const
Returns the in tangent of the given CV.
virtual PN_stdfloat get_max_t() const
Returns the upper bound of t for the entire curve.
virtual bool get_bezier_segs(BezierSegs &) const
Fills up the indicated vector with a list of BezierSeg structs that describe the curve.
virtual bool safe_to_flatten() const
Returns true if it is generally safe to flatten out this particular kind of PandaNode by duplicating ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool set_cv_type(int n, int type)
Changes the given CV's continuity type.
virtual bool adjust_pt(PN_stdfloat t, PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat tx, PN_stdfloat ty, PN_stdfloat tz)
Recomputes the curve such that it passes through the point (px, py, pz) with the tangent (tx,...
virtual bool adjust_tangent(PN_stdfloat t, PN_stdfloat tx, PN_stdfloat ty, PN_stdfloat tz)
Recomputes the curve such that it has the tangent (tx, ty, tz) at time t, but keeps the same position...
void set_curve_type(int type)
Sets the flag indicating the use to which the curve is intended to be put.
bool set_cv_tstart(int n, PN_stdfloat tstart)
Changes the given CV's parametric starting time.
PN_stdfloat calc_length() const
Approximates the length of the entire curve to within a few decimal places.
void unregister_drawer(ParametricCurveDrawer *drawer)
Removes a previously registered drawer from the list of automatically- refreshed drawers.
A class to retrieve the individual data elements previously stored in a Datagram.
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:44
int8_t get_int8()
Extracts a signed 8-bit integer.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
virtual bool convert_to_nurbs(ParametricCurve *nc) const
Stores in the indicated NurbsCurve a NURBS representation of an equivalent curve.
bool set_cv_in(int n, PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Changes the given CV's in tangent.
virtual bool get_bezier_seg(BezierSeg &) const
Fills the BezierSeg structure with a description of the curve segment as a Bezier,...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
const LVecBase3 & get_cv_out(int n) const
Returns the out tangent of the given CV.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_curve_type() const
Returns the flag indicating the use to which the curve is intended to be put.