Panda3D
nurbsSurfaceEvaluator.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 nurbsSurfaceEvaluator.cxx
10  * @author drose
11  * @date 2003-10-10
12  */
13 
14 #include "nurbsSurfaceEvaluator.h"
15 
16 /**
17  *
18  */
19 NurbsSurfaceEvaluator::
20 NurbsSurfaceEvaluator() {
21  _u_order = 4;
22  _v_order = 4;
23  _u_knots_dirty = true;
24  _v_knots_dirty = true;
25  _u_basis_dirty = true;
26  _v_basis_dirty = true;
27 }
28 
29 /**
30  *
31  */
32 NurbsSurfaceEvaluator::
33 ~NurbsSurfaceEvaluator() {
34 }
35 
36 /**
37  * Resets all the vertices and knots to their default values, and sets the
38  * surface up with the indicated number of vertices. You must then call
39  * set_vertex() repeatedly to fill in all of the vertex values appropriately.
40  */
42 reset(int num_u_vertices, int num_v_vertices) {
43  int num_vertices = num_u_vertices * num_v_vertices;
44  _vertices.clear();
45  _vertices.reserve(num_vertices);
46  _num_u_vertices = num_u_vertices;
47  _num_v_vertices = num_v_vertices;
48 
49  for (int i = 0; i < num_vertices; i++) {
50  _vertices.push_back(NurbsVertex());
51  }
52  _u_knots_dirty = true;
53  _v_knots_dirty = true;
54  _u_basis_dirty = true;
55  _v_basis_dirty = true;
56 }
57 
58 /**
59  * Returns the coordinate space of the nth control vertex of the surface,
60  * expressed as a NodePath.
61  */
63 get_vertex_space(int ui, int vi, const NodePath &rel_to) const {
64 #ifndef NDEBUG
65  static NodePath empty_node_path;
66  nassertr(ui >= 0 && ui < _num_u_vertices &&
67  vi >= 0 && vi < _num_v_vertices, empty_node_path);
68 #endif
69  return vert(ui, vi).get_space(rel_to);
70 }
71 
72 /**
73  * Simultaneously sets several extended values in the slots d through (d +
74  * num_values - 1) from the num_values elements of the indicated array. This
75  * is equivalent to calling set_extended_vertex() num_values times. See
76  * set_extended_vertex().
77  */
79 set_extended_vertices(int ui, int vi, int d,
80  const PN_stdfloat values[], int num_values) {
81  nassertv(ui >= 0 && ui < _num_u_vertices &&
82  vi >= 0 && vi < _num_v_vertices);
83 
84  NurbsVertex &vertex = vert(ui, vi);
85  for (int n = 0; n < num_values; n++) {
86  vertex.set_extended_vertex(d + n, values[n]);
87  }
88 }
89 
90 /**
91  * Sets the value of the nth knot. Each knot value should be greater than or
92  * equal to the preceding value. If no knot values are set, a default knot
93  * vector is supplied.
94  */
96 set_u_knot(int i, PN_stdfloat knot) {
97  if (_u_knots_dirty) {
98  recompute_u_knots();
99  }
100  nassertv(i >= 0 && i < (int)_u_knots.size());
101  _u_knots[i] = knot;
102 }
103 
104 /**
105  * Returns the value of the nth knot.
106  */
107 PN_stdfloat NurbsSurfaceEvaluator::
108 get_u_knot(int i) const {
109  if (_u_knots_dirty) {
110  ((NurbsSurfaceEvaluator *)this)->recompute_u_knots();
111  }
112  nassertr(i >= 0 && i < (int)_u_knots.size(), 0.0f);
113  return _u_knots[i];
114 }
115 
116 /**
117  * Normalizes the knot sequence so that the parametric range of the surface in
118  * the U direction is 0 .. 1.
119  */
122  if (_u_knots_dirty) {
123  recompute_u_knots();
124  }
125 
126  if (_num_u_vertices > _u_order - 1) {
127  double min_value = _u_knots[_u_order - 1];
128  double max_value = _u_knots[_num_u_vertices];
129  double range = (max_value - min_value);
130 
131  for (Knots::iterator ki = _u_knots.begin(); ki != _u_knots.end(); ++ki) {
132  (*ki) = ((*ki) - min_value) / range;
133  }
134  _u_basis_dirty = true;
135  }
136 }
137 
138 /**
139  * Sets the value of the nth knot. Each knot value should be greater than or
140  * equal to the preceding value. If no knot values are set, a default knot
141  * vector is supplied.
142  */
144 set_v_knot(int i, PN_stdfloat knot) {
145  if (_v_knots_dirty) {
146  recompute_v_knots();
147  }
148  nassertv(i >= 0 && i < (int)_v_knots.size());
149  _v_knots[i] = knot;
150 }
151 
152 /**
153  * Returns the value of the nth knot.
154  */
155 PN_stdfloat NurbsSurfaceEvaluator::
156 get_v_knot(int i) const {
157  if (_v_knots_dirty) {
158  ((NurbsSurfaceEvaluator *)this)->recompute_v_knots();
159  }
160  nassertr(i >= 0 && i < (int)_v_knots.size(), 0.0f);
161  return _v_knots[i];
162 }
163 
164 /**
165  * Normalizes the knot sequence so that the parametric range of the surface in
166  * the U direction is 0 .. 1.
167  */
170  if (_v_knots_dirty) {
171  recompute_v_knots();
172  }
173 
174  if (_num_v_vertices > _v_order - 1) {
175  double min_value = _v_knots[_v_order - 1];
176  double max_value = _v_knots[_num_v_vertices];
177  double range = (max_value - min_value);
178 
179  for (Knots::iterator ki = _v_knots.begin(); ki != _v_knots.end(); ++ki) {
180  (*ki) = ((*ki) - min_value) / range;
181  }
182  _v_basis_dirty = true;
183  }
184 }
185 
186 /**
187  * Returns a NurbsSurfaceResult object that represents the result of applying
188  * the knots to all of the current values of the vertices, transformed into
189  * the indicated coordinate space.
190  */
191 PT(NurbsSurfaceResult) NurbsSurfaceEvaluator::
192 evaluate(const NodePath &rel_to) const {
193  if (_u_basis_dirty) {
194  ((NurbsSurfaceEvaluator *)this)->recompute_u_basis();
195  }
196  if (_v_basis_dirty) {
197  ((NurbsSurfaceEvaluator *)this)->recompute_v_basis();
198  }
199 
200  // First, transform the vertices as appropriate.
201  Vert4Array vecs;
202  get_vertices(vecs, rel_to);
203 
204  // And apply those transformed vertices to the basis matrices to derive the
205  // result.
206  return new NurbsSurfaceResult(_u_basis, _v_basis,
207  &vecs[0], &_vertices[0],
208  _num_u_vertices, _num_v_vertices);
209 }
210 
211 /**
212  *
213  */
214 void NurbsSurfaceEvaluator::
215 output(std::ostream &out) const {
216  out << "NurbsSurface, (" << get_num_u_knots() << ", " << get_num_v_knots()
217  << ") knots.";
218 }
219 
220 /**
221  * Fills the indicated vector with the set of vertices in the surface,
222  * transformed to the given space. This flavor returns the vertices in
223  * 4-dimensional homogenous space.
224  *
225  * Vertices are arranged in linear sequence, with the v coordinate changing
226  * more rapidly.
227  */
228 void NurbsSurfaceEvaluator::
229 get_vertices(NurbsSurfaceEvaluator::Vert4Array &verts, const NodePath &rel_to) const {
230  int num_vertices = (int)_vertices.size();
231  verts.reserve(verts.size() + num_vertices);
232  int vi;
233  for (vi = 0; vi < num_vertices; vi++) {
234  NodePath space = _vertices[vi].get_space(rel_to);
235  const LVecBase4 &vertex = _vertices[vi].get_vertex();
236  if (space.is_empty()) {
237  verts.push_back(vertex);
238  } else {
239  CPT(TransformState) transform = space.get_transform(rel_to);
240  const LMatrix4 &mat = transform->get_mat();
241  verts.push_back(vertex * mat);
242  }
243  }
244 }
245 
246 /**
247  * Fills the indicated vector with the set of vertices in the surface,
248  * transformed to the given space. This flavor returns the vertices in
249  * 3-dimensional space.
250  *
251  * Vertices are arranged in linear sequence, with the v coordinate changing
252  * more rapidly.
253  */
254 void NurbsSurfaceEvaluator::
255 get_vertices(NurbsSurfaceEvaluator::Vert3Array &verts, const NodePath &rel_to) const {
256  int num_vertices = (int)_vertices.size();
257  verts.reserve(verts.size() + num_vertices);
258  int vi;
259  for (vi = 0; vi < num_vertices; vi++) {
260  const NodePath &space = _vertices[vi].get_space(rel_to);
261  LVecBase4 vertex = _vertices[vi].get_vertex();
262  if (!space.is_empty()) {
263  CPT(TransformState) transform = space.get_transform(rel_to);
264  const LMatrix4 &mat = transform->get_mat();
265  vertex = vertex * mat;
266  }
267  LPoint3 v3(vertex[0] / vertex[3], vertex[1] / vertex[3], vertex[2] / vertex[3]);
268  verts.push_back(v3);
269  }
270 }
271 
272 /**
273  * Creates a default knot vector.
274  */
275 void NurbsSurfaceEvaluator::
276 recompute_u_knots() {
277  _u_knots.clear();
278  int num_knots = get_num_u_knots();
279  _u_knots.reserve(num_knots);
280 
281  PN_stdfloat value = 0.0f;
282 
283  int i = 0;
284  while (i < _u_order) {
285  _u_knots.push_back(value);
286  i++;
287  }
288  while (i < num_knots - _u_order) {
289  value += 1.0f;
290  _u_knots.push_back(value);
291  i++;
292  }
293  value += 1.0f;
294  while (i < num_knots) {
295  _u_knots.push_back(value);
296  i++;
297  }
298 
299  _u_knots_dirty = false;
300 }
301 
302 /**
303  * Creates a default knot vector.
304  */
305 void NurbsSurfaceEvaluator::
306 recompute_v_knots() {
307  _v_knots.clear();
308  int num_knots = get_num_v_knots();
309  _v_knots.reserve(num_knots);
310 
311  PN_stdfloat value = 0.0f;
312 
313  int i = 0;
314  while (i < _v_order) {
315  _v_knots.push_back(value);
316  i++;
317  }
318  while (i < num_knots - _v_order) {
319  value += 1.0f;
320  _v_knots.push_back(value);
321  i++;
322  }
323  value += 1.0f;
324  while (i < num_knots) {
325  _v_knots.push_back(value);
326  i++;
327  }
328 
329  _v_knots_dirty = false;
330 }
331 
332 /**
333  * Recomputes the basis matrices according to the knot vector.
334  */
335 void NurbsSurfaceEvaluator::
336 recompute_u_basis() {
337  if (_u_knots_dirty) {
338  ((NurbsSurfaceEvaluator *)this)->recompute_u_knots();
339  }
340 
341  _u_basis.clear(_u_order);
342  if (_num_u_vertices > _u_order - 1) {
343  int min_knot = _u_order;
344  int max_knot = _num_u_vertices;
345 
346  for (int i = min_knot; i <= max_knot; i++) {
347  nassertv(i - 1 >= 0 && i < (int)_u_knots.size());
348  if (_u_knots[i - 1] < _u_knots[i]) {
349  // Here's a non-empty segment.
350  _u_basis.append_segment(i - _u_order, &_u_knots[i - _u_order]);
351  }
352  }
353  }
354 
355  _u_basis_dirty = false;
356 }
357 
358 /**
359  * Recomputes the basis matrices according to the knot vector.
360  */
361 void NurbsSurfaceEvaluator::
362 recompute_v_basis() {
363  if (_v_knots_dirty) {
364  ((NurbsSurfaceEvaluator *)this)->recompute_v_knots();
365  }
366 
367  _v_basis.clear(_v_order);
368  if (_num_v_vertices > _v_order - 1) {
369  int min_knot = _v_order;
370  int max_knot = _num_v_vertices;
371 
372  for (int i = min_knot; i <= max_knot; i++) {
373  nassertv(i - 1 >= 0 && i < (int)_v_knots.size());
374  if (_v_knots[i - 1] < _v_knots[i]) {
375  // Here's a non-empty segment.
376  _v_basis.append_segment(i - _v_order, &_v_knots[i - _v_order]);
377  }
378  }
379  }
380 
381  _v_basis_dirty = false;
382 }
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:795
void append_segment(int vertex_index, const PN_stdfloat knots[])
Computes a NURBS basis for one segment of the curve and appends it to the set of basis matrices.
void clear(int order)
Removes all the segments from the curve.
This class is an abstraction for evaluating NURBS surfaces.
void set_extended_vertices(int ui, int vi, int d, const PN_stdfloat values[], int num_values)
Simultaneously sets several extended values in the slots d through (d + num_values - 1) from the num_...
void reset(int num_u_vertices, int num_v_vertices)
Resets all the vertices and knots to their default values, and sets the surface up with the indicated...
set_u_knot
Sets the value of the nth knot.
get_v_knot
Returns the value of the nth knot.
set_v_knot
Sets the value of the nth knot.
get_num_v_knots
Returns the number of knot values in the surface in the V direction.
void normalize_u_knots()
Normalizes the knot sequence so that the parametric range of the surface in the U direction is 0 .
get_num_u_knots
Returns the number of knot values in the surface in the U direction.
NodePath get_vertex_space(int ui, int vi, const NodePath &rel_to) const
Returns the coordinate space of the nth control vertex of the surface, expressed as a NodePath.
void normalize_v_knots()
Normalizes the knot sequence so that the parametric range of the surface in the U direction is 0 .
get_u_knot
Returns the value of the nth knot.
The result of a NurbsSurfaceEvaluator.
This represents a single control vertex in a NurbsEvaluator.
Definition: nurbsVertex.h:32
void set_extended_vertex(int d, PN_stdfloat value)
Sets an n-dimensional vertex value.
Definition: nurbsVertex.cxx:33
Indicates a coordinate-system transform on vertices.
PT(NurbsSurfaceResult) NurbsSurfaceEvaluator
Returns a NurbsSurfaceResult object that represents the result of applying the knots to all of the cu...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.