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