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  */
35 add_curve(ParametricCurve *curve) {
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  */
57 add_curves(PandaNode *node) {
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  */
152 clear_timewarps() {
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  */
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  */
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  */
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  */
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  */
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  */
602 recompute() {
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  */
786 r_add_curves(PandaNode *node) {
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 }
void add_xyz(PN_stdfloat t, const LVecBase3 &xyz)
Adds a single sample xyz.
Definition: curveFitter.cxx:55
void wrap_hpr()
Resets each HPR data point so that the maximum delta between any two consecutive points is 180 degree...
void compute_tangents(PN_stdfloat scale)
Once a set of points has been built, and prior to calling MakeHermite() or MakeNurbs(),...
void add_hpr(PN_stdfloat t, const LVecBase3 &hpr)
Adds a single sample hpr.
Definition: curveFitter.cxx:67
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:44
A Nonuniform Rational B-Spline.
Definition: nurbsCurve.h:41
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
get_child
Returns the nth child node of this node.
Definition: pandaNode.h:124
get_num_children
Returns the number of child nodes this node has.
Definition: pandaNode.h:124
This is a set of zero or more ParametricCurves, which may or may not be related.
bool recompute()
Ensures all the curves are freshly computed and up-to-date.
void clear_timewarps()
Removes all the timewarp curves from the collection.
get_default_curve
If there is an XYZ curve in the collection, returns it; otherwise, returns the first curve whose type...
int add_curves(PandaNode *node)
Adds all the curves found in the scene graph rooted at the given node.
void insert_curve(size_t index, ParametricCurve *curve)
Adds a new ParametricCurve to the collection at the indicated index.
void unregister_drawer(ParametricCurveDrawer *drawer)
Removes a previously registered drawer from the list of automatically- refreshed drawers.
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.
bool has_curve(ParametricCurve *curve) const
Returns true if the indicated ParametricCurve appears in this collection, false otherwise.
void write(std::ostream &out, int indent_level=0) const
Writes a complete multi-line description of the ParametricCurveCollection to the indicated output str...
void add_curve(ParametricCurve *curve)
Adds a new ParametricCurve to the collection.
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.
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...
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.
get_xyz_curve
Returns the first XYZ curve in the collection, if any, or NULL if there are none.
get_timewarp_curve
Returns the nth timewarp curve in the collection.
get_num_curves
Returns the number of ParametricCurves in the collection.
int r_add_curves(PandaNode *node)
The recursive implementation of add_curves().
void register_drawer(ParametricCurveDrawer *drawer)
Registers a Drawer with this curve collection that will automatically be updated whenever the collect...
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_num_timewarps
Returns the number of timewarp curves in the collection.
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...
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.
remove_curve
Removes the indicated ParametricCurve from the collection.
void clear()
Removes all ParametricCurves from the collection.
set_curve
Replaces the indicated ParametricCurve from the collection, by its index number.
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 reset_max_t(PN_stdfloat max_t)
Adjusts the apparent length of the curve by applying a new timewarp that maps the range [0....
void output(std::ostream &out) const
Writes a brief one-line description of the ParametricCurveCollection to the indicated output stream.
get_hpr_curve
Returns the first HPR curve in the collection, if any, or NULL if there are none.
get_max_t
Returns the maximum T value associated with the *last* curve in the collection.
A virtual base class for parametric curves.
PN_stdfloat calc_length() const
Approximates the length of the entire curve to within a few decimal places.
int get_curve_type() const
Returns the flag indicating the use to which the curve is intended to be put.
virtual bool recompute()
Recalculates the curve, if necessary.
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 PN_stdfloat get_max_t() const
Returns the upper bound of t for the entire curve.
void set_curve_type(int type)
Sets the flag indicating the use to which the curve is intended to be put.
void register_drawer(ParametricCurveDrawer *drawer)
Registers a Drawer with this curve that will automatically be updated whenever the curve is modified,...
bool write_egg(Filename filename, CoordinateSystem cs=CS_default)
Writes an egg description of the nurbs curve to the specified output file.
void unregister_drawer(ParametricCurveDrawer *drawer)
Removes a previously registered drawer from the list of automatically- refreshed drawers.
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...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.