Panda3D
parametricCurveCollection.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 parametricCurveCollection.cxx
10  * @author drose
11  * @date 2001-03-04
12  */
13 
15 #include "config_parametrics.h"
16 #include "curveFitter.h"
17 #include "nurbsCurve.h"
18 
19 #include "indent.h"
20 #include "compose_matrix.h"
21 #include "string_utils.h"
22 #include "look_at.h"
23 
24 /**
25  *
26  */
27 ParametricCurveCollection::
28 ParametricCurveCollection() {
29 }
30 
31 /**
32  * Adds a new ParametricCurve to the collection.
33  */
36  prepare_add_curve(curve);
37  _curves.push_back(curve);
38  redraw();
39 }
40 
41 /**
42  * Adds a new ParametricCurve to the collection at the indicated index.
43  */
45 insert_curve(size_t index, ParametricCurve *curve) {
46  prepare_add_curve(curve);
47  index = std::min(index, _curves.size());
48  _curves.insert(_curves.begin() + index, curve);
49  redraw();
50 }
51 
52 /**
53  * Adds all the curves found in the scene graph rooted at the given node.
54  * Returns the number of curves found.
55  */
58  int num_curves = r_add_curves(node);
59 
60  if (num_curves > 0) {
61  redraw();
62  }
63 
64  return num_curves;
65 }
66 
67 /**
68  * Removes the indicated ParametricCurve from the collection. Returns true if
69  * the curve was removed, false if it was not a member of the collection.
70  */
73  int curve_index = -1;
74  for (int i = 0; curve_index == -1 && i < (int)_curves.size(); i++) {
75  if (_curves[i] == curve) {
76  curve_index = i;
77  }
78  }
79 
80  if (curve_index == -1) {
81  // The indicated curve was not a member of the collection.
82  return false;
83  }
84 
85  remove_curve(curve_index);
86 
87  return true;
88 }
89 
90 
91 /**
92  * Removes the indicated ParametricCurve from the collection, by its index
93  * number.
94  */
96 remove_curve(size_t index) {
97  nassertv(index < _curves.size());
98  PT(ParametricCurve) curve = _curves[index];
99  prepare_remove_curve(curve);
100  _curves.erase(_curves.begin() + index);
101  redraw();
102 }
103 
104 
105 /**
106  * Replaces the indicated ParametricCurve from the collection, by its index
107  * number.
108  */
110 set_curve(size_t index, ParametricCurve *curve) {
111  nassertv(index < _curves.size());
112  prepare_remove_curve(_curves[index]);
113  prepare_add_curve(curve);
114  _curves[index] = curve;
115  redraw();
116 }
117 
118 /**
119  * Returns true if the indicated ParametricCurve appears in this collection,
120  * false otherwise.
121  */
123 has_curve(ParametricCurve *curve) const {
124  ParametricCurves::const_iterator ci;
125  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
126  if (curve == (*ci)) {
127  return true;
128  }
129  }
130  return false;
131 }
132 
133 /**
134  * Removes all ParametricCurves from the collection.
135  */
137 clear() {
138  ParametricCurves::iterator ci;
139  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
140  ParametricCurve *curve = (*ci);
141  prepare_remove_curve(curve);
142  }
143  _curves.clear();
144 
145  redraw();
146 }
147 
148 /**
149  * Removes all the timewarp curves from the collection.
150  */
153  PT(ParametricCurve) xyz_curve = nullptr;
154  PT(ParametricCurve) hpr_curve = nullptr;
155 
156  ParametricCurves::iterator ci;
157  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
158  ParametricCurve *curve = (*ci);
159 
160  switch (curve->get_curve_type()) {
161  case PCT_XYZ:
162  if (xyz_curve == nullptr) {
163  xyz_curve = curve;
164  } else {
165  prepare_remove_curve(curve);
166  }
167  break;
168 
169  case PCT_HPR:
170  if (hpr_curve == nullptr) {
171  hpr_curve = curve;
172  } else {
173  prepare_remove_curve(curve);
174  }
175  break;
176 
177  default:
178  prepare_remove_curve(curve);
179  }
180  }
181 
182  _curves.clear();
183  _curves.push_back(xyz_curve);
184 
185  if (hpr_curve != nullptr) {
186  _curves.push_back(hpr_curve);
187  }
188 
189  redraw();
190 }
191 
192 /**
193  * Returns the first XYZ curve in the collection, if any, or NULL if there are
194  * none.
195  */
196 ParametricCurve *ParametricCurveCollection::
197 get_xyz_curve() const {
198  ParametricCurves::const_iterator ci;
199  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
200  ParametricCurve *curve = (*ci);
201  if (curve->get_curve_type() == PCT_XYZ) {
202  return curve;
203  }
204  }
205  return nullptr;
206 }
207 
208 /**
209  * Returns the first HPR curve in the collection, if any, or NULL if there are
210  * none.
211  */
212 ParametricCurve *ParametricCurveCollection::
213 get_hpr_curve() const {
214  ParametricCurves::const_iterator ci;
215  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
216  ParametricCurve *curve = (*ci);
217  if (curve->get_curve_type() == PCT_HPR) {
218  return curve;
219  }
220  }
221  return nullptr;
222 }
223 
224 /**
225  * If there is an XYZ curve in the collection, returns it; otherwise, returns
226  * the first curve whose type is unspecified. Returns NULL if no curve meets
227  * the criteria.
228  */
229 ParametricCurve *ParametricCurveCollection::
230 get_default_curve() const {
231  ParametricCurve *xyz_curve = get_xyz_curve();
232  if (xyz_curve != nullptr) {
233  return xyz_curve;
234  }
235 
236  ParametricCurves::const_iterator ci;
237  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
238  ParametricCurve *curve = (*ci);
239  if (curve->get_curve_type() == PCT_NONE) {
240  return curve;
241  }
242  }
243  return nullptr;
244 }
245 
246 /**
247  * Returns the number of timewarp curves in the collection.
248  */
249 int ParametricCurveCollection::
250 get_num_timewarps() const {
251  int count = 0;
252 
253  ParametricCurves::const_iterator ci;
254  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
255  ParametricCurve *curve = (*ci);
256  if (curve->get_curve_type() == PCT_T) {
257  count++;
258  }
259  }
260 
261  return count;
262 }
263 
264 /**
265  * Returns the nth timewarp curve in the collection.
266  */
268 get_timewarp_curve(int n) const {
269  ParametricCurves::const_iterator ci;
270  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
271  ParametricCurve *curve = (*ci);
272  if (curve->get_curve_type() == PCT_T) {
273  if (n == 0) {
274  return curve;
275  }
276  n--;
277  }
278  }
279  nassert_raise("index out of range");
280  return nullptr;
281 }
282 
283 /**
284  * Discards all existing timewarp curves and recomputes a new timewarp curve
285  * that maps distance along the curve to parametric time, so that the distance
286  * between any two points in parametric time is proportional to the
287  * approximate distance of those same two points along the XYZ curve.
288  *
289  * segments_per_unit represents the number of segments to take per each unit
290  * of parametric time of the original XYZ curve.
291  *
292  * The new timewarp curve (and thus, the apparent range of the collection)
293  * will range from 0 to max_t.
294  */
296 make_even(PN_stdfloat max_t, PN_stdfloat segments_per_unit) {
297  ParametricCurve *xyz_curve = get_xyz_curve();
298  if (xyz_curve == nullptr) {
299  parametrics_cat.error()
300  << "No XYZ curve for make_even().\n";
301  return;
302  }
303 
304  clear_timewarps();
305 
306  // Now divvy up the XYZ curve into num_segments sections, each approximately
307  // the same length as all the others.
308  CurveFitter fitter;
309 
310  int num_segments = std::max(1, (int)cfloor(segments_per_unit * xyz_curve->get_max_t() + 0.5f));
311 
312  if (parametrics_cat.is_debug()) {
313  parametrics_cat.debug()
314  << "Calculating length of curve.\n";
315  }
316 
317  PN_stdfloat net_length = xyz_curve->calc_length();
318  PN_stdfloat segment_length = net_length / (PN_stdfloat)num_segments;
319 
320  if (parametrics_cat.is_debug()) {
321  parametrics_cat.debug()
322  << "Curve has total length " << net_length << "; dividing into "
323  << num_segments << " segments of " << segment_length << " units each.\n";
324  }
325 
326  PN_stdfloat last_t = 0.0f;
327  fitter.add_xyz(0.0f, LVecBase3(last_t, 0.0f, 0.0f));
328  PN_stdfloat val_inc= max_t/num_segments;
329  PN_stdfloat val=val_inc;
330 
331  for (int i = 0; i < num_segments; i++,val+=val_inc) {
332  PN_stdfloat next_t = xyz_curve->find_length(last_t, segment_length);
333  fitter.add_xyz(/*(PN_stdfloat)(i + 1)/num_segments * max_t,*/
334  val, LVecBase3(next_t, 0.0f, 0.0f));
335 
336  if (parametrics_cat.is_spam()) {
337  parametrics_cat.spam()
338  << "Point " << i << " is at " << next_t << "\n";
339  }
340 
341  last_t = next_t;
342  }
343 
344  if (parametrics_cat.is_debug()) {
345  parametrics_cat.debug()
346  << "Done computing segments.\n";
347  }
348 
349  fitter.compute_tangents(1);
350  PT(ParametricCurveCollection) fit = fitter.make_nurbs();
351  ParametricCurve *t_curve = fit->get_xyz_curve();
352  nassertv(t_curve != nullptr);
353  t_curve->set_curve_type(PCT_T);
354  add_curve(t_curve);
355 }
356 
357 /**
358  * Discards the existing HPR curve and generates a new one that looks in the
359  * direction of travel along the XYZ curve, based on the XYZ curve's tangent
360  * at each point.
361  */
363 face_forward(PN_stdfloat segments_per_unit) {
364  ParametricCurve *xyz_curve = get_xyz_curve();
365  if (xyz_curve == nullptr) {
366  parametrics_cat.error()
367  << "No XYZ curve for face_forward().\n";
368  return;
369  }
370 
371  // Eliminate all the old hpr curves, and also take note of the index number
372  // of the first XYZ curve.
373  int xyz_index = -1;
374  ParametricCurves::const_iterator ci;
375  ParametricCurves new_curves;
376 
377  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
378  ParametricCurve *curve = (*ci);
379  if (curve->get_curve_type() == PCT_HPR) {
380  prepare_remove_curve(curve);
381  } else {
382  if (curve->get_curve_type() == PCT_XYZ && xyz_index == -1) {
383  xyz_index = (ci - _curves.begin());
384  }
385  new_curves.push_back(curve);
386  }
387  }
388  _curves.swap(new_curves);
389 
390  // Now divvy up the XYZ curve into num_segments sections, of equal length in
391  // parametric time (based on the timewarp curves).
392  CurveFitter fitter;
393 
394  PN_stdfloat max_t = get_max_t();
395  int num_segments = (int)cfloor(segments_per_unit * max_t + 0.5);
396 
397  LVecBase3 hpr(0.0f, 0.0f, 0.0f);
398 
399  // We compute the first HPR point a little point into the beginning of the
400  // curve, instead of at 0.0f, because the tangent at 0.0f is likely to be
401  // zero.
402  determine_hpr(0.001, xyz_curve, hpr);
403  fitter.add_hpr(0.0f, hpr);
404 
405  for (int i = 0; i < num_segments; i++) {
406  PN_stdfloat t = (PN_stdfloat)(i + 1) / num_segments * max_t;
407  determine_hpr(t, xyz_curve, hpr);
408  fitter.add_hpr(t, hpr);
409  }
410 
411  fitter.wrap_hpr();
412  fitter.compute_tangents(1);
413  PT(ParametricCurveCollection) fit = fitter.make_nurbs();
414  ParametricCurve *hpr_curve = fit->get_hpr_curve();
415  nassertv(hpr_curve != nullptr);
416  add_curve(hpr_curve, xyz_index + 1);
417 }
418 
419 /**
420  * Adjusts the apparent length of the curve by applying a new timewarp that
421  * maps the range [0..max_t] to the range [0..get_max_t()]. After this call,
422  * the curve collection will contain one more timewarp curve, and get_max_t()
423  * will return the given max_t value.
424  */
426 reset_max_t(PN_stdfloat max_t) {
427  // Define a linear NURBS curve.
428  PT(NurbsCurve) nurbs = new NurbsCurve;
429  nurbs->set_curve_type(PCT_T);
430  nurbs->set_order(2);
431  nurbs->append_cv(LVecBase3(0.0f, 0.0f, 0.0f));
432  nurbs->append_cv(LVecBase3(get_max_t(), 0.0f, 0.0f));
433  nurbs->set_knot(0, 0.0f);
434  nurbs->set_knot(1, 0.0f);
435  nurbs->set_knot(2, max_t);
436  nurbs->set_knot(3, max_t);
437  nurbs->recompute();
438  add_curve(nurbs);
439 }
440 
441 /**
442  * Computes the position and rotation represented by the first XYZ and HPR
443  * curves in the collection at the given point t, after t has been modified by
444  * all the timewarp curves in the collection applied in sequence, from back to
445  * front.
446  *
447  * Returns true if the point is valid (i.e. t is within the bounds indicated
448  * by all the timewarp curves and within the bounds of the curves themselves),
449  * or false otherwise.
450  */
452 evaluate(PN_stdfloat t, LVecBase3 &xyz, LVecBase3 &hpr) const {
453  // First, apply all the timewarps in sequence, from back to front. Also
454  // take note of the XYZ and HPR curves.
455  ParametricCurve *xyz_curve = nullptr;
456  ParametricCurve *hpr_curve = nullptr;
457  ParametricCurve *default_curve = nullptr;
458 
459  PN_stdfloat t0 = t;
460  LVecBase3 point;
461 
462  ParametricCurves::const_reverse_iterator ci;
463  for (ci = _curves.rbegin(); ci != _curves.rend(); ++ci) {
464  ParametricCurve *curve = (*ci);
465 
466  switch (curve->get_curve_type()) {
467  case PCT_XYZ:
468  xyz_curve = curve;
469  break;
470 
471  case PCT_HPR:
472  hpr_curve = curve;
473  break;
474 
475  case PCT_NONE:
476  default_curve = curve;
477  break;
478 
479  case PCT_T:
480  if (!curve->get_point(t0, point)) {
481  return false;
482  }
483  t0 = point[0];
484  }
485  }
486 
487  if (xyz_curve == nullptr) {
488  xyz_curve = default_curve;
489  }
490 
491  // Now compute the position and orientation.
492  if (xyz_curve != nullptr) {
493  if (!xyz_curve->get_point(t0, xyz)) {
494  return false;
495  }
496  }
497 
498  if (hpr_curve != nullptr) {
499  if (!hpr_curve->get_point(t0, hpr)) {
500  return false;
501  }
502  }
503 
504  return true;
505 }
506 
507 /**
508  * Computes the transform matrix representing translation to the position
509  * indicated by the first XYZ curve in the collection and the rotation
510  * indicated by the first HPR curve in the collection, after t has been
511  * modified by all the timewarp curves in the collection applied in sequence,
512  * from back to front.
513  *
514  * Returns true if the point is valid (i.e. t is within the bounds indicated
515  * by all the timewarp curves and within the bounds of the curves themselves),
516  * or false otherwise.
517  */
519 evaluate(PN_stdfloat t, LMatrix4 &result, CoordinateSystem cs) const {
520  LVecBase3 xyz(0.0f, 0.0f, 0.0f);
521  LVecBase3 hpr(0.0f, 0.0f, 0.0f);
522 
523  if (!evaluate(t, xyz, hpr)) {
524  return false;
525  }
526 
527  compose_matrix(result,
528  LVecBase3(1.0f, 1.0f, 1.0f),
529  LVecBase3(0.0f, 0.0f, 0.0f),
530  hpr, xyz, cs);
531  return true;
532 }
533 
534 /**
535  * Determines the value of t that should be passed to the XYZ and HPR curves,
536  * after applying the given value of t to all the timewarps. Return -1.0f if
537  * the value of t exceeds one of the timewarps' ranges.
538  */
539 PN_stdfloat ParametricCurveCollection::
540 evaluate_t(PN_stdfloat t) const {
541  PN_stdfloat t0 = t;
542  LVecBase3 point;
543 
544  ParametricCurves::const_iterator ci;
545  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
546  ParametricCurve *curve = (*ci);
547 
548  if (curve->get_curve_type() == PCT_T) {
549  if (!curve->get_point(t0, point)) {
550  return -1.0f;
551  }
552  t0 = point[0];
553  }
554  }
555 
556  return t0;
557 }
558 
559 /**
560  * Adjust the XYZ curve at the indicated time to the new value. The curve
561  * shape will change correspondingly. Returns true if successful, false if
562  * unable to make the adjustment for some reason.
563  */
565 adjust_xyz(PN_stdfloat t, const LVecBase3 &xyz) {
566  ParametricCurve *xyz_curve = get_xyz_curve();
567  if (xyz_curve == nullptr) {
568  return false;
569  }
570 
571  PN_stdfloat t0 = evaluate_t(t);
572  if (t0 >= 0.0f && t < xyz_curve->get_max_t()) {
573  return xyz_curve->adjust_point(t, xyz[0], xyz[1], xyz[2]);
574  }
575  return false;
576 }
577 
578 /**
579  * Adjust the HPR curve at the indicated time to the new value. The curve
580  * shape will change correspondingly. Returns true if successful, false if
581  * unable to make the adjustment for some reason.
582  */
584 adjust_hpr(PN_stdfloat t, const LVecBase3 &hpr) {
585  ParametricCurve *hpr_curve = get_hpr_curve();
586  if (hpr_curve == nullptr) {
587  return false;
588  }
589 
590  PN_stdfloat t0 = evaluate_t(t);
591  if (t0 >= 0.0f && t < hpr_curve->get_max_t()) {
592  return hpr_curve->adjust_point(t, hpr[0], hpr[1], hpr[2]);
593  }
594  return false;
595 }
596 
597 /**
598  * Ensures all the curves are freshly computed and up-to-date. Returns true
599  * if everything is valid, false if at least one curve is incorrect.
600  */
603  bool all_ok = true;
604 
605  ParametricCurves::iterator ci;
606  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
607  ParametricCurve *curve = (*ci);
608  if (!curve->recompute()) {
609  all_ok = false;
610  }
611  }
612 
613  return all_ok;
614 }
615 
616 /**
617  * Regenerates this curve as one long curve: the first curve connected end-to-
618  * end with the second one. Either a or b may be the same as 'this'. This
619  * will lose any timewarps on the input curves.
620  *
621  * Returns true if successful, false on failure.
622  */
625  const ParametricCurveCollection *b) {
626  PT(ParametricCurve) a_xyz = a->get_xyz_curve();
627  PT(ParametricCurve) b_xyz = b->get_xyz_curve();
628 
629  PT(ParametricCurve) a_hpr = a->get_hpr_curve();
630  PT(ParametricCurve) b_hpr = b->get_hpr_curve();
631 
632  clear();
633 
634  if (a_xyz != nullptr && b_xyz != nullptr) {
635  PT(NurbsCurve) new_xyz = new NurbsCurve;
636  if (!new_xyz->stitch(a_xyz, b_xyz)) {
637  return false;
638  }
639  new_xyz->set_curve_type(PCT_XYZ);
640  add_curve(new_xyz);
641  }
642 
643  if (a_hpr != nullptr && b_hpr != nullptr) {
644  PT(NurbsCurve) new_hpr = new NurbsCurve;
645  if (!new_hpr->stitch(a_hpr, b_hpr)) {
646  return false;
647  }
648  new_hpr->set_curve_type(PCT_HPR);
649  add_curve(new_hpr);
650  }
651 
652  return true;
653 }
654 
655 /**
656  * Writes a brief one-line description of the ParametricCurveCollection to the
657  * indicated output stream.
658  */
660 output(std::ostream &out) const {
661  if (get_num_curves() == 1) {
662  out << "1 ParametricCurve";
663  } else {
664  out << get_num_curves() << " ParametricCurves";
665  }
666 }
667 
668 /**
669  * Writes a complete multi-line description of the ParametricCurveCollection
670  * to the indicated output stream.
671  */
673 write(std::ostream &out, int indent_level) const {
674  ParametricCurves::const_iterator ci;
675  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
676  ParametricCurve *curve = (*ci);
677  indent(out, indent_level) << *curve << "\n";
678  }
679 }
680 
681 /**
682  * Writes an egg description of all the nurbs curves in the collection to the
683  * specified output file. Returns true if the file is successfully written.
684  */
686 write_egg(Filename filename, CoordinateSystem cs) {
687  pofstream out;
688  filename.set_text();
689 
690  if (!filename.open_write(out)) {
691  parametrics_cat.error()
692  << "Unable to write to " << filename << "\n";
693  return false;
694  }
695  return write_egg(out, filename, cs);
696 }
697 
698 /**
699  * Writes an egg description of all the nurbs curves in the collection to the
700  * specified output stream. Returns true if the file is successfully written.
701  */
703 write_egg(std::ostream &out, const Filename &filename, CoordinateSystem cs) {
704  if (cs == CS_default) {
705  cs = get_default_coordinate_system();
706  }
707 
708  if (cs != CS_invalid) {
709  out << "<CoordinateSystem> { ";
710  switch (cs) {
711  case CS_zup_right:
712  out << "Z-Up";
713  break;
714 
715  case CS_yup_right:
716  out << "Y-Up";
717  break;
718 
719  case CS_zup_left:
720  out << "Z-Up-Left";
721  break;
722 
723  case CS_yup_left:
724  out << "Y-Up-Left";
725  break;
726 
727  default:
728  break;
729  }
730  out << " }\n\n";
731  }
732 
733  int xyz_count = 0;
734  int hpr_count = 0;
735  int t_count = 0;
736 
737  ParametricCurves::iterator ci;
738  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
739  ParametricCurve *curve = (*ci);
740 
741  if (!curve->has_name()) {
742  // If we don't have a name, come up with one.
743  std::string name = filename.get_basename_wo_extension();
744 
745  switch (curve->get_curve_type()) {
746  case PCT_XYZ:
747  name += "_xyz";
748  if (xyz_count > 0) {
749  name += format_string(xyz_count);
750  }
751  xyz_count++;
752  break;
753 
754  case PCT_HPR:
755  name += "_hpr";
756  if (hpr_count > 0) {
757  name += format_string(hpr_count);
758  }
759  hpr_count++;
760  break;
761 
762  case PCT_T:
763  name += "_t";
764  if (t_count > 0) {
765  name += format_string(t_count);
766  }
767  t_count++;
768  break;
769  }
770 
771  curve->set_name(name);
772  }
773 
774  if (!curve->write_egg(out, filename, CS_invalid)) {
775  return false;
776  }
777  }
778 
779  return true;
780 }
781 
782 /**
783  * The recursive implementation of add_curves().
784  */
787  int num_curves = 0;
788 
789  if (node->is_of_type(ParametricCurve::get_class_type())) {
790  ParametricCurve *curve = DCAST(ParametricCurve, node);
791  prepare_add_curve(curve);
792  _curves.push_back(curve);
793  num_curves++;
794  }
795 
796  int num_children = node->get_num_children();
797  for (int i = 0; i < num_children; i++) {
798  PandaNode *child = node->get_child(i);
799  num_curves += r_add_curves(child);
800  }
801 
802  return num_curves;
803 }
804 
805 /**
806  * Registers a Drawer with this curve collection that will automatically be
807  * updated whenever the collection is modified, so that the visible
808  * representation of the curve is kept up to date. This is called
809  * automatically by the ParametricCurveDrawer.
810  *
811  * Any number of Drawers may be registered with a particular curve collection.
812  */
814 register_drawer(ParametricCurveDrawer *drawer) {
815  _drawers.push_back(drawer);
816 
817  ParametricCurves::iterator ci;
818  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
819  ParametricCurve *curve = (*ci);
820  curve->register_drawer(drawer);
821  }
822 }
823 
824 /**
825  * Removes a previously registered drawer from the list of automatically-
826  * refreshed drawers. This is called automatically by the
827  * ParametricCurveDrawer.
828  */
830 unregister_drawer(ParametricCurveDrawer *drawer) {
831  _drawers.remove(drawer);
832 
833  ParametricCurves::iterator ci;
834  for (ci = _curves.begin(); ci != _curves.end(); ++ci) {
835  ParametricCurve *curve = (*ci);
836  curve->unregister_drawer(drawer);
837  }
838 }
839 
840 /**
841  * Computes the orientation at the given point in time, based on the tangent
842  * of the XYZ curve. Returns true if the orientation can be determined, or
843  * false if it cannot (in which case hpr is left unchanged).
844  */
845 bool ParametricCurveCollection::
846 determine_hpr(PN_stdfloat t, ParametricCurve *xyz_curve, LVecBase3 &hpr) const {
847  PN_stdfloat t0 = evaluate_t(t);
848 
849  LVector3 tangent;
850  if (!xyz_curve->get_tangent(t0, tangent)) {
851  return false;
852  }
853 
854  if (tangent.length_squared() == 0.0f) {
855  return false;
856  }
857 
858  LMatrix3 mat;
859  look_at(mat, tangent);
860 
861  LVecBase3 scale, shear;
862  return decompose_matrix(mat, scale, shear, hpr);
863 }
864 
865 /**
866  * Registers the curve with the list of drawers that share this collection, in
867  * preparation for adding it to the _curves list.
868  */
869 void ParametricCurveCollection::
870 prepare_add_curve(ParametricCurve *curve) {
871  DrawerList::iterator di;
872  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
873  ParametricCurveDrawer *drawer = (*di);
874  curve->register_drawer(drawer);
875  }
876 }
877 
878 /**
879  * Unregisters the curve with the list of drawers that share this collection,
880  * in preparation for removing it from the _curves list.
881  */
882 void ParametricCurveCollection::
883 prepare_remove_curve(ParametricCurve *curve) {
884  DrawerList::iterator di;
885  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
886  ParametricCurveDrawer *drawer = (*di);
887  curve->unregister_drawer(drawer);
888  }
889 }
890 
891 /**
892  * Calls redraw() on all drawers that share this collection.
893  */
894 void ParametricCurveCollection::
895 redraw() {
896  /*
897  DrawerList::iterator di;
898  for (di = _drawers.begin(); di != _drawers.end(); ++di) {
899  ParametricCurveDrawer *drawer = (*di);
900  drawer->redraw();
901  }
902  */
903 }
bool adjust_hpr(PN_stdfloat t, PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Adjust the HPR curve at the indicated time to the new value.
get_max_t
Returns the maximum T value associated with the *last* curve in the collection.
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
void insert_curve(size_t index, ParametricCurve *curve)
Adds a new ParametricCurve to the collection at the indicated index.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
void register_drawer(ParametricCurveDrawer *drawer)
Registers a Drawer with this curve collection that will automatically be updated whenever the collect...
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,...
int r_add_curves(PandaNode *node)
The recursive implementation of add_curves().
PN_stdfloat evaluate_t(PN_stdfloat t) const
Determines the value of t that should be passed to the XYZ and HPR curves, after applying the given v...
A virtual base class for parametric curves.
void face_forward(PN_stdfloat segments_per_unit)
Discards the existing HPR curve and generates a new one that looks in the direction of travel along t...
virtual bool recompute()
Recalculates the curve, if necessary.
void register_drawer(ParametricCurveDrawer *drawer)
Registers a Drawer with this curve that will automatically be updated whenever the curve is modified,...
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
bool has_curve(ParametricCurve *curve) const
Returns true if the indicated ParametricCurve appears in this collection, false otherwise.
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...
get_num_curves
Returns the number of ParametricCurves in the collection.
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.
remove_curve
Removes the indicated ParametricCurve from the collection.
bool evaluate(PN_stdfloat t, LVecBase3 &xyz, LVecBase3 &hpr) const
Computes the position and rotation represented by the first XYZ and HPR curves in the collection at t...
void unregister_drawer(ParametricCurveDrawer *drawer)
Removes a previously registered drawer from the list of automatically- refreshed drawers.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear_timewarps()
Removes all the timewarp curves from the collection.
set_curve
Replaces the indicated ParametricCurve from the collection, by its index number.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void compute_tangents(PN_stdfloat scale)
Once a set of points has been built, and prior to calling MakeHermite() or MakeNurbs(),...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a set of zero or more ParametricCurves, which may or may not be related.
get_num_children
Returns the number of child nodes this node has.
Definition: pandaNode.h:124
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_timewarp_curve
Returns the nth timewarp curve in the collection.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
int add_curves(PandaNode *node)
Adds all the curves found in the scene graph rooted at the given node.
get_hpr_curve
Returns the first HPR curve in the collection, if any, or NULL if there are none.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
void wrap_hpr()
Resets each HPR data point so that the maximum delta between any two consecutive points is 180 degree...
A Nonuniform Rational B-Spline.
Definition: nurbsCurve.h:41
void clear()
Removes all ParametricCurves from the collection.
bool adjust_xyz(PN_stdfloat t, PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Adjust the XYZ curve at the indicated time to the new value.
virtual PN_stdfloat get_max_t() const
Returns the upper bound of t for the entire curve.
bool recompute()
Ensures all the curves are freshly computed and up-to-date.
void add_hpr(PN_stdfloat t, const LVecBase3 &hpr)
Adds a single sample hpr.
Definition: curveFitter.cxx:67
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_xyz(PN_stdfloat t, const LVecBase3 &xyz)
Adds a single sample xyz.
Definition: curveFitter.cxx:55
bool write_egg(Filename filename, CoordinateSystem cs=CS_default)
Writes an egg description of all the nurbs curves in the collection to the specified output file.
void set_curve_type(int type)
Sets the flag indicating the use to which the curve is intended to be put.
void make_even(PN_stdfloat max_t, PN_stdfloat segments_per_unit)
Discards all existing timewarp curves and recomputes a new timewarp curve that maps distance along th...
get_xyz_curve
Returns the first XYZ curve in the collection, if any, or NULL if there are none.
void add_curve(ParametricCurve *curve)
Adds a new ParametricCurve to the collection.
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.
void write(std::ostream &out, int indent_level=0) const
Writes a complete multi-line description of the ParametricCurveCollection to the indicated output str...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:44
get_child
Returns the nth child node of this node.
Definition: pandaNode.h:124
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void output(std::ostream &out) const
Writes a brief one-line description of the ParametricCurveCollection to the indicated output stream.
bool stitch(const ParametricCurveCollection *a, const ParametricCurveCollection *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.
int get_curve_type() const
Returns the flag indicating the use to which the curve is intended to be put.
void reset_max_t(PN_stdfloat max_t)
Adjusts the apparent length of the curve by applying a new timewarp that maps the range [0....