Panda3D

nurbsCurve.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 nurbsCurve.cxx
10 * @author drose
11 * @date 1998-02-27
12 */
13
14 #include "nurbsCurve.h"
15 #include "config_parametrics.h"
16
17 #include "indent.h"
18 #include "datagram.h"
19 #include "datagramIterator.h"
20 #include "bamWriter.h"
21 #include "bamReader.h"
22 #include "epvector.h"
23
24 TypeHandle NurbsCurve::_type_handle;
25
26 /**
27 *
28 */
29 NurbsCurve::
30 NurbsCurve() {
31 _order = 4;
32 }
33
34 /**
35 * Constructs a NURBS curve equivalent to the indicated (possibly non-NURBS)
36 * curve.
37 */
38 NurbsCurve::
39 NurbsCurve(const ParametricCurve &pc) {
40 _order = 4;
41
42 if (!pc.convert_to_nurbs(this)) {
43 parametrics_cat->warning()
44 << "Cannot make a NURBS from the indicated curve.\n";
45 }
46 }
47
48 /**
49 * Constructs a NURBS curve according to the indicated NURBS parameters.
50 */
51 NurbsCurve::
52 NurbsCurve(int order, int num_cvs,
53 const PN_stdfloat knots[], const LVecBase4 cvs[]) {
54 _order = order;
55
56 int i;
57 _cvs.reserve(num_cvs);
58 for (i = 0; i < num_cvs; i++) {
59 append_cv(cvs[i]);
60 }
61
62 int num_knots = num_cvs + order;
63 for (i = 0; i < num_knots; i++) {
64 set_knot(i, knots[i]);
65 }
66
67 recompute();
68 }
69
70 /**
71 *
72 */
73 NurbsCurve::
74 ~NurbsCurve() {
75 }
76
77 /**
78 * Returns a newly-allocated PandaNode that is a shallow copy of this one. It
79 * will be a different pointer, but its internal data may or may not be shared
80 * with that of the original PandaNode. No children will be copied.
81 */
82 PandaNode *NurbsCurve::
83 make_copy() const {
84 return new NurbsCurve(*this);
85 }
86
87 /**
88 * Changes the order of the curve. Must be a value from 1 to 4. Can only be
89 * done when there are no cv's.
90 */
91 void NurbsCurve::
92 set_order(int order) {
93 nassertv(order >= 1 && order <= 4);
94 nassertv(_cvs.empty());
95
96 _order = order;
97 }
98
99 /**
100 *
101 */
102 int NurbsCurve::
103 get_order() const {
104 return _order;
105 }
106
107 /**
108 *
109 */
110 int NurbsCurve::
111 get_num_cvs() const {
112 return _cvs.size();
113 }
114
115 /**
116 * Returns the number of knots on the curve.
117 */
118 int NurbsCurve::
119 get_num_knots() const {
120 return _cvs.size() + _order;
121 }
122
123
124
125 /**
126 * Inserts a new CV into the middle of the curve at the indicated parametric
127 * value. This doesn't change the shape or timing of the curve; however, it
128 * is irreversible: if the new CV is immediately removed, the curve will be
129 * changed. Returns true if successful, false otherwise.
130 */
131 bool NurbsCurve::
132 insert_cv(PN_stdfloat t) {
133 if (_cvs.empty()) {
134 append_cv(0.0f, 0.0f, 0.0f);
135 return true;
136 }
137
138 if (t <= 0) {
139 t = 0.0f;
140 }
141
142 int k = find_cv(t);
143 if (k < 0) {
144 append_cv(_cvs.back()._p);
145 return true;
146 }
147
148 // Now we are inserting a knot between k-1 and k. We'll adjust the CV's
149 // according to Bohm's rule.
150
151 // First, get the new values of all the CV's that will change. These are
152 // the CV's in the range [k - (_order-1), k-1].
153
154 LVecBase4 new_cvs[3];
155 int i;
156 for (i = 0; i < _order-1; i++) {
157 int nk = i + k - (_order-1);
158 PN_stdfloat ti = get_knot(nk);
159 PN_stdfloat d = get_knot(nk + _order-1) - ti;
160 if (d == 0.0f) {
161 new_cvs[i] = _cvs[nk-1]._p;
162 } else {
163 PN_stdfloat a = (t - ti) / d;
164 new_cvs[i] = (1.0f-a)*_cvs[nk-1]._p + a*_cvs[nk]._p;
165 }
166 }
167
168 // Now insert the new CV
169 _cvs.insert(_cvs.begin() + k-1, CV());
170
171 // Set all the new position values
172 for (i = 0; i < _order-1; i++) {
173 int nk = i + k - (_order-1);
174 _cvs[nk]._p = new_cvs[i];
175 }
176
177 // And set the new knot value.
178 _cvs[k-1]._t = t;
179
180 return true;
181 }
182
183 /**
184 * Removes the indicated CV from the curve. Returns true if the CV index was
185 * valid, false otherwise.
186 */
187 bool NurbsCurve::
188 remove_cv(int n) {
189 if (n < 0 || n >= (int)_cvs.size()) {
190 return false;
191 }
192
193 _cvs.erase(_cvs.begin() + n);
194 return true;
195 }
196
197 /**
198 * Removes all CV's from the curve.
199 */
200 void NurbsCurve::
201 remove_all_cvs() {
202 _cvs.erase(_cvs.begin(), _cvs.end());
203 }
204
205
206 /**
207 * Repositions the indicated CV. Returns true if successful, false otherwise.
208 */
209 bool NurbsCurve::
210 set_cv(int n, const LVecBase4 &v) {
211 nassertr(n >= 0 && n < get_num_cvs(), false);
212
213 _cvs[n]._p = v;
214 return true;
215 }
216
217 /**
218 * Returns the position in homogeneous space of the indicated CV.
219 */
220 LVecBase4 NurbsCurve::
221 get_cv(int n) const {
222 nassertr(n >= 0 && n < get_num_cvs(), LVecBase4::zero());
223
224 return _cvs[n]._p;
225 }
226
227
228 /**
229 * Sets the value of the indicated knot. There are get_num_cvs() + _order
230 * knot values, but the first _order - 1 and the last 1 knot values cannot be
231 * changed. It is also an error to set a knot value outside the range of its
232 * neighbors.
233 */
234 bool NurbsCurve::
235 set_knot(int n, PN_stdfloat t) {
236 nassertr(n >= 0 && n < get_num_knots(), false);
237
238 if (n < _order || n-1 >= (int)_cvs.size()) {
239 return false;
240 }
241 _cvs[n-1]._t = t;
242 return true;
243 }
244
245 /**
246 * Retrieves the value of the indicated knot.
247 */
248 PN_stdfloat NurbsCurve::
249 get_knot(int n) const {
250 if (n < _order || _cvs.empty()) {
251 return 0.0f;
252 } else if (n-1 >= (int)_cvs.size()) {
253 return _cvs.back()._t;
254 } else {
255 return _cvs[n-1]._t;
256 }
257 }
258
259
260 /**
261 * Recalculates the curve basis according to the latest position of the CV's,
262 * knots, etc. Until this function is called, adjusting the NURBS parameters
263 * will have no visible effect on the curve. Returns true if the resulting
264 * curve is valid, false otherwise.
265 */
266 bool NurbsCurve::
267 recompute() {
268 _segs.erase(_segs.begin(), _segs.end());
269
270 PN_stdfloat knots[8];
271 LVecBase4 cvs[4];
272
273 if ((int)_cvs.size() > _order-1) {
274 for (int cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
275 if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
276 // There are _order consecutive CV's that define each segment,
277 // beginning at cv. Collect the CV's and knot values that define this
278 // segment.
279 int c;
280 for (c = 0; c < _order; c++) {
281 cvs[c] = _cvs[c+cv]._p;
282 }
283 for (c = 0; c < _order+_order; c++) {
284 knots[c] = get_knot(c+cv);
285 }
286
287 insert_curveseg(_segs.size(), new CubicCurveseg(_order, knots, cvs),
288 knots[_order] - knots[_order-1]);
289 }
290 }
291 }
292
293 return !_segs.empty();
294 }
295
296 /**
297 * Rebuilds the current curve segment (as selected by the most recent call to
298 * find_curve()) according to the specified properties (see
299 * CubicCurveseg::compute_seg). Returns true if possible, false if something
300 * goes horribly wrong.
301 */
302 bool NurbsCurve::
303 rebuild_curveseg(int rtype0, PN_stdfloat t0, const LVecBase4 &v0,
304 int rtype1, PN_stdfloat t1, const LVecBase4 &v1,
305 int rtype2, PN_stdfloat t2, const LVecBase4 &v2,
306 int rtype3, PN_stdfloat t3, const LVecBase4 &v3) {
307 // Figure out which CV's contributed to this segment.
308 int seg = 0;
309
310 nassertr((int)_cvs.size() > _order-1, false);
311
312 int cv = 0;
313 for (cv = 0; cv < (int)_cvs.size()-(_order-1); cv++) {
314 if (get_knot(cv+_order-1) < get_knot(cv+_order)) {
315 if (seg == _last_ti) {
316 break;
317 }
318 seg++;
319 }
320 }
321
322 // Now copy the cvs and knots in question.
323 LMatrix4 G;
324 PN_stdfloat knots[8];
325
326 int c;
327
328 // We only need to build the geometry matrix if at least one of the
329 // properties depends on the original value.
330 if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
331 for (c = 0; c < 4; c++) {
332 const LVecBase4 &s = (c < _order) ? _cvs[c+cv]._p : LVecBase4::zero();
333
334 G.set_col(c, s);
335 }
336 }
337
338 // But we always need the knot vector to determine the basis matrix.
339 for (c = 0; c < _order+_order; c++) {
340 knots[c] = get_knot(c+cv);
341 }
342
343 LMatrix4 B;
344 compute_nurbs_basis(_order, knots, B);
345
346 LMatrix4 Bi;
347 Bi = invert(B);
348
349 if (!CubicCurveseg::compute_seg(rtype0, t0, v0,
350 rtype1, t1, v1,
351 rtype2, t2, v2,
352 rtype3, t3, v3,
353 B, Bi, G)) {
354 return false;
355 }
356
357 // Now extract the new CV's from the new G matrix, and restore them to the
358 // curve.
359 for (c = 0; c < _order; c++) {
360 _cvs[c+cv]._p = G.get_col(c);
361 }
362
363 return true;
364 }
365
366 /**
367 * Regenerates this curve as one long curve: the first curve connected end-to-
368 * end with the second one. Either a or b may be the same as 'this'.
369 *
370 * Returns true if successful, false on failure or if the curve type does not
371 * support stitching.
372 */
373 bool NurbsCurve::
374 stitch(const ParametricCurve *a, const ParametricCurve *b) {
375 // First, make a copy of both of our curves. This ensures they are of the
376 // correct type, and also protects us in case one of them is the same as
377 // 'this'.
378 PT(NurbsCurve) na = new NurbsCurve(*a);
379 PT(NurbsCurve) nb = new NurbsCurve(*b);
380
381 if (na->get_num_cvs() == 0 || nb->get_num_cvs() == 0) {
382 return false;
383 }
384
385 if (na->get_order() != nb->get_order()) {
386 parametrics_cat->error()
387 << "Cannot stitch NURBS curves of different orders!\n";
388 return false;
389 }
390
391 // First, translate curve B to move its first CV to curve A's last CV.
392 LVecBase3 point_offset =
393 na->get_cv_point(na->get_num_cvs() - 1) - nb->get_cv_point(0);
394 int num_b_cvs = nb->get_num_cvs();
395 for (int i = 0; i < num_b_cvs; i++) {
396 nb->set_cv_point(i, nb->get_cv_point(i) + point_offset);
397 }
398
399 // Now define a vector of all of A's CV's except the last one.
400 _cvs = na->_cvs;
401 if (!_cvs.empty()) {
402 _cvs.pop_back();
403 }
404
405 PN_stdfloat t = na->get_max_t();
406
407 // Now add all the new CV's.
408 epvector<CV>::iterator ci;
409 for (ci = nb->_cvs.begin(); ci != nb->_cvs.end(); ++ci) {
410 CV new_cv = (*ci);
411 new_cv._t += t;
412 _cvs.push_back(new_cv);
413 }
414
415 recompute();
416 return true;
417 }
418
419
420 /**
421 * Returns a pointer to the object as a NurbsCurveInterface object if it
422 * happens to be a NURBS-style curve; otherwise, returns NULL.
423 */
424 NurbsCurveInterface *NurbsCurve::
425 get_nurbs_interface() {
426 return this;
427 }
428
429 /**
430 * Stores in the indicated NurbsCurve a NURBS representation of an equivalent
431 * curve. Returns true if successful, false otherwise.
432 */
433 bool NurbsCurve::
434 convert_to_nurbs(ParametricCurve *nc) const {
435 nc->set_curve_type(_curve_type);
436 return NurbsCurveInterface::convert_to_nurbs(nc);
437 }
438
439 /**
440 *
441 */
442 void NurbsCurve::
443 write(std::ostream &out, int indent_level) const {
444 NurbsCurveInterface::write(out, indent_level);
445 }
446
447 /**
448 * Adds a new CV to the end of the curve. Creates a new knot value by adding
449 * 1 to the last knot value. Returns the index of the new CV.
450 */
451 int NurbsCurve::
452 append_cv_impl(const LVecBase4 &v) {
453 _cvs.push_back(CV(v, get_knot(_cvs.size())+1.0f));
454 return _cvs.size()-1;
455 }
456
457 /**
458 * Formats the curve as an egg structure to write to the indicated stream.
459 * Returns true on success, false on failure.
460 */
461 bool NurbsCurve::
462 format_egg(std::ostream &out, const std::string &name, const std::string &curve_type,
463 int indent_level) const {
464 return NurbsCurveInterface::format_egg(out, name, curve_type, indent_level);
465 }
466
467 /**
468 * Finds the first knot whose value is >= t, or -1 if t is beyond the end of
469 * the curve.
470 */
471 int NurbsCurve::
472 find_cv(PN_stdfloat t) {
473 int i;
474 for (i = _order-1; i < (int)_cvs.size(); i++) {
475 if (_cvs[i]._t >= t) {
476 return i+1;
477 }
478 }
479
480 return -1;
481 }
482
483 /**
484 * Initializes the factory for reading these things from Bam files.
485 */
486 void NurbsCurve::
487 register_with_read_factory() {
488 BamReader::get_factory()->register_factory(get_class_type(), make_NurbsCurve);
489 }
490
491 /**
492 * Factory method to generate an object of this type.
493 */
494 TypedWritable *NurbsCurve::
495 make_NurbsCurve(const FactoryParams &params) {
496 NurbsCurve *me = new NurbsCurve;
497 DatagramIterator scan;
498 BamReader *manager;
499
500 parse_params(params, scan, manager);
501 me->fillin(scan, manager);
502 return me;
503 }
504
505 /**
506 * Function to write the important information in the particular object to a
507 * Datagram
508 */
509 void NurbsCurve::
510 write_datagram(BamWriter *manager, Datagram &me) {
511 PiecewiseCurve::write_datagram(manager, me);
512
513 me.add_int8(_order);
514
515 me.add_uint32(_cvs.size());
516 size_t i;
517 for (i = 0; i < _cvs.size(); i++) {
518 const CV &cv = _cvs[i];
519 cv._p.write_datagram(me);
520 me.add_float64(cv._t);
521 }
522 }
523
524 /**
525 * Function that reads out of the datagram (or asks manager to read) all of
526 * the data that is needed to re-create this object and stores it in the
527 * appropiate place
528 */
529 void NurbsCurve::
530 fillin(DatagramIterator &scan, BamReader *manager) {
531 PiecewiseCurve::fillin(scan, manager);
532
533 _order = scan.get_int8();
534
535 size_t num_cvs = scan.get_uint32();
536
537 _cvs.reserve(num_cvs);
538 size_t i;
539 for (i = 0; i < num_cvs; i++) {
540 CV cv;
541 cv._p.read_datagram(scan);
542 cv._t = scan.get_float64();
543 _cvs.push_back(cv);
544 }
545 }
bamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
parse_params
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
bamWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
CubicCurveseg
A CubicCurveseg is any curve that can be completely described by four 4-valued basis vectors,...
Definition: cubicCurveseg.h:50
CubicCurveseg::compute_seg
static bool compute_seg(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, const LMatrix4 &B, const LMatrix4 &Bi, LMatrix4 &G)
Given a set of four properties of a curve segment (e.g.
Definition: cubicCurveseg.cxx:459
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
DatagramIterator::get_uint32
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: datagramIterator.I:164
DatagramIterator::get_int8
int8_t get_int8()
Extracts a signed 8-bit integer.
Definition: datagramIterator.I:56
DatagramIterator::get_float64
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
Definition: datagramIterator.I:221
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Datagram::add_uint32
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
Datagram::add_float64
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
Definition: datagram.I:123
Datagram::add_int8
void add_int8(int8_t value)
Adds a signed 8-bit integer to the datagram.
Definition: datagram.I:42
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
Factory::register_factory
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
NurbsCurveInterface
This abstract class defines the interface only for a Nurbs-style curve, with knots and coordinates in...
Definition: nurbsCurveInterface.h:30
NurbsCurve
A Nonuniform Rational B-Spline.
Definition: nurbsCurve.h:41
NurbsCurve::get_nurbs_interface
virtual NurbsCurveInterface * get_nurbs_interface()
Returns a pointer to the object as a NurbsCurveInterface object if it happens to be a NURBS-style cur...
Definition: nurbsCurve.cxx:425
NurbsCurve::set_order
virtual void set_order(int order)
Changes the order of the curve.
Definition: nurbsCurve.cxx:92
NurbsCurve::recompute
virtual bool recompute()
Recalculates the curve basis according to the latest position of the CV's, knots, etc.
Definition: nurbsCurve.cxx:267
NurbsCurve::get_knot
virtual PN_stdfloat get_knot(int n) const
Retrieves the value of the indicated knot.
Definition: nurbsCurve.cxx:249
NurbsCurve::register_with_read_factory
static void register_with_read_factory()
Initializes the factory for reading these things from Bam files.
Definition: nurbsCurve.cxx:487
NurbsCurve::insert_cv
virtual bool insert_cv(PN_stdfloat t)
Inserts a new CV into the middle of the curve at the indicated parametric value.
Definition: nurbsCurve.cxx:132
NurbsCurve::remove_all_cvs
virtual void remove_all_cvs()
Removes all CV's from the curve.
Definition: nurbsCurve.cxx:201
NurbsCurve::remove_cv
virtual bool remove_cv(int n)
Removes the indicated CV from the curve.
Definition: nurbsCurve.cxx:188
NurbsCurve::get_num_knots
virtual int get_num_knots() const
Returns the number of knots on the curve.
Definition: nurbsCurve.cxx:119
NurbsCurve::stitch
virtual bool stitch(const ParametricCurve *a, const ParametricCurve *b)
Regenerates this curve as one long curve: the first curve connected end-to- end with the second one.
Definition: nurbsCurve.cxx:374
NurbsCurve::rebuild_curveseg
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...
Definition: nurbsCurve.cxx:303
NurbsCurve::set_cv
virtual bool set_cv(int n, const LVecBase4 &v)
Repositions the indicated CV.
Definition: nurbsCurve.cxx:210
NurbsCurve::make_copy
virtual PandaNode * make_copy() const
Returns a newly-allocated PandaNode that is a shallow copy of this one.
Definition: nurbsCurve.cxx:83
NurbsCurve::convert_to_nurbs
virtual bool convert_to_nurbs(ParametricCurve *nc) const
Stores in the indicated NurbsCurve a NURBS representation of an equivalent curve.
Definition: nurbsCurve.cxx:434
NurbsCurve::set_knot
virtual bool set_knot(int n, PN_stdfloat t)
Sets the value of the indicated knot.
Definition: nurbsCurve.cxx:235
PandaNode
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
ParametricCurve
A virtual base class for parametric curves.
Definition: parametricCurve.h:56
ParametricCurve::convert_to_nurbs
virtual bool convert_to_nurbs(ParametricCurve *nc) const
Stores in the indicated NurbsCurve a NURBS representation of an equivalent curve.
Definition: parametricCurve.cxx:498
ParametricCurve::set_curve_type
void set_curve_type(int type)
Sets the flag indicating the use to which the curve is intended to be put.
Definition: parametricCurve.cxx:105
PiecewiseCurve::insert_curveseg
bool insert_curveseg(int ti, ParametricCurve *seg, PN_stdfloat tlength)
Inserts a new curve segment at the indicated index.
Definition: piecewiseCurve.cxx:222
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
config_parametrics.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
datagramIterator.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
datagram.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
epvector.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
nurbsCurve.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.