Panda3D
nurbsSurfaceResult.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 nurbsSurfaceResult.cxx
10  * @author drose
11  * @date 2003-10-10
12  */
13 
14 #include "nurbsSurfaceResult.h"
15 #include "nurbsVertex.h"
16 
17 
18 /**
19  * The constructor automatically builds up the result as the product of the
20  * indicated set of basis matrices and the indicated table of control vertex
21  * positions.
22  */
25  const NurbsBasisVector &v_basis,
26  const LVecBase4 vecs[], const NurbsVertex *verts,
27  int num_u_vertices, int num_v_vertices) :
28  _u_basis(u_basis),
29  _v_basis(v_basis),
30  _verts(verts),
31  _num_u_vertices(num_u_vertices),
32  _num_v_vertices(num_v_vertices)
33 {
34  // The V basis matrices will always be transposed.
35  _v_basis.transpose();
36 
37  _last_u_segment = -1;
38  _last_v_segment = -1;
39  int u_order = _u_basis.get_order();
40  int v_order = _v_basis.get_order();
41  int num_u_segments = _u_basis.get_num_segments();
42  int num_v_segments = _v_basis.get_num_segments();
43  int num_segments = num_u_segments * num_v_segments;
44 
45  _composed.reserve(num_segments);
46  for (int i = 0; i < num_segments; i++) {
47  _composed.push_back(ComposedMats());
48  }
49 
50  for (int vi = 0; vi < num_v_segments; vi++) {
51  const LMatrix4 &v_basis_transpose = _v_basis.get_basis(vi);
52 
53  int vn = _v_basis.get_vertex_index(vi);
54  nassertv(vn >= 0 && vn + v_order - 1 < _num_v_vertices);
55 
56  for (int ui = 0; ui < num_u_segments; ui++) {
57  const LMatrix4 &u_basis_mat = _u_basis.get_basis(ui);
58 
59  int un = _u_basis.get_vertex_index(ui);
60  nassertv(un >= 0 && un + u_order - 1 < _num_u_vertices);
61 
62  // Create four geometry matrices from our (up to) sixteen involved
63  // vertices.
64  LMatrix4 geom_x, geom_y, geom_z, geom_w;
65  geom_x.fill(0);
66  geom_y.fill(0);
67  geom_z.fill(0);
68  geom_w.fill(0);
69 
70  for (int uni = 0; uni < 4; uni++) {
71  for (int vni = 0; vni < 4; vni++) {
72  if (uni < u_order && vni < v_order) {
73  const LVecBase4 &vec = vecs[verti(un + uni, vn + vni)];
74  geom_x(uni, vni) = vec[0];
75  geom_y(uni, vni) = vec[1];
76  geom_z(uni, vni) = vec[2];
77  geom_w(uni, vni) = vec[3];
78  }
79  }
80  }
81 
82  // And compose these geometry matrices with the basis matrices to
83  // produce a new set of matrices, which will be used to evaluate the
84  // surface.
85  int i = segi(ui, vi);
86  nassertv(i >= 0 && i < (int)_composed.size());
87  ComposedMats &result = _composed[i];
88  result._x = u_basis_mat * geom_x * v_basis_transpose;
89  result._y = u_basis_mat * geom_y * v_basis_transpose;
90  result._z = u_basis_mat * geom_z * v_basis_transpose;
91  result._w = u_basis_mat * geom_w * v_basis_transpose;
92  }
93  }
94 }
95 
96 /**
97  * Evaluates the point on the surface corresponding to the indicated value in
98  * parametric time within the indicated surface segment. u and v should be in
99  * the range [0, 1].
100  *
101  * The surface is internally represented as a number of connected (or possibly
102  * unconnected) piecewise continuous segments. The exact number of segments
103  * for a particular surface depends on the knot vector, and is returned by
104  * get_num_segments(). Normally, eval_point() is used to evaluate a point
105  * along the continuous surface, but when you care more about local
106  * continuity, you can use eval_segment_point() to evaluate the points along
107  * each segment.
108  */
110 eval_segment_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &point) const {
111  int i = segi(ui, vi);
112  nassertv(i >= 0 && i < (int)_composed.size());
113 
114  PN_stdfloat u2 = u*u;
115  LVecBase4 uvec(u*u2, u2, u, 1.0f);
116  PN_stdfloat v2 = v*v;
117  LVecBase4 vvec(v*v2, v2, v, 1.0f);
118 
119  PN_stdfloat weight = vvec.dot(uvec * _composed[i]._w);
120 
121  point.set(vvec.dot(uvec * _composed[i]._x) / weight,
122  vvec.dot(uvec * _composed[i]._y) / weight,
123  vvec.dot(uvec * _composed[i]._z) / weight);
124 }
125 
126 /**
127  * As eval_segment_point, but computes the normal to the surface at the
128  * indicated point. The normal vector will not necessarily be normalized, and
129  * could be zero.
130  */
132 eval_segment_normal(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &normal) const {
133  int i = segi(ui, vi);
134  nassertv(i >= 0 && i < (int)_composed.size());
135 
136  PN_stdfloat u2 = u*u;
137  LVecBase4 uvec(u*u2, u2, u, 1.0f);
138  LVecBase4 duvec(3.0f * u2, 2.0f * u, 1.0f, 0.0f);
139  PN_stdfloat v2 = v*v;
140  LVecBase4 vvec(v*v2, v2, v, 1.0f);
141  LVecBase4 dvvec(3.0f * v2, 2.0f * v, 1.0f, 0.0f);
142 
143  LVector3 utan(vvec.dot(duvec * _composed[i]._x),
144  vvec.dot(duvec * _composed[i]._y),
145  vvec.dot(duvec * _composed[i]._z));
146 
147  LVector3 vtan(dvvec.dot(uvec * _composed[i]._x),
148  dvvec.dot(uvec * _composed[i]._y),
149  dvvec.dot(uvec * _composed[i]._z));
150 
151  normal = utan.cross(vtan);
152 }
153 
154 /**
155  * Evaluates the surface in n-dimensional space according to the extended
156  * vertices associated with the surface in the indicated dimension.
157  */
158 PN_stdfloat NurbsSurfaceResult::
159 eval_segment_extended_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d) const {
160  int i = segi(ui, vi);
161  nassertr(i >= 0 && i < (int)_composed.size(), 0.0f);
162 
163  PN_stdfloat u2 = u*u;
164  LVecBase4 uvec(u*u2, u2, u, 1.0f);
165  PN_stdfloat v2 = v*v;
166  LVecBase4 vvec(v*v2, v2, v, 1.0f);
167 
168  PN_stdfloat weight = vvec.dot(uvec * _composed[i]._w);
169 
170  // Calculate the composition of the basis matrices and the geometry matrix
171  // on-the-fly.
172  const LMatrix4 &v_basis_transpose = _v_basis.get_basis(vi);
173  const LMatrix4 &u_basis_mat = _u_basis.get_basis(ui);
174  int u_order = _u_basis.get_order();
175  int v_order = _v_basis.get_order();
176 
177  int un = _u_basis.get_vertex_index(ui);
178  int vn = _v_basis.get_vertex_index(vi);
179 
180  LMatrix4 geom;
181  geom.fill(0);
182 
183  for (int uni = 0; uni < 4; uni++) {
184  for (int vni = 0; vni < 4; vni++) {
185  if (uni < u_order && vni < v_order) {
186  geom(uni, vni) = _verts[verti(un + uni, vn + vni)].get_extended_vertex(d);
187  }
188  }
189  }
190 
191  LMatrix4 composed = u_basis_mat * geom * v_basis_transpose;
192  return vvec.dot(uvec * composed) / weight;
193 }
194 
195 /**
196  * Simultaneously performs eval_extended_point on a contiguous sequence of
197  * dimensions. The dimensions evaluated are d through (d + num_values - 1);
198  * the results are filled into the num_values elements in the indicated result
199  * array.
200  */
202 eval_segment_extended_points(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d,
203  PN_stdfloat result[], int num_values) const {
204  int i = segi(ui, vi);
205  nassertv(i >= 0 && i < (int)_composed.size());
206 
207  PN_stdfloat u2 = u*u;
208  LVecBase4 uvec(u*u2, u2, u, 1.0f);
209  PN_stdfloat v2 = v*v;
210  LVecBase4 vvec(v*v2, v2, v, 1.0f);
211 
212  PN_stdfloat weight = vvec.dot(uvec * _composed[i]._w);
213 
214  // Calculate the composition of the basis matrices and the geometry matrix
215  // on-the-fly.
216  const LMatrix4 &v_basis_transpose = _v_basis.get_basis(vi);
217  const LMatrix4 &u_basis_mat = _u_basis.get_basis(ui);
218  int u_order = _u_basis.get_order();
219  int v_order = _v_basis.get_order();
220 
221  int un = _u_basis.get_vertex_index(ui);
222  int vn = _v_basis.get_vertex_index(vi);
223 
224  for (int n = 0; n < num_values; n++) {
225  LMatrix4 geom;
226  geom.fill(0);
227 
228  for (int uni = 0; uni < 4; uni++) {
229  for (int vni = 0; vni < 4; vni++) {
230  if (uni < u_order && vni < v_order) {
231  geom(uni, vni) =
232  _verts[verti(un + uni, vn + vni)].get_extended_vertex(d + n);
233  }
234  }
235  }
236 
237  LMatrix4 composed = u_basis_mat * geom * v_basis_transpose;
238  result[n] = vvec.dot(uvec * composed) / weight;
239  }
240 }
241 
242 /**
243  * Returns the index of the segment that contains the indicated value of t, or
244  * -1 if no segment contains this value.
245  */
246 int NurbsSurfaceResult::
247 find_u_segment(PN_stdfloat u) {
248  // Trivially check the endpoints of the surface.
249  if (u >= get_end_u()) {
250  return _u_basis.get_num_segments() - 1;
251  } else if (u <= get_start_u()) {
252  return 0;
253  }
254 
255  // Check the last segment we searched for. Often, two consecutive requests
256  // are for the same segment.
257  if (_last_u_segment != -1 && (u >= _last_u_from && u < _last_u_to)) {
258  return _last_u_segment;
259  }
260 
261  // Look for the segment the hard way.
262  int segment = r_find_u_segment(u, 0, _u_basis.get_num_segments() - 1);
263  if (segment != -1) {
264  _last_u_segment = segment;
265  _last_u_from = _u_basis.get_from(segment);
266  _last_u_to = _u_basis.get_to(segment);
267  }
268  return segment;
269 }
270 
271 /**
272  * Recursively searches for the segment that contains the indicated value of t
273  * by performing a binary search. This assumes the segments are stored in
274  * increasing order of t, and they don't overlap.
275  */
276 int NurbsSurfaceResult::
277 r_find_u_segment(PN_stdfloat u, int top, int bot) const {
278  if (bot < top) {
279  // Not found.
280  return -1;
281  }
282  int mid = (top + bot) / 2;
283  nassertr(mid >= 0 && mid < _u_basis.get_num_segments(), -1);
284 
285  PN_stdfloat from = _u_basis.get_from(mid);
286  PN_stdfloat to = _u_basis.get_to(mid);
287  if (from > u) {
288  // Too high, try lower.
289  return r_find_u_segment(u, top, mid - 1);
290 
291  } else if (to <= u) {
292  // Too low, try higher.
293  return r_find_u_segment(u, mid + 1, bot);
294 
295  } else {
296  // Here we are!
297  return mid;
298  }
299 }
300 
301 
302 /**
303  * Returns the index of the segment that contains the indicated value of t, or
304  * -1 if no segment contains this value.
305  */
306 int NurbsSurfaceResult::
307 find_v_segment(PN_stdfloat v) {
308  // Trivially check the endpoints of the surface.
309  if (v >= get_end_v()) {
310  return _v_basis.get_num_segments() - 1;
311  } else if (v <= get_start_v()) {
312  return 0;
313  }
314 
315  // Check the last segment we searched for. Often, two consecutive requests
316  // are for the same segment.
317  if (_last_v_segment != -1 && (v >= _last_v_from && v < _last_v_to)) {
318  return _last_v_segment;
319  }
320 
321  // Look for the segment the hard way.
322  int segment = r_find_v_segment(v, 0, _v_basis.get_num_segments() - 1);
323  if (segment != -1) {
324  _last_v_segment = segment;
325  _last_v_from = _v_basis.get_from(segment);
326  _last_v_to = _v_basis.get_to(segment);
327  }
328  return segment;
329 }
330 
331 /**
332  * Recursively searches for the segment that contains the indicated value of t
333  * by performing a binary search. This assumes the segments are stored in
334  * increasing order of t, and they don't overlap.
335  */
336 int NurbsSurfaceResult::
337 r_find_v_segment(PN_stdfloat v, int top, int bot) const {
338  if (bot < top) {
339  // Not found.
340  return -1;
341  }
342  int mid = (top + bot) / 2;
343  nassertr(mid >= 0 && mid < _v_basis.get_num_segments(), -1);
344 
345  PN_stdfloat from = _v_basis.get_from(mid);
346  PN_stdfloat to = _v_basis.get_to(mid);
347  if (from > v) {
348  // Too high, try lower.
349  return r_find_v_segment(v, top, mid - 1);
350 
351  } else if (to <= v) {
352  // Too low, try higher.
353  return r_find_v_segment(v, mid + 1, bot);
354 
355  } else {
356  // Here we are!
357  return mid;
358  }
359 }
NurbsSurfaceResult(const NurbsBasisVector &u_basis, const NurbsBasisVector &v_basis, const LVecBase4 vecs[], const NurbsVertex *verts, int num_u_vertices, int num_v_vertices)
The constructor automatically builds up the result as the product of the indicated set of basis matri...
const LMatrix4 & get_basis(int segment) const
Returns the basis matrix associated with the nth segment.
int get_num_segments() const
Returns the number of piecewise continuous segments in the curve.
void transpose()
Transposes the basis matrices stored in the vector.
PN_stdfloat get_from(int segment) const
Returns the t value of the beginning of this segment.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_vertex_index(int segment) const
Returns the vertex index of the nth segment.
PN_stdfloat get_start_v() const
Returns the first legal value of v on the surface.
PN_stdfloat get_to(int segment) const
Returns the t value of the end of this segment.
This represents a single control vertex in a NurbsEvaluator.
Definition: nurbsVertex.h:32
PN_stdfloat get_end_v() const
Returns the last legal value of v on the surface.
int get_order() const
Returns the order of the segments in the curve.
PN_stdfloat get_start_u() const
Returns the first legal value of u on the surface.
This encapsulates a series of matrices that are used to represent the sequential segments of a NurbsC...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PN_stdfloat get_end_u() const
Returns the last legal value of u on the surface.
void eval_segment_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &point) const
Evaluates the point on the surface corresponding to the indicated value in parametric time within the...
PN_stdfloat get_extended_vertex(int d) const
Returns an n-dimensional vertex value.
Definition: nurbsVertex.cxx:43
void eval_segment_normal(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &normal) const
As eval_segment_point, but computes the normal to the surface at the indicated point.
PN_stdfloat eval_segment_extended_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d) const
Evaluates the surface in n-dimensional space according to the extended vertices associated with the s...
void eval_segment_extended_points(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d, PN_stdfloat result[], int num_values) const
Simultaneously performs eval_extended_point on a contiguous sequence of dimensions.