Panda3D
piecewiseCurve.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 piecewiseCurve.cxx
10  * @author drose
11  * @date 2001-03-04
12  */
13 
14 #include "piecewiseCurve.h"
15 #include "cubicCurveseg.h"
16 #include "config_parametrics.h"
17 
18 #include "datagram.h"
19 #include "datagramIterator.h"
20 #include "bamWriter.h"
21 #include "bamReader.h"
22 
23 using std::cerr;
24 
25 TypeHandle PiecewiseCurve::_type_handle;
26 
27 /**
28  *
29  */
30 PiecewiseCurve::
31 PiecewiseCurve() {
32  _last_ti = 0;
33 }
34 
35 /**
36  *
37  */
38 PiecewiseCurve::
39 ~PiecewiseCurve() {
41 }
42 
43 
44 /**
45  * Returns true if the curve is defined. In the case of a PiecewiseCurve,
46  * this means we have at least one segment.
47  */
49 is_valid() const {
50  return !_segs.empty();
51 }
52 
53 /**
54  * Returns the upper bound of t for the entire curve. The curve is defined in
55  * the range 0.0f <= t <= get_max_t().
56  */
57 PN_stdfloat PiecewiseCurve::
58 get_max_t() const {
59  return _segs.empty() ? 0.0f : _segs.back()._tend;
60 }
61 
62 
63 /**
64  * Returns the point of the curve at a given parametric point t. Returns true
65  * if t is in the valid range 0.0f <= t <= get_max_t(); if t is outside this
66  * range, sets point to the value of the curve at the beginning or end
67  * (whichever is nearer) and returns false.
68  */
70 get_point(PN_stdfloat t, LVecBase3 &point) const {
71  const ParametricCurve *curve;
72  bool result = find_curve(curve, t);
73  if (curve == nullptr){
74  return false;
75  }
76  // We use | instead of || so we won't short-circuit this calculation.
77  return result | curve->get_point(t, point);
78 }
79 
80 
81 /**
82  * Returns the tangent of the curve at a given parametric point t.
83  */
85 get_tangent(PN_stdfloat t, LVecBase3 &tangent) const {
86  const ParametricCurve *curve;
87  bool result = find_curve(curve, t);
88 
89  // We use | instead of || so we won't short-circuit this calculation.
90  return result | curve->get_tangent(t, tangent);
91 }
92 
93 
94 /**
95  * Returns the tangent of the first derivative of the curve at the point t.
96  */
98 get_2ndtangent(PN_stdfloat t, LVecBase3 &tangent2) const {
99  const ParametricCurve *curve;
100  bool result = find_curve(curve, t);
101 
102  // We use | instead of || so we won't short-circuit this calculation.
103  return result | curve->get_2ndtangent(t, tangent2);
104 }
105 
106 /**
107  * Recomputes the curve such that it passes through the point (px, py, pz) at
108  * time t, but keeps the same tangent value at that point.
109  */
110 bool PiecewiseCurve::
111 adjust_point(PN_stdfloat t,
112  PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz) {
113  if (parametrics_cat.is_debug()) {
114  parametrics_cat.debug()
115  << "Adjusting point at " << t << " to " << px << " " << py << " "
116  << pz << "\n";
117  }
118 
119  const ParametricCurve *curve;
120  bool result = find_curve(curve, t);
121 
122  if (!result) {
123  cerr << "No curve segment at t = " << t << "\n";
124  return false;
125  }
126 
127  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4(),
128  RT_POINT, t, LVecBase4(px, py, pz, 1.0f),
129  RT_TANGENT | RT_KEEP_ORIG, t, LVecBase4(),
130  RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4());
131  return true;
132 }
133 
134 /**
135  * Recomputes the curve such that it has the tangent (tx, ty, tz) at time t,
136  * but keeps the same position at the point.
137  */
138 bool PiecewiseCurve::
139 adjust_tangent(PN_stdfloat t,
140  PN_stdfloat tx, PN_stdfloat ty, PN_stdfloat tz) {
141  const ParametricCurve *curve;
142  bool result = find_curve(curve, t);
143 
144  if (!result) {
145  cerr << "No curve segment at t = " << t << "\n";
146  return false;
147  }
148 
149  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4(),
150  RT_POINT | RT_KEEP_ORIG, t, LVecBase4(),
151  RT_TANGENT, t, LVecBase4(tx, ty, tz, 0.0f),
152  RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4());
153  return true;
154 }
155 
156 /**
157  * Recomputes the curve such that it passes through the point (px, py, pz)
158  * with the tangent (tx, ty, tz).
159  */
160 bool PiecewiseCurve::
161 adjust_pt(PN_stdfloat t,
162  PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz,
163  PN_stdfloat tx, PN_stdfloat ty, PN_stdfloat tz) {
164  const ParametricCurve *curve;
165  bool result = find_curve(curve, t);
166 
167  if (!result) {
168  cerr << "No curve segment at t = " << t << "\n";
169  return false;
170  }
171 
172  rebuild_curveseg(RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4(),
173  RT_POINT, t, LVecBase4(px, py, pz, 1.0f),
174  RT_TANGENT, t, LVecBase4(tx, ty, tz, 0.0f),
175  RT_CV | RT_KEEP_ORIG, 0.0f, LVecBase4());
176  return true;
177 }
178 
179 
180 /**
181  * Simultaneously returns the point and tangent of the curve at a given
182  * parametric point t.
183  */
184 bool PiecewiseCurve::
185 get_pt(PN_stdfloat t, LVecBase3 &point, LVecBase3 &tangent) const {
186  const ParametricCurve *curve;
187  bool result = find_curve(curve, t);
188 
189  // We use | instead of || so we won't short-circuit this calculation.
190  return result | curve->get_pt(t, point, tangent);
191 }
192 
193 
194 /**
195  * Returns the number of curve segments that make up the Piecewise curve.
196  */
198 get_num_segs() const {
199  return _segs.size();
200 }
201 
202 /**
203  * Returns the curve segment corresponding to the given index.
204  */
206 get_curveseg(int ti) {
207  assert(ti >= 0 && ti < (int)_segs.size());
208  return _segs[ti]._curve;
209 }
210 
211 
212 /**
213  * Inserts a new curve segment at the indicated index. The curve segment must
214  * have been allocated via new; it will be freed using delete when it is
215  * removed or the PiecewiseCurve destructs.
216  *
217  * If the curve segment is not inserted at the end, its tlength is subtracted
218  * from that of the following segment, so that the overall length of the curve
219  * is not changed.
220  */
221 bool PiecewiseCurve::
222 insert_curveseg(int ti, ParametricCurve *seg, PN_stdfloat tlength) {
223  if (ti < 0 || ti > (int)_segs.size()) {
224  return false;
225  }
226 
227  if (ti == (int)_segs.size()) {
228  _segs.push_back(Curveseg(seg, get_max_t() + tlength));
229 
230  } else if (ti==0) {
231  _segs.insert(_segs.begin(),
232  Curveseg(seg, tlength));
233 
234  } else {
235  _segs.insert(_segs.begin() + ti,
236  Curveseg(seg, _segs[ti-1]._tend + tlength));
237  }
238 
239  return true;
240 }
241 
242 
243 /**
244  * Removes the given curve segment from the curve and frees it. Returns true
245  * if the segment was defined, false otherwise.
246  */
247 bool PiecewiseCurve::
249  if (ti < 0 || ti >= (int)_segs.size()) {
250  return false;
251  }
252 
253  PN_stdfloat tlength = get_tlength(ti);
254  _segs.erase(_segs.begin() + ti);
255 
256  // Now update the _tend figures for everything after the one we removed.
257  while (ti < (int)_segs.size()) {
258  _segs[ti]._tend -= tlength;
259  ti++;
260  }
261 
262  _last_ti = 0;
263  return true;
264 }
265 
266 /**
267  * Removes all curve segments from the curve.
268  */
269 void PiecewiseCurve::
271  _segs.erase(_segs.begin(), _segs.end());
272  _last_ti = 0;
273 }
274 
275 /**
276  * Returns the parametric length of the given segment of the curve.
277  */
278 PN_stdfloat PiecewiseCurve::
279 get_tlength(int ti) const {
280  assert(ti >= 0 && ti < (int)_segs.size());
281  return (ti==0) ? _segs[ti]._tend : _segs[ti]._tend - _segs[ti-1]._tend;
282 }
283 
284 /**
285  * Returns the parametric start of the given segment of the curve.
286  */
287 PN_stdfloat PiecewiseCurve::
288 get_tstart(int ti) const {
289  assert(ti >= 0 && ti <= (int)_segs.size());
290  return (ti==0) ? 0.0f : _segs[ti-1]._tend;
291 }
292 
293 /**
294  * Returns the parametric end of the given segment of the curve.
295  */
296 PN_stdfloat PiecewiseCurve::
297 get_tend(int ti) const {
298  assert(ti >= 0 && ti < (int)_segs.size());
299  return _segs[ti]._tend;
300 }
301 
302 
303 /**
304  * Sets the parametric length of the given segment of the curve. The length
305  * of the following segment is lengthened by the corresponding amount to keep
306  * the overall length of the curve the same.
307  */
308 bool PiecewiseCurve::
309 set_tlength(int ti, PN_stdfloat tlength) {
310  if (ti < 0 || ti >= (int)_segs.size()) {
311  return false;
312  }
313 
314  _segs[ti]._tend += tlength - get_tlength(ti);
315  return true;
316 }
317 
318 
319 
320 /**
321  * Defines the curve as a general NURBS curve. The order is the degree plus
322  * one and must be 1, 2, 3, or 4; cvs is an array of num_cvs points each with
323  * a homogeneous coordinate; knots is an array of num_cvs+order knot values.
324  *
325  * This creates the individual curve segments and sets up the basis matrices,
326  * but does not store the CV's or knot values so the curve shape is not later
327  * modifiable.
328  */
329 void PiecewiseCurve::
330 make_nurbs(int order, int num_cvs,
331  const PN_stdfloat knots[], const LVecBase4 cvs[]) {
333 
334  for (int i=0; i<num_cvs - order + 1; i++) {
335  if (knots[i+order] > knots[i+order-1]) {
336  int ti = get_num_segs();
337  bool result =
338  insert_curveseg(ti, new CubicCurveseg(order, knots+i, cvs+i),
339  knots[i+order] - knots[i+order-1]);
340  assert(result);
341  }
342  }
343 }
344 
345 
346 /**
347  * Fills up the indicated vector with a list of BezierSeg structs that
348  * describe the curve. This assumes the curve is a PiecewiseCurve of
349  * CubicCurvesegs. Returns true if successful, false otherwise.
350  */
351 bool PiecewiseCurve::
352 get_bezier_segs(BezierSegs &bz_segs) const {
353  bz_segs.erase(bz_segs.begin(), bz_segs.end());
354  int i;
355  BezierSeg seg;
356  for (i = 0; i < (int)_segs.size(); i++) {
357  if (!_segs[i]._curve->get_bezier_seg(seg)) {
358  return false;
359  }
360  seg._t = _segs[i]._tend;
361  bz_segs.push_back(seg);
362  }
363 
364  return true;
365 }
366 
367 /**
368  * Rebuilds the current curve segment (as selected by the most recent call to
369  * find_curve()) according to the specified properties (see
370  * CubicCurveseg::compute_seg). Returns true if possible, false if something
371  * goes horribly wrong.
372  */
373 bool PiecewiseCurve::
374 rebuild_curveseg(int, PN_stdfloat, const LVecBase4 &,
375  int, PN_stdfloat, const LVecBase4 &,
376  int, PN_stdfloat, const LVecBase4 &,
377  int, PN_stdfloat, const LVecBase4 &) {
378  cerr << "rebuild_curveseg not implemented for this curve type.\n";
379  return false;
380 }
381 
382 /**
383  * Finds the curve corresponding to the given value of t. If t is inside the
384  * curve's defined range, sets curve to the appropriate segment, translates t
385  * to [0,1] to index into the segment's coordinate system, and returns true.
386  * If t is outside the curve's defined range, sets curve to the nearest
387  * segment and t to the nearest point on this segment, and returns false.
388  */
389 bool PiecewiseCurve::
390 find_curve(const ParametricCurve *&curve, PN_stdfloat &t) const {
391  // Check the index computed by the last call to find_curve(). If it's still
392  // a reasonable starting value, start searching from there. This way, we
393  // take advantage of locality of reference: the search is trivial it is the
394  // same segment as last time, or the next segment after the last one.
395  if (_last_ti>0 && _segs[_last_ti-1]._tend>=t) {
396  // However, if the new t value precedes that of last time, we'll have to
397  // start over.
398 
399  // We do some messy casting so we can get away with assigning a value to a
400  // member within a const function. This assignment doesn't really count
401  // as a const violation since we're just updating a cached value, not
402  // changing any real data of the class.
403  ((PiecewiseCurve *)this)->_last_ti = 0;
404  }
405 
406  int ti;
407  for (ti = _last_ti; ti < (int)_segs.size(); ti++) {
408  if (_segs[ti]._tend+0.00001f > t) {
409  break;
410  }
411  }
412 
413  if (ti < (int)_segs.size()) {
414  // Adjust t to the range [0,1).
415  if (ti > 0) {
416  t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend);
417  } else {
418  t /= _segs[0]._tend;
419  }
420  }
421 
422  if (t < 0) {
423  // Oops.
424  curve = _segs[0]._curve;
425  t = 0.0f;
426  return false;
427  }
428 
429  if (ti >= (int)_segs.size() || !_segs[ti]._curve->is_valid()) {
430  assert(ti <= (int)_segs.size());
431 
432  // If we're out of bounds, or the curve is undefined, we're probably
433  // screwed. There's one exception: if we were right on a border between
434  // curves, try the curve before.
435 
436  if (ti > 0 && t < _segs[ti-1]._tend+0.0001f) {
437  ti--;
438  t = 1.0f;
439  }
440 
441  if (ti >= (int)_segs.size()) {
442  if (_segs.empty()) {
443  curve = nullptr;
444  t = 0.0f;
445  return false;
446  } else {
447  curve = _segs.back()._curve;
448  t = 1.0f;
449  return false;
450  }
451  } else if (!_segs[ti]._curve->is_valid()) {
452  curve = _segs[ti]._curve;
453  return false;
454  }
455  }
456 
457  // Again, some messy casting so we can get away with updating the cached
458  // index value for next time.
459  ((PiecewiseCurve *)this)->_last_ti = ti;
460 
461  // Now scale t back into the curve's own valid range.
462  t *= _segs[ti]._curve->get_max_t();
463  curve = _segs[ti]._curve;
464  return true;
465 }
466 
467 
468 /**
469  * Returns a number in the range [0,1], representing the conversion of t into
470  * the current segment's coordinate system (the segment last returned by
471  * find_curve). This operation is already performed automatically on the t
472  * passed into find_seg; this function is useful only to adjust a different
473  * value into the same range.
474  *
475  * It is an error to call this function if find_curve() has not yet been
476  * called, or if find_curve() returned false from its previous call.
477  */
478 PN_stdfloat PiecewiseCurve::
479 current_seg_range(PN_stdfloat t) const {
480  int ti = _last_ti;
481 
482  assert(ti < (int)_segs.size());
483 
484  // Adjust t to the range [0,1).
485  if (ti > 0) {
486  t = (t - _segs[ti-1]._tend) / (_segs[ti]._tend - _segs[ti-1]._tend);
487  } else {
488  t /= _segs[0]._tend;
489  }
490 
491  return t;
492 }
493 
494 /**
495  * Function to write the important information in the particular object to a
496  * Datagram
497  */
498 void PiecewiseCurve::
499 write_datagram(BamWriter *manager, Datagram &me) {
500  ParametricCurve::write_datagram(manager, me);
501 
502  me.add_uint32(_segs.size());
503  size_t i;
504  for (i = 0; i < _segs.size(); i++) {
505  const Curveseg &seg = _segs[i];
506  manager->write_pointer(me, seg._curve);
507  me.add_float64(seg._tend);
508  }
509 
510  _last_ti = 0;
511 }
512 
513 /**
514  * Function that reads out of the datagram (or asks manager to read) all of
515  * the data that is needed to re-create this object and stores it in the
516  * appropiate place
517  */
518 void PiecewiseCurve::
519 fillin(DatagramIterator &scan, BamReader *manager) {
520  ParametricCurve::fillin(scan, manager);
521 
522  size_t num_segs = scan.get_uint32();
523  _segs.reserve(num_segs);
524  size_t i;
525  for (i = 0; i < num_segs; i++) {
526  Curveseg seg;
527  manager->read_pointer(scan);
528  seg._curve = nullptr;
529  seg._tend = scan.get_float64();
530  _segs.push_back(seg);
531  }
532 }
533 
534 /**
535  * Takes in a vector of pointes to TypedWritable objects that correspond to
536  * all the requests for pointers that this object made to BamReader.
537  */
538 int PiecewiseCurve::
539 complete_pointers(TypedWritable **p_list, BamReader *manager) {
540  int used = ParametricCurve::complete_pointers(p_list, manager);
541 
542  size_t i;
543  for (i = 0; i < _segs.size(); i++) {
544  _segs[i]._curve = DCAST(ParametricCurve, p_list[used + i]);
545  }
546 
547  return used + _segs.size();
548 }
ParametricCurve * get_curveseg(int ti)
Returns the curve segment corresponding to the given index.
void remove_all_curvesegs()
Removes all curve segments from the curve.
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
Definition: datagram.I:123
PN_stdfloat get_tend(int ti) const
Returns the parametric end of the given segment of the curve.
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,...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
A virtual base class for parametric curves.
bool insert_curveseg(int ti, ParametricCurve *seg, PN_stdfloat tlength)
Inserts a new curve segment at the indicated index.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
int get_num_segs() const
Returns the number of curve segments that make up the Piecewise curve.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool get_tangent(PN_stdfloat t, LVecBase3 &tangent) const
Returns the tangent of the curve at a given parametric point t.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
virtual bool adjust_tangent(PN_stdfloat t, PN_stdfloat tx, PN_stdfloat ty, PN_stdfloat tz)
Recomputes the curve such that it has the tangent (tx, ty, tz) at time t, but keeps the same position...
virtual bool get_pt(PN_stdfloat t, LVecBase3 &point, LVecBase3 &tangent) const
Simultaneously returns the point and tangent of the curve at a given parametric point t.
A CubicCurveseg is any curve that can be completely described by four 4-valued basis vectors,...
Definition: cubicCurveseg.h:50
virtual PN_stdfloat get_max_t() const
Returns the upper bound of t for the entire curve.
PN_stdfloat get_tlength(int ti) const
Returns the parametric length of the given segment of the curve.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
A PiecewiseCurve is a curve made up of several curve segments, connected in a head-to-tail fashion.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool adjust_pt(PN_stdfloat t, PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat tx, PN_stdfloat ty, PN_stdfloat tz)
Recomputes the curve such that it passes through the point (px, py, pz) with the tangent (tx,...
bool set_tlength(int ti, PN_stdfloat tlength)
Sets the parametric length of the given segment of the curve.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
virtual bool get_bezier_segs(BezierSegs &bz_segs) const
Fills up the indicated vector with a list of BezierSeg structs that describe the curve.
A class to retrieve the individual data elements previously stored in a Datagram.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
virtual bool get_2ndtangent(PN_stdfloat t, LVecBase3 &tangent2) const
Returns the tangent of the first derivative of the curve at the point t.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void make_nurbs(int order, int num_cvs, const PN_stdfloat knots[], const LVecBase4 cvs[])
Defines the curve as a general NURBS curve.
bool remove_curveseg(int ti)
Removes the given curve segment from the curve and frees it.
PN_stdfloat get_tstart(int ti) const
Returns the parametric start of the given segment of the curve.
virtual bool rebuild_curveseg(int rtype0, PN_stdfloat t0, const LVecBase4 &v0, int rtype1, PN_stdfloat t1, const LVecBase4 &v1, int rtype2, PN_stdfloat t2, const LVecBase4 &v2, int rtype3, PN_stdfloat t3, const LVecBase4 &v3)
Rebuilds the current curve segment (as selected by the most recent call to find_curve()) according to...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool get_point(PN_stdfloat t, LVecBase3 &point) const
Returns the point of the curve at a given parametric point t.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
virtual bool is_valid() const
Returns true if the curve is defined.