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