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  */
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  */
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  */
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  */
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  */
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  */
248 remove_curveseg(int ti) {
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  */
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  */
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  */
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  */
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  */
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
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
A CubicCurveseg is any curve that can be completely described by four 4-valued basis vectors,...
Definition: cubicCurveseg.h:50
A class to retrieve the individual data elements previously stored in a Datagram.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
Definition: datagram.I:123
A virtual base class for parametric curves.
A PiecewiseCurve is a curve made up of several curve segments, connected in a head-to-tail fashion.
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,...
void remove_all_curvesegs()
Removes all curve segments from the curve.
ParametricCurve * get_curveseg(int ti)
Returns the curve segment corresponding to the given index.
PN_stdfloat get_tend(int ti) const
Returns the parametric end of the given segment of the curve.
bool insert_curveseg(int ti, ParametricCurve *seg, PN_stdfloat tlength)
Inserts a new curve segment at the indicated index.
virtual bool is_valid() const
Returns true if the curve is defined.
virtual bool get_tangent(PN_stdfloat t, LVecBase3 &tangent) const
Returns the tangent of the curve at a given parametric point t.
PN_stdfloat get_tlength(int ti) const
Returns the parametric length of the given segment of the curve.
virtual bool get_point(PN_stdfloat t, LVecBase3 &point) const
Returns the point of the curve at a given parametric point t.
virtual bool get_bezier_segs(BezierSegs &bz_segs) const
Fills up the indicated vector with a list of BezierSeg structs that describe the curve.
bool set_tlength(int ti, PN_stdfloat tlength)
Sets the parametric length of the given segment of the curve.
virtual PN_stdfloat get_max_t() const
Returns the upper bound of t for the entire curve.
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 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...
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 get_2ndtangent(PN_stdfloat t, LVecBase3 &tangent2) const
Returns the tangent of the first derivative of the curve at the point t.
int get_num_segs() const
Returns the number of curve segments that make up the Piecewise curve.
void make_nurbs(int order, int num_cvs, const PN_stdfloat knots[], const LVecBase4 cvs[])
Defines the curve as a general NURBS curve.
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.
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,...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
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().
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
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.