Panda3D
Loading...
Searching...
No Matches
cubicCurveseg.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 cubicCurveseg.cxx
10 * @author drose
11 * @date 2001-03-04
12 */
13
14#include "piecewiseCurve.h"
15
16#include "config_parametrics.h"
17#include "hermiteCurve.h"
18
19#include "datagram.h"
20#include "datagramIterator.h"
21#include "bamWriter.h"
22#include "bamReader.h"
23
24TypeHandle CubicCurveseg::_type_handle;
25
26/**
27 *
28 */
29CubicCurveseg::
30CubicCurveseg() {
31}
32
33/**
34 * Creates the curveseg given the four basis vectors (the columns of the
35 * matrix) explicitly.
36 */
37CubicCurveseg::
38CubicCurveseg(const LMatrix4 &basis) {
39 Bx = basis.get_col(0);
40 By = basis.get_col(1);
41 Bz = basis.get_col(2);
42 Bw = basis.get_col(3);
43 rational = true;
44}
45
46
47/**
48 * Creates the curveseg as a Bezier segment.
49 */
50CubicCurveseg::
51CubicCurveseg(const BezierSeg &seg) {
52 bezier_basis(seg);
53}
54
55
56/**
57 * Creates the curveseg as a NURBS segment. See nurbs_basis for a description
58 * of the parameters.
59 */
60CubicCurveseg::
61CubicCurveseg(int order, const PN_stdfloat knots[], const LVecBase4 cvs[]) {
62 nurbs_basis(order, knots, cvs);
63}
64
65/**
66 *
67 */
68CubicCurveseg::
69~CubicCurveseg() {
70}
71
72
73
74/**
75 * Computes the surface point at a given parametric point t.
76 */
78get_point(PN_stdfloat t, LVecBase3 &point) const {
79 PN_stdfloat t_sqrd = t*t;
80 evaluate_point(LVecBase4(t*t_sqrd, t_sqrd, t, 1.0f), point);
81 return true;
82}
83
84/**
85 * Computes the surface tangent at a given parametric point t.
86 */
88get_tangent(PN_stdfloat t, LVecBase3 &tangent) const {
89 evaluate_vector(LVecBase4(3.0f*t*t, 2.0f*t, 1.0f, 0.0f), tangent);
90 return true;
91}
92
93/**
94 * Simultaneously computes the point and the tangent at the given parametric
95 * point.
96 */
98get_pt(PN_stdfloat t, LVecBase3 &point, LVecBase3 &tangent) const {
99 PN_stdfloat t_sqrd=t*t;
100 evaluate_point(LVecBase4(t*t_sqrd, t_sqrd, t, 1.0f), point);
101 evaluate_vector(LVecBase4(3.0f*t_sqrd, /*2.0f*t*/t+t, 1.0f, 0.0f), tangent);
102 return true;
103}
104
105/**
106 * Computes the surface 2nd-order tangent at a given parametric point t.
107 */
109get_2ndtangent(PN_stdfloat t, LVecBase3 &tangent2) const {
110 evaluate_vector(LVecBase4(6.0f*t, 2.0f, 0.0f, 0.0f), tangent2);
111 return true;
112}
113
114
115/**
116 * Defines the curve segment as a Hermite. This only sets up the basis
117 * vectors, so the curve will be computed correctly; it does not retain the
118 * CV's.
119 */
122 const HermiteCurveCV &cv1,
123 PN_stdfloat tlength) {
124 static LMatrix4
125 Mh( 2.0f, -3.0f, 0.0f, 1.0f,
126 -2.0f, 3.0f, 0.0f, 0.0f,
127 1.0f, -2.0f, 1.0f, 0.0f,
128 1.0f, -1.0f, 0.0f, 0.0f);
129
130 LVecBase4 Gx(cv0._p[0], cv1._p[0],
131 cv0._out[0]*tlength, cv1._in[0]*tlength);
132 LVecBase4 Gy(cv0._p[1], cv1._p[1],
133 cv0._out[1]*tlength, cv1._in[1]*tlength);
134 LVecBase4 Gz(cv0._p[2], cv1._p[2],
135 cv0._out[2]*tlength, cv1._in[2]*tlength);
136
137 Bx = Gx * Mh;
138 By = Gy * Mh;
139 Bz = Gz * Mh;
140 rational = false;
141}
142
143/**
144 * Defines the curve segment as a Bezier. This only sets up the basis
145 * vectors, so the curve will be computed correctly; it does not retain the
146 * CV's.
147 */
149bezier_basis(const BezierSeg &seg) {
150 static LMatrix4
151 Mb(-1.0f, 3.0f, -3.0f, 1.0f,
152 3.0f, -6.0f, 3.0f, 0.0f,
153 -3.0f, 3.0f, 0.0f, 0.0f,
154 1.0f, 0.0f, 0.0f, 0.0f);
155
156 LVecBase4 Gx(seg._v[0][0], seg._v[1][0], seg._v[2][0], seg._v[3][0]);
157 LVecBase4 Gy(seg._v[0][1], seg._v[1][1], seg._v[2][1], seg._v[3][1]);
158 LVecBase4 Gz(seg._v[0][2], seg._v[1][2], seg._v[2][2], seg._v[3][2]);
159
160 Bx = Gx * Mb;
161 By = Gy * Mb;
162 Bz = Gz * Mb;
163 rational = false;
164}
165
166static LVecBase4
167nurbs_blending_function(int order, int i, int j,
168 const PN_stdfloat knots[]) {
169 // This is doubly recursive. Ick.
170 LVecBase4 r;
171
172 if (j==1) {
173 if (i==order-1 && knots[i] < knots[i+1]) {
174 r.set(0.0f, 0.0f, 0.0f, 1.0f);
175 } else {
176 r.set(0.0f, 0.0f, 0.0f, 0.0f);
177 }
178
179 } else {
180 LVecBase4 bi0 = nurbs_blending_function(order, i, j-1, knots);
181 LVecBase4 bi1 = nurbs_blending_function(order, i+1, j-1, knots);
182
183 PN_stdfloat d0 = knots[i+j-1] - knots[i];
184 PN_stdfloat d1 = knots[i+j] - knots[i+1];
185
186 // First term. Division by zero is defined to equal zero.
187 if (d0 != 0.0f) {
188 if (d1 != 0.0f) {
189 r = bi0 / d0 - bi1 / d1;
190 } else {
191 r = bi0 / d0;
192 }
193
194 } else if (d1 != 0.0f) {
195 r = - bi1 / d1;
196
197 } else {
198 r.set(0.0f, 0.0f, 0.0f, 0.0f);
199 }
200
201 // scale by t.
202 r[0] = r[1];
203 r[1] = r[2];
204 r[2] = r[3];
205 r[3] = 0.0f;
206
207 // Second term.
208 if (d0 != 0.0f) {
209 if (d1 != 0.0f) {
210 r += bi0 * (- knots[i] / d0) + bi1 * (knots[i+j] / d1);
211 } else {
212 r += bi0 * (- knots[i] / d0);
213 }
214
215 } else if (d1 != 0.0f) {
216 r += bi1 * (knots[i+j] / d1);
217 }
218 }
219
220 return r;
221}
222
223void
224compute_nurbs_basis(int order,
225 const PN_stdfloat knots_in[],
226 LMatrix4 &basis) {
227 int i;
228
229 // Scale the supplied knots to the range 0..1.
230 PN_stdfloat knots[8];
231 PN_stdfloat mink = knots_in[order-1];
232 PN_stdfloat maxk = knots_in[order];
233
234 if (mink==maxk) {
235 // Huh. What were you thinking? This is a trivial NURBS.
236 parametrics_cat->warning()
237 << "Trivial NURBS curve specified." << std::endl;
238 memset((void *)&basis, 0, sizeof(LMatrix4));
239 return;
240 }
241
242 for (i = 0; i<2*order; i++) {
243 knots[i] = (knots_in[i] - mink) / (maxk-mink);
244 }
245
246
247 LVecBase4 b[4];
248 for (i = 0; i<order; i++) {
249 b[i] = nurbs_blending_function(order, i, order, knots);
250 }
251
252 for (i = 0; i<order; i++) {
253 basis.set_row(i, b[i]);
254 }
255
256 for (i=order; i<4; i++) {
257 basis.set_row(i, LVecBase4::zero());
258 }
259}
260
261
262
263/**
264 * Defines the curve segment as a NURBS. Order is one more than the degree,
265 * and must be 1, 2, 3, or 4; knots is an array of order*2 values, and cvs is
266 * an array of order values.
267 */
269nurbs_basis(int order, const PN_stdfloat knots[], const LVecBase4 cvs[]) {
270 assert(order>=1 && order<=4);
271
272 LMatrix4 B;
273 compute_nurbs_basis(order, knots, B);
274
275 // Create a local copy of our CV's, so we can zero out the unused elements.
276 LVecBase4 c[4];
277 for (int i = 0; i < 4; i++) {
278 c[i] = (i<order) ? cvs[i] : LVecBase4(0.0f, 0.0f, 0.0f, 0.0f);
279 }
280
281 Bx = LVecBase4(c[0][0], c[1][0], c[2][0], c[3][0]) * B;
282 By = LVecBase4(c[0][1], c[1][1], c[2][1], c[3][1]) * B;
283 Bz = LVecBase4(c[0][2], c[1][2], c[2][2], c[3][2]) * B;
284 Bw = LVecBase4(c[0][3], c[1][3], c[2][3], c[3][3]) * B;
285
286 rational = true;
287}
288
289/**
290 * Fills the BezierSeg structure with a description of the curve segment as a
291 * Bezier, if possible, but does not change the _t member of the structure.
292 * Returns true if successful, false otherwise.
293 */
295get_bezier_seg(BezierSeg &seg) const {
296 static LMatrix4
297 Mbi(0.0f, 0.0f, 0.0f, 1.0f,
298 0.0f, 0.0f, 1.0f/3.0f, 1.0f,
299 0.0f, 1.0f/3.0f, 2.0f/3.0f, 1.0f,
300 1.0f, 1.0f, 1.0f, 1.0f);
301
302 LVecBase4 Gx = Bx * Mbi;
303 LVecBase4 Gy = By * Mbi;
304 LVecBase4 Gz = Bz * Mbi;
305
306 if (rational) {
307 LVecBase4 Gw = Bw * Mbi;
308 seg._v[0].set(Gx[0]/Gw[0], Gy[0]/Gw[0], Gz[0]/Gw[0]);
309 seg._v[1].set(Gx[1]/Gw[1], Gy[1]/Gw[1], Gz[1]/Gw[1]);
310 seg._v[2].set(Gx[2]/Gw[2], Gy[2]/Gw[2], Gz[2]/Gw[2]);
311 seg._v[3].set(Gx[3]/Gw[3], Gy[3]/Gw[3], Gz[3]/Gw[3]);
312 } else {
313 seg._v[0].set(Gx[0], Gy[0], Gz[0]);
314 seg._v[1].set(Gx[1], Gy[1], Gz[1]);
315 seg._v[2].set(Gx[2], Gy[2], Gz[2]);
316 seg._v[3].set(Gx[3], Gy[3], Gz[3]);
317 }
318
319 return true;
320}
321
322// We need this operator since Performer didn't supply it.
323inline LVecBase4
324col_mult(const LMatrix4 &M, const LVecBase4 &v) {
325 return LVecBase4(M(0,0)*v[0] + M(0,1)*v[1] + M(0,2)*v[2] + M(0,3)*v[3],
326 M(1,0)*v[0] + M(1,1)*v[1] + M(1,2)*v[2] + M(1,3)*v[3],
327 M(2,0)*v[0] + M(2,1)*v[1] + M(2,2)*v[2] + M(2,3)*v[3],
328 M(3,0)*v[0] + M(3,1)*v[1] + M(3,2)*v[2] + M(3,3)*v[3]);
329}
330
331/**
332 * Interprets the parameters for a particular column of compute_seg. Builds
333 * the indicated column of T and P.
334 */
335static bool
336compute_seg_col(int c,
337 int rtype, PN_stdfloat t, const LVecBase4 &v,
338 const LMatrix4 &B,
339 const LMatrix4 &Bi,
340 const LMatrix4 &G,
341 const LMatrix4 &GB,
342 LMatrix4 &T, LMatrix4 &P) {
343 bool keep_orig = ((rtype & RT_KEEP_ORIG) != 0);
344
345 if (parametrics_cat.is_debug()) {
346 parametrics_cat.debug()
347 << "Computing col " << c << " type " << (rtype & RT_BASE_TYPE)
348 << " at " << t << " keep_orig = " << keep_orig
349 << " v = " << v << "\n";
350 }
351
352 switch (rtype & RT_BASE_TYPE) {
353 // RT_point defines the point on the curve at t. This is the vector [ t^3
354 // t^2 t^1 t^0 ].
355 PN_stdfloat t_sqrd,t_cubed;
356
357 case RT_POINT:
358 t_sqrd = t*t;
359 t_cubed = t_sqrd*t;
360 T.set_col(c, LVecBase4(t_cubed, t_sqrd, t, 1.0f));
361 if (keep_orig) {
362 LVecBase4 vec(t_cubed, t_sqrd, t, 1.0f);
363 LVecBase4 ov = col_mult(GB, vec);
364 if (parametrics_cat.is_debug()) {
365 parametrics_cat.debug()
366 << "orig point = " << ov << "\n";
367 }
368 P.set_col(c, ov);
369 } else {
370 P.set_col(c, v);
371 }
372 break;
373
374 // RT_tangent defines the tangent to the curve at t. This is the vector [
375 // 3t^2 2t 1 0 ].
376 case RT_TANGENT:
377 t_sqrd = t*t;
378 T.set_col(c, LVecBase4(3.0f*t_sqrd, t+t, 1.0f, 0.0f));
379 if (keep_orig) {
380 LVecBase4 vec(3.0f*t_sqrd, /*2.0f*t*/t+t, 1.0f, 0.0f);
381 LVecBase4 ov = col_mult(GB, vec);
382 if (parametrics_cat.is_debug()) {
383 parametrics_cat.debug()
384 << "Matrix is:\n";
385 GB.write(parametrics_cat.debug(false), 2);
386 parametrics_cat.debug(false)
387 << "vector is " << vec << "\n"
388 << "orig tangent = " << ov << "\n";
389 }
390 P.set_col(c, ov);
391 } else {
392 P.set_col(c, v);
393 }
394 break;
395
396 // RT_cv defines the cth control point. This is the cth column vector
397 // from Bi.
398 case RT_CV:
399 T.set_col(c, Bi.get_col(c));
400 if (keep_orig) {
401 if (parametrics_cat.is_debug()) {
402 parametrics_cat.debug()
403 << "orig CV = " << G.get_col(c) << "\n";
404 }
405 P.set_col(c, G.get_col(c));
406 } else {
407 P.set_col(c, v);
408 }
409 break;
410
411 default:
412 std::cerr << "Invalid rebuild type in compute_seg\n";
413 return false;
414 }
415
416 return true;
417}
418
419/**
420 * Given a set of four properties of a curve segment (e.g. four points, four
421 * tangent values, four control points, or any combination), and a basis
422 * matrix, computes the corresponding geometry matrix that (together with the
423 * basis matrix) represents the curve that satisfies the four properties.
424 *
425 * The basis matrix is passed in as B, and its inverse must be precomputed and
426 * passed in as Bi.
427 *
428 * The result is returned in the matrix G, each column of which represents the
429 * cth control vertex. If any of the four properties has RT_KEEP_ORIG set
430 * (see below), G's input value is used to define the original shape of the
431 * curve; otherwise, G's input value is ignored.
432 *
433 * Each property is defined by an rtype, which may be any of RT_POINT,
434 * RT_TANGENT, or RT_CV, and may or may not be or'ed with RT_KEEP_ORIG. The
435 * meanings of the types are as follows:
436 *
437 * RT_POINT defines a specific point which the curve segment must pass
438 * through. t is in the range [0,1] and represents the parametric value at
439 * which the curve segment will intersect the given point. If RT_KEEP_ORIG is
440 * not set, v defines the point; otherwise, v is ignored and the original
441 * curve at point t defines the point.
442 *
443 * RT_TANGENT defines a specific tangent value which the curve segment must
444 * have at point t. As with RT_POINT, if RT_KEEP_ORIG is not set, v defines
445 * the tangent; otherwise, v is ignored and the original curve defines the
446 * tangent.
447 *
448 * RT_CV defines a specific control vertex which the curve segment must have.
449 * In this case, t is ignored. The position within the argument list
450 * determines which control vertex is applicable; e.g. rtype0 = RT_CV defines
451 * control vertex 0, and rtype2 = RT_CV defines control vertex 2. If
452 * RT_KEEP_ORIG is not set, v defines the new control vertex; otherwise, the
453 * control vertex is taken from G.
454 *
455 * The return value is true if all the parameters are sensible, or false if
456 * there is some error.
457 */
459compute_seg(int rtype0, PN_stdfloat t0, const LVecBase4 &v0,
460 int rtype1, PN_stdfloat t1, const LVecBase4 &v1,
461 int rtype2, PN_stdfloat t2, const LVecBase4 &v2,
462 int rtype3, PN_stdfloat t3, const LVecBase4 &v3,
463 const LMatrix4 &B,
464 const LMatrix4 &Bi,
465 LMatrix4 &G) {
466
467 // We can define a cubic curve segment given four arbitrary properties of
468 // the segment: any point along the curve, any tangent along the curve, any
469 // control point. Given any four such properties, a single cubic curve
470 // segment is defined.
471
472 // For a given cubic curve segment so defined, and given a basis matrix B,
473 // we can define the four control vertices that represent the segment with
474 // the basis matrix. That is, we can define the matrix G such that G * B *
475 // tc, where tc is [ t^3 t^2 t^1 t^0 ] for t in [ 0..1 ], represents the
476 // point on the curve segment corresponding to t.
477
478 // First, we build a matrix T, such that each of the four columns of T
479 // contains the vector that would compute the corresponding property. We
480 // also build a corresponding matrix P, such that each of its columns
481 // contains the vector that is the solution of the corresponding column in
482 // T.
483
484 LMatrix4 T, P, GB;
485
486 // GB is G * B, but we only need to compute this if any of the columns wants
487 // the value from the original G.
488 if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
489 GB = G * B;
490 }
491
492 if (! (compute_seg_col(0, rtype0, t0, v0, B, Bi, G, GB, T, P) &&
493 compute_seg_col(1, rtype1, t1, v1, B, Bi, G, GB, T, P) &&
494 compute_seg_col(2, rtype2, t2, v2, B, Bi, G, GB, T, P) &&
495 compute_seg_col(3, rtype3, t3, v3, B, Bi, G, GB, T, P))) {
496 return false;
497 }
498
499 LMatrix4 Ti;
500 Ti = invert(T);
501
502 // Now we have T and P such that P represents the solution of T, when T is
503 // applied to the geometry and basis matrices. That is, each column of P
504 // represents the solution computed by the corresponding column of T. P = G
505 // * B * T.
506
507 // We simply solve for G and get G = P * T^(-1) * B^(-1).
508
509 G = P * Ti * Bi;
510
511 return true;
512}
513
514/**
515 * Initializes the factory for reading these things from Bam files.
516 */
519 BamReader::get_factory()->register_factory(get_class_type(), make_CubicCurveseg);
520}
521
522/**
523 * Factory method to generate an object of this type.
524 */
525TypedWritable *CubicCurveseg::
526make_CubicCurveseg(const FactoryParams &params) {
528 DatagramIterator scan;
529 BamReader *manager;
530
531 parse_params(params, scan, manager);
532 me->fillin(scan, manager);
533 return me;
534}
535
536/**
537 * Function to write the important information in the particular object to a
538 * Datagram
539 */
540void CubicCurveseg::
541write_datagram(BamWriter *manager, Datagram &me) {
542 ParametricCurve::write_datagram(manager, me);
543
544 Bx.write_datagram(me);
545 By.write_datagram(me);
546 Bz.write_datagram(me);
547 Bw.write_datagram(me);
548 me.add_bool(rational);
549}
550
551/**
552 * Function that reads out of the datagram (or asks manager to read) all of
553 * the data that is needed to re-create this object and stores it in the
554 * appropiate place
555 */
556void CubicCurveseg::
557fillin(DatagramIterator &scan, BamReader *manager) {
558 ParametricCurve::fillin(scan, manager);
559
560 Bx.read_datagram(scan);
561 By.read_datagram(scan);
562 Bz.read_datagram(scan);
563 Bw.read_datagram(scan);
564 rational = scan.get_bool();
565}
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,...
void bezier_basis(const BezierSeg &seg)
Defines the curve segment as a Bezier.
virtual bool get_point(PN_stdfloat t, LVecBase3 &point) const
Computes the surface point at a given parametric point t.
virtual bool get_pt(PN_stdfloat t, LVecBase3 &point, LVecBase3 &tangent) const
Simultaneously computes the point and the tangent at the given parametric point.
virtual bool get_tangent(PN_stdfloat t, LVecBase3 &tangent) const
Computes the surface tangent at a given parametric point t.
void hermite_basis(const HermiteCurveCV &cv0, const HermiteCurveCV &cv1, PN_stdfloat tlength=1.0f)
Defines the curve segment as a Hermite.
static void register_with_read_factory()
Initializes the factory for reading these things from Bam files.
virtual bool get_bezier_seg(BezierSeg &seg) const
Fills the BezierSeg structure with a description of the curve segment as a Bezier,...
void nurbs_basis(int order, const PN_stdfloat knots[], const LVecBase4 cvs[])
Defines the curve segment as a NURBS.
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.
virtual bool get_2ndtangent(PN_stdfloat t, LVecBase3 &tangent2) const
Computes the surface 2nd-order tangent at a given parametric point t.
A class to retrieve the individual data elements previously stored in a Datagram.
bool get_bool()
Extracts a boolean value.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition datagram.I:34
An instance of this class is passed to the Factory when requesting it to do its business and construc...
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
A single CV of a Hermite curve.
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.
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.