Panda3D
Loading...
Searching...
No Matches
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
23using std::cerr;
24
25TypeHandle PiecewiseCurve::_type_handle;
26
27/**
28 *
29 */
30PiecewiseCurve::
31PiecewiseCurve() {
32 _last_ti = 0;
33}
34
35/**
36 *
37 */
38PiecewiseCurve::
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 */
49is_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 */
58get_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 */
70get_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 */
85get_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 */
98get_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 */
111adjust_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 */
139adjust_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 */
161adjust_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 */
185get_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 */
198get_num_segs() const {
199 return _segs.size();
200}
201
202/**
203 * Returns the curve segment corresponding to the given index.
204 */
206get_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 */
222insert_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 */
248remove_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 */
279get_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 */
288get_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 */
297get_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 */
309set_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 */
330make_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 */
352get_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 */
374rebuild_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 */
389bool PiecewiseCurve::
390find_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 */
478PN_stdfloat PiecewiseCurve::
479current_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 */
498void PiecewiseCurve::
499write_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 */
518void PiecewiseCurve::
519fillin(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 */
538int PiecewiseCurve::
539complete_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.
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.
A CubicCurveseg is any curve that can be completely described by four 4-valued basis vectors,...
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.
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.