/* Last edited on 2008-02-08 14:06:37 by stolfi */ #include #include #include #include #include #include #include void pz_geo_debug(char *msg, double_vec_t *x); r3_t pz_linear_interpolate ( double t, double a, r3_t *pa, double b, r3_t *pb ) { double Ca, Cb; if ((b - a) != 0.0) { Ca = (b - t) / (b - a); Cb = 1.00 - Ca; } else { Ca = 0.5; Cb = 0.5; } return (r3_t){{ Ca * pa->c[0] + Cb * pb->c[0], Ca * pa->c[1] + Cb * pb->c[1], Ca * pa->c[2] + Cb * pb->c[2] }}; } void pz_hermite_interpolate ( double t, double a, r3_t *pa, r3_t *va, double b, r3_t *pb, r3_t *vb, r3_t *p, r3_t *v ) { affirm(a <= b, "arg order"); /* Test for degenerate intervals: If the interval has zero width, return the mean of the positions (which is an OK result) and the mean of the velocities (which is almost certainly wrong). */ if (b - a == 0.0) { /* Interval is degenerate: */ r3_mix(0.5, pa, 0.5, pb, p); r3_mix(0.5, va, 0.5, vb, v); } else { double tab = b - a; double tax = t - a; double txb = b - t; double rax = tax/tab; double rxb = txb/tab; double Cpa = rxb * rxb * (3.0 * rax + rxb); double Dpa = - 6.0 * rax * rxb / tab; double Cva = + rax * rxb * txb; double Dva = + rxb * (rxb - 2.0 * rax); double Cvb = - rax * rxb * tax; double Dvb = + rax * (rax - 2.0 * rxb); double Cpb = rax * rax * (rax + 3.0 * rxb); double Dpb = + 6.0 * rax * rxb / tab; *p = (r3_t){{ Cpa*pa->c[0] + Cva*va->c[0] + Cvb*vb->c[0] + Cpb*pb->c[0], Cpa*pa->c[1] + Cva*va->c[1] + Cvb*vb->c[1] + Cpb*pb->c[1], Cpa*pa->c[2] + Cva*va->c[2] + Cvb*vb->c[2] + Cpb*pb->c[2] }}; *v = (r3_t){{ Dpa*pa->c[0] + Dva*va->c[0] + Dvb*vb->c[0] + Dpb*pb->c[0], Dpa*pa->c[1] + Dva*va->c[1] + Dvb*vb->c[1] + Dpb*pb->c[1], Dpa*pa->c[2] + Dva*va->c[2] + Dvb*vb->c[2] + Dpb*pb->c[2] }}; } } r3_t pz_estimate_velocity_Q ( double a, r3_t *pa, double b, r3_t *pb, double c, r3_t *pc ) { affirm((a <= b) && (b <= c), "arg order"); /* Test for degenerate intervals: If intervals are degenerate, use linear interpolation, just to return something (but it will be almost surely wrong, and discontinuous). */ double eps = 1.0e-7*(c - a); if (eps == 0.0) { /* Whole interval is degenerate: */ return (r3_t){{0.0, 0.0, 0.0}}; } else if ((b-a) < eps) { /* Left interval is degenerate; */ r3_t pbc; r3_sub(pc, pb, &pbc); r3_scale(1.0/(c-b), &pbc, &pbc); return pbc; } else if ((c-b) < eps) { /* Right interval is degenerate; */ r3_t pab; r3_sub(pb, pa, &pab); r3_scale(1.0/(b-a), &pab, &pab); return pab; } else { double fac = 1.0/(c - a); double fab = 1.0/(b - a); double fbc = 1.0/(c - b); double Ca = fac - fab; double Cb = fab - fbc; double Cc = fbc - fac; return (r3_t){{ Ca*pa->c[0] + Cb*pb->c[0] + Cc*pc->c[0], Ca*pa->c[1] + Cb*pb->c[1] + Cc*pc->c[1], Ca*pa->c[2] + Cb*pb->c[2] + Cc*pc->c[2] }}; } } r3_t pz_estimate_velocity_L ( double a, r3_t *pa, double b, r3_t *pb, double c, r3_t *pc ) { affirm((a <= b) && (b <= c), "arg order"); /* Test for degenerate intervals: If whole interval is degenerate, return 0 (which is almost surely wrong). */ if (c-a == 0.0) { return (r3_t){{0.0, 0.0, 0.0}}; } else { double Ca = 1.0/(c - a); return (r3_t){{ Ca*(pc->c[0] - pa->c[0]), Ca*(pc->c[1] - pa->c[1]), Ca*(pc->c[2] - pa->c[2]) }}; } } r3_t pz_estimate_velocity_C ( double a, r3_t *pa, double b, r3_t *pb, double c, r3_t *pc ) { affirm((a <= b) && (b <= c), "arg order"); /* Test for degenerate intervals: */ /* If whole interval is degenerate, return 0 (which is almost surely wrong). */ if (c - a == 0.0) { /* Whole interval is degenerate: */ return (r3_t){{0.0, 0.0, 0.0}}; } else { double tab = b - a; double tbc = c - b; double den = tab*tab + tbc*tbc; double Ca = - tab/den; double Cb = (tab - tbc)/den; double Cc = + tbc/den; affirm(den > 0.0, "bug"); return (r3_t){{ Ca*pa->c[0] + Cb*pb->c[0] + Cc*pc->c[0], Ca*pa->c[1] + Cb*pb->c[1] + Cc*pc->c[1], Ca*pa->c[2] + Cb*pb->c[2] + Cc*pc->c[2] }}; } } double pz_hermite_curve_length ( double a, r3_t *pa, r3_t *va, double b, r3_t *pb, r3_t *vb ) { /* Shift time axis so that midpoint of {b} and {c} is zero: */ { double m = 0.5*(a+b); a = a - m; b = b - m; } /* In case of degenerate interval, assume straight line: */ affirm(a <= b, "arg order"); if (b-a == 0.0) { return r3_dist(pa, pb); } else { double ab = b - a; /* Compute the derivative of the Hermite interpolant. Let the Hermite interpolant be {h(u) = h3*u^3 + ... + h0}; where {u = 2*(t - a)/(b-a)-1} ranges from -1 to +1. Note that {dh/du(-1) = va*(b-a)/2}, and {dh/du(1) = vb*(b-a)/2}. Denoting {b-a} by {ab}, the polynomial {h} is then | { h(u) = | | + pa*(+u^3 - 3*u + 2)/4 | + pb*(-u^3 + 3*u + 2)/4 | + va*ab*(u^3 - u^2 - u + 1)/8 | + vb*ab*(u^3 + u^2 - u - 1)/8 | = | + (1/4*(pa-pb) + 1/8*(va+vb)*ab)*u^3 | + ((vb-va)*ab/8)*u^2 | + (3/4*(pb-pa) - 1/8*(va+vb)*ab)*u | + (1/2*(pa+pb) + 1/8*(va-vb)*ab | } Its derivative with respect to {u} is then | { dh/du(u) = | + (3/4*(pa-pb)+3/8*(va+vb)*ab) * u^2 | + (1/4*(vb-va)*ab) * u | + (3/4*(pb-pa) - 1/8*(va+vb)*ab) | } Note that the curve depends only on the products {va*ab} and {vb*ab}, not on {va}, {vb}, or {ab} separately. */ double dxdu_2 = 0.750*(pa->c[0]-pb->c[0]) + 0.375*(va->c[0]+vb->c[0])*ab; double dydu_2 = 0.750*(pa->c[1]-pb->c[1]) + 0.375*(va->c[1]+vb->c[1])*ab; double dzdu_2 = 0.750*(pa->c[2]-pb->c[2]) + 0.375*(va->c[2]+vb->c[2])*ab; double dxdu_1 = 0.250*(vb->c[0] - va->c[0])*ab; double dydu_1 = 0.250*(vb->c[1] - va->c[1])*ab; double dzdu_1 = 0.250*(vb->c[2] - va->c[2])*ab; double dxdu_0 = 0.750*(pb->c[0]-pa->c[0]) - 0.125*(va->c[0]+vb->c[0])*ab; double dydu_0 = 0.750*(pb->c[1]-pa->c[1]) - 0.125*(va->c[1]+vb->c[1])*ab; double dzdu_0 = 0.750*(pb->c[2]-pa->c[2]) - 0.125*(va->c[2]+vb->c[2])*ab; /* Computes the speed squared {|dh/du|^2}, as a function of {u}. We only need the even powers: */ double s_4 = dxdu_2*dxdu_2 + dydu_2*dydu_2 + dzdu_2*dzdu_2; double s_2 = 2.0 * (dxdu_2*dxdu_0 + dydu_2*dydu_0 + dzdu_2*dzdu_0) + (dxdu_1*dxdu_1 + dydu_1*dydu_1 + dzdu_1*dzdu_1); double s_0 = dxdu_0*dxdu_0 + dydu_0*dydu_0 + dzdu_0*dzdu_0; /* Computes the approximate square root of the polynomial Assumes the constant term {s0} dominates, so {sqrt(U + s_0) ~ sqrt(s_0) + U/sqrt(s_0)/2} */ double r_0 = sqrt(s_0 + 1.0e-50); double r_2 = 0.5 * s_2/r_0; double r_4 = 0.5 * s_4/r_0; /* Integrates the speed from -1 to +1 to get the length: */ double length = 2.0*(r_4/5.0 + r_2/3.0 + r_0); return length; } } void pz_geo_debug(char *msg, double_vec_t *x) { fprintf(stderr, msg); int i; for (i = 0; i < x->nel; i++) { fprintf(stderr, " %12.8f", x->el[i]); } fprintf(stderr, "\n"); } r3_t pz_B_spline_approximation ( double t, double a, double b, r3_t *Pabc, double c, r3_t *Pbcd, double d, r3_t *Pcde, double e, r3_t *Pdef, double f ) { r3_t Ptbc = pz_linear_interpolate(t, a, Pabc, d, Pbcd); r3_t Ptcd = pz_linear_interpolate(t, b, Pbcd, e, Pcde); r3_t Ptde = pz_linear_interpolate(t, c, Pcde, f, Pdef); r3_t Pttc = pz_linear_interpolate(t, b, &Ptbc, d, &Ptcd); r3_t Pttd = pz_linear_interpolate(t, c, &Ptcd, e, &Ptde); r3_t Pttt = pz_linear_interpolate(t, c, &Pttc, d, &Pttd); return Pttt; } double pz_avg_seg_dist ( r3_t *a1, r3_t *a2, r3_t *b1, r3_t *b2 ) { return sqrt(pz_avg_seg_dist_sqr(a1, a2, b1, b2)); } double pz_avg_seg_dist_sqr ( r3_t *a1, r3_t *a2, r3_t *b1, r3_t *b2 ) { r3_t d1; r3_sub(a1, b1, &d1); r3_t d2; r3_sub(a2, b2, &d2); double d = ( d1.c[0]*d1.c[0] + d1.c[0]*d2.c[0] + d2.c[0]*d2.c[0] + d1.c[1]*d1.c[1] + d1.c[1]*d2.c[1] + d2.c[1]*d2.c[1] + d1.c[2]*d1.c[2] + d1.c[2]*d2.c[2] + d2.c[2]*d2.c[2] )/3.0; return d; } r3_t pz_seg_dir(r3_t *p, r3_t *q) { r3_t r; r.c[0] = q->c[0] - p->c[0]; r.c[1] = q->c[1] - p->c[1]; r.c[2] = q->c[2] - p->c[2]; r3_dir(&r, &r); return r; } r3_t pz_seg_mid(r3_t *p, r3_t *q) { r3_t o; int i; for (i = 0; i < 3; i++) { o.c[i] = (p->c[i] + q->c[i]) / 2.0; } return o; }