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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
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
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
A CubicCurveseg is any curve that can be completely described by four 4-valued basis vectors,...
Definition: cubicCurveseg.h:50
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.
A class to retrieve the individual data elements previously stored in a Datagram.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
int8_t get_int8()
Extracts a signed 8-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
void add_int8(int8_t value)
Adds a signed 8-bit integer to the datagram.
Definition: datagram.I:42
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
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
This abstract class defines the interface only for a Nurbs-style curve, with knots and coordinates in...
A Nonuniform Rational B-Spline.
Definition: nurbsCurve.h:41
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
virtual void set_order(int order)
Changes the order of the curve.
Definition: nurbsCurve.cxx:92
virtual bool recompute()
Recalculates the curve basis according to the latest position of the CV's, knots, etc.
Definition: nurbsCurve.cxx:267
virtual PN_stdfloat get_knot(int n) const
Retrieves the value of the indicated knot.
Definition: nurbsCurve.cxx:249
static void register_with_read_factory()
Initializes the factory for reading these things from Bam files.
Definition: nurbsCurve.cxx:487
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
virtual void remove_all_cvs()
Removes all CV's from the curve.
Definition: nurbsCurve.cxx:201
virtual bool remove_cv(int n)
Removes the indicated CV from the curve.
Definition: nurbsCurve.cxx:188
virtual int get_num_knots() const
Returns the number of knots on the curve.
Definition: nurbsCurve.cxx:119
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
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
virtual bool set_cv(int n, const LVecBase4 &v)
Repositions the indicated CV.
Definition: nurbsCurve.cxx:210
virtual PandaNode * make_copy() const
Returns a newly-allocated PandaNode that is a shallow copy of this one.
Definition: nurbsCurve.cxx:83
virtual bool convert_to_nurbs(ParametricCurve *nc) const
Stores in the indicated NurbsCurve a NURBS representation of an equivalent curve.
Definition: nurbsCurve.cxx:434
virtual bool set_knot(int n, PN_stdfloat t)
Sets the value of the indicated knot.
Definition: nurbsCurve.cxx:235
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
A virtual base class for parametric curves.
virtual bool convert_to_nurbs(ParametricCurve *nc) const
Stores in the indicated NurbsCurve a NURBS representation of an equivalent curve.
void set_curve_type(int type)
Sets the flag indicating the use to which the curve is intended to be put.
bool insert_curveseg(int ti, ParametricCurve *seg, PN_stdfloat tlength)
Inserts a new curve segment at the indicated index.
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
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.