int Lemon::Intersect(const Vector3d& P, const Vector3d& D, LEMON_INT *Intersection, TraceThreadData *Thread) const { int i = 0; DBL a, b, c[5], r[4]; DBL d; DBL R2,r2, Pz2, Dz2, PDz2, k1, k2, horizontal, vertical, OCSquared; int n; Vector3d Padj, Second_Center,Ipoint, INormal; Second_Center = Vector3d(0, 0, VerticalPosition); r2 = Sqr(inner_radius); if (HorizontalPosition < -Lemon_Tolerance ) { // use a torus Padj = P - Second_Center; R2 = Sqr(HorizontalPosition); Pz2 = Padj[Z] * Padj[Z]; Dz2 = D[Z] * D[Z]; PDz2 = Padj[Z] * D[Z]; k1 = Padj[X] * Padj[X] + Padj[Y] * Padj[Y] + Pz2 - R2 - r2; k2 = Padj[X] * D[X] + Padj[Y] * D[Y] + PDz2; // this is just like a big torus c[0] = 1.0; c[1] = 4.0 * k2; c[2] = 2.0 * (k1 + 2.0 * (k2 * k2 + R2 * Dz2)); c[3] = 4.0 * (k2 * k1 + 2.0 * R2 * PDz2); c[4] = k1 * k1 + 4.0 * R2 * (Pz2 - r2); n = Solve_Polynomial(4, c, r, Test_Flag(this, STURM_FLAG), ROOT_TOLERANCE, Thread->Stats()); while (n--) { // here we only keep the 'lemon' inside the torus // and dismiss the 'apple' // If you find a solution to resolve the rotation of // (x + r)^2 + z^2 = R^2 around z (so replacing x by sqrt(x^2+y^2)) // with something which is faster than a 4th degree polynome, // please feel welcome to update and share... Ipoint = P + r[n] * D; vertical = Ipoint[Z]; if ((vertical >= 0.0) && (vertical <= 1.0)) { horizontal = sqrt(Sqr(Ipoint[X]) + Sqr(Ipoint[Y])); OCSquared = Sqr((horizontal - HorizontalPosition)) + Sqr((vertical - VerticalPosition)); if (fabs(OCSquared - r2 ) < ROOT_TOLERANCE) { Intersection[i].d = r[n]; INormal = Ipoint; INormal[Z] -= VerticalPosition; INormal[X] -= (INormal[X] * HorizontalPosition / horizontal); INormal[Y] -= (INormal[Y] * HorizontalPosition / horizontal); INormal.normalize(); Intersection[i].n = INormal; ++i; } } } } else { // use a sphere, as the center is on the axis // keeping a torus would trigger a problem of self-coincidence surface DBL OCSquared, t_Closest_Approach, Half_Chord, t_Half_Chord_Squared; DBL Depth; Vector3d Origin_To_Center; Origin_To_Center = Second_Center - P; OCSquared = Origin_To_Center.lengthSqr(); t_Closest_Approach = dot(Origin_To_Center, D); if (!((OCSquared >= r2) && (t_Closest_Approach < EPSILON ))) { t_Half_Chord_Squared = r2 - OCSquared + Sqr(t_Closest_Approach); if (t_Half_Chord_Squared > EPSILON) { Half_Chord = sqrt(t_Half_Chord_Squared); // first intersection Depth = t_Closest_Approach - Half_Chord; if((Depth > DEPTH_TOLERANCE) && (Depth < MAX_DISTANCE)) { Ipoint = P + Depth * D; vertical = Ipoint[Z]; if ((vertical >= 0.0) && (vertical <= 1.0)) { Intersection[i].d = Depth; INormal = Ipoint; INormal[Z] -= VerticalPosition; INormal.normalize(); Intersection[i].n = INormal; ++i; } } // second intersection Depth = t_Closest_Approach + Half_Chord; if((Depth > DEPTH_TOLERANCE) && (Depth < MAX_DISTANCE)) { Ipoint = P + Depth * D; vertical = Ipoint[Z]; if ((vertical >= 0.0) && (vertical <= 1.0)) { Intersection[i].d = Depth; INormal = Ipoint; INormal[Z] -= VerticalPosition; INormal.normalize(); Intersection[i].n = INormal; ++i; } } } } } // intersection with apex and base disc if (Test_Flag(this, CLOSED_FLAG) && (fabs(D[Z]) > EPSILON)) { d = - P[Z] / D[Z]; a = (P[X] + d * D[X]); b = (P[Y] + d * D[Y]); if (((Sqr(a) + Sqr(b)) <= Sqr(base_radius)) && (d > Lemon_Tolerance) && (d < MAX_DISTANCE)) { Intersection[i].d = d ; Intersection[i].n = Vector3d(0, 0, -1); ++i; } d = (1.0 - P[Z]) / D[Z]; a = (P[X] + d * D[X]); b = (P[Y] + d * D[Y]); if (((Sqr(a) + Sqr(b)) <= Sqr(apex_radius)) && (d > Lemon_Tolerance) && (d < MAX_DISTANCE)) { Intersection[i].d = d ; Intersection[i].n = Vector3d(0, 0, 1); ++i; } } return (i); }
void Ovus::Intersect_Ovus_Spheres(const Vector3d& P, const Vector3d& D, DBL * Depth1, DBL * Depth2, DBL * Depth3, DBL * Depth4, DBL * Depth5, DBL * Depth6, TraceThreadData *Thread) const { DBL OCSquared, t_Closest_Approach, Half_Chord, t_Half_Chord_Squared; Vector3d Padj; Vector3d IPoint; DBL R2, r2, Py2, Dy2, PDy2, k1, k2, horizontal, vertical; DBL Rad1, Rad2; int n; int lcount=0; Vector3d Second_Center; DBL c[5], r[4]; *Depth1 = *Depth2 = *Depth3 = *Depth4 = *Depth5 = *Depth6 = -100; // TODO FIXME - magic value // no hit unless... Padj = -P; Rad1 = Sqr(BottomRadius); Rad2 = Sqr(TopRadius); OCSquared = Padj.lengthSqr(); t_Closest_Approach = dot(Padj, D); if ((OCSquared < Rad1) || (t_Closest_Approach > EPSILON)) { t_Half_Chord_Squared = Rad1 - OCSquared + Sqr(t_Closest_Approach); if (t_Half_Chord_Squared > EPSILON) { Half_Chord = sqrt(t_Half_Chord_Squared); *Depth1 = t_Closest_Approach - Half_Chord; *Depth2 = t_Closest_Approach + Half_Chord; IPoint = P + *Depth1 * D; if (IPoint[Y] < BottomVertical) { lcount++; } IPoint = P + *Depth2 * D; if (IPoint[Y] < BottomVertical) { lcount++; } } } if (lcount > 1) return; Second_Center = Vector3d(0, BottomRadius, 0); Padj = Second_Center - P; OCSquared = Padj.lengthSqr(); t_Closest_Approach = dot(Padj, D); if ((OCSquared < Rad2) || (t_Closest_Approach > EPSILON)) { t_Half_Chord_Squared = Rad2 - OCSquared + Sqr(t_Closest_Approach); if (t_Half_Chord_Squared > EPSILON) { Half_Chord = sqrt(t_Half_Chord_Squared); *Depth3 = t_Closest_Approach - Half_Chord; *Depth4 = t_Closest_Approach + Half_Chord; IPoint = P + *Depth3 * D; if (IPoint[Y] > TopVertical) { lcount++; } IPoint = P + *Depth4 * D; if (IPoint[Y] > TopVertical) { lcount++; } } } if (lcount > 1) return; Second_Center = Vector3d(0, VerticalPosition, 0); Padj = P - Second_Center; R2 = Sqr(HorizontalPosition); r2 = Sqr(ConnectingRadius); // Notice : ConnectingRadius > HorizontalPosition here ! Py2 = Padj[Y] * Padj[Y]; Dy2 = D[Y] * D[Y]; PDy2 = Padj[Y] * D[Y]; k1 = Padj[X] * Padj[X] + Padj[Z] * Padj[Z] + Py2 - R2 - r2; k2 = Padj[X] * D[X] + Padj[Z] * D[Z] + PDy2; // this is just like a big torus c[0] = 1.0; c[1] = 4.0 * k2; c[2] = 2.0 * (k1 + 2.0 * (k2 * k2 + R2 * Dy2)); c[3] = 4.0 * (k2 * k1 + 2.0 * R2 * PDy2); c[4] = k1 * k1 + 4.0 * R2 * (Py2 - r2); n = Solve_Polynomial(4, c, r, Test_Flag(this, STURM_FLAG), ROOT_TOLERANCE, Thread->Stats()); while (n--) { // here we only keep the 'lemon' inside the torus // and dismiss the 'apple' // If you find a solution to resolve the rotation of // (x + r)^2 + y^2 = R^2 around y (so replacing x by sqrt(x^2+z^2)) // with something which is faster than a 4th degree polynome, // please feel welcome to update and share... IPoint = P + r[n] * D; vertical = IPoint[Y]; if ((vertical > BottomVertical) && (vertical < TopVertical)) { horizontal = sqrt(Sqr(IPoint[X]) + Sqr(IPoint[Z])); OCSquared = Sqr((horizontal + HorizontalPosition)) + Sqr((vertical - VerticalPosition)); if (fabs(OCSquared - Sqr(ConnectingRadius)) < ROOT_TOLERANCE) { if (*Depth5 < 0) { *Depth5 = r[n]; } else { *Depth6 = r[n]; } } } } }
int Torus::Intersect(const Ray& ray, DBL *Depth, SceneThreadData *Thread) const { int i, n; DBL len, R2, Py2, Dy2, PDy2, k1, k2; DBL y1, y2, r1, r2; DBL c[5]; DBL r[4]; VECTOR P, D; DBL DistanceP; // Distance from P to torus center (origo). DBL BoundingSphereRadius; // Sphere fully (amply) enclosing torus. DBL Closer; // P is moved Closer*D closer to torus. Thread->Stats()[Ray_Torus_Tests]++; /* Transform the ray into the torus space. */ MInvTransPoint(P, ray.Origin, Trans); MInvTransDirection(D, ray.Direction, Trans); VLength(len, D); VInverseScaleEq(D, len); i = 0; y1 = -MinorRadius; y2 = MinorRadius; r1 = Sqr(MajorRadius - MinorRadius); if ( MajorRadius < MinorRadius ) r1 = 0; r2 = Sqr(MajorRadius + MinorRadius); #ifdef TORUS_EXTRA_STATS Thread->Stats()[Torus_Bound_Tests]++; #endif if (Test_Thick_Cylinder(P, D, y1, y2, r1, r2)) { #ifdef TORUS_EXTRA_STATS Thread->Stats()[Torus_Bound_Tests_Succeeded]++; #endif // Move P close to bounding sphere to have more precise root calculation. // Bounding sphere radius is R + r, we add r once more to ensure // that P is safely outside sphere. BoundingSphereRadius = MajorRadius + MinorRadius + MinorRadius; DistanceP = VSumSqr(P); // Distance is currently squared. Closer = 0.0; if (DistanceP > Sqr(BoundingSphereRadius)) { DistanceP = sqrt(DistanceP); // Now real distance. Closer = DistanceP - BoundingSphereRadius; VAddScaledEq(P, Closer, D); } R2 = Sqr(MajorRadius); r2 = Sqr(MinorRadius); Py2 = P[Y] * P[Y]; Dy2 = D[Y] * D[Y]; PDy2 = P[Y] * D[Y]; k1 = P[X] * P[X] + P[Z] * P[Z] + Py2 - R2 - r2; k2 = P[X] * D[X] + P[Z] * D[Z] + PDy2; c[0] = 1.0; c[1] = 4.0 * k2; c[2] = 2.0 * (k1 + 2.0 * (k2 * k2 + R2 * Dy2)); c[3] = 4.0 * (k2 * k1 + 2.0 * R2 * PDy2); c[4] = k1 * k1 + 4.0 * R2 * (Py2 - r2); n = Solve_Polynomial(4, c, r, Test_Flag(this, STURM_FLAG), ROOT_TOLERANCE, Thread); while(n--) Depth[i++] = (r[n] + Closer) / len; } if (i) Thread->Stats()[Ray_Torus_Tests_Succeeded]++; return(i); }
void Lathe::Compute_Lathe(Vector2d *P, TraceThreadData *Thread) { int i, i1, i2, i3, n, segment, number_of_segments; DBL x[4], y[4]; DBL c[3]; DBL r[2]; DBL xmin, xmax, ymin, ymax; DBL *tmp_r1; DBL *tmp_r2; DBL *tmp_h1; DBL *tmp_h2; Vector2d A, B, C, D; /* Get number of segments. */ switch (Spline_Type) { case LINEAR_SPLINE: number_of_segments = Number - 1; break; case QUADRATIC_SPLINE: number_of_segments = Number - 2; break; case CUBIC_SPLINE: number_of_segments = Number - 3; break; case BEZIER_SPLINE: number_of_segments = Number / 4; break; default: /* tw */ number_of_segments = 0; /* tw */ } /* Allocate segments. */ if (Spline == NULL) { Spline = reinterpret_cast<LATHE_SPLINE *>(POV_MALLOC(sizeof(LATHE_SPLINE), "spline segments of lathe")); /* Init spline. */ Spline->References = 1; Spline->Entry = reinterpret_cast<LATHE_SPLINE_ENTRY *>(POV_MALLOC(number_of_segments*sizeof(LATHE_SPLINE_ENTRY), "spline segments of lathe")); } else { /* This should never happen! */ throw POV_EXCEPTION_STRING("Lathe segments are already defined."); } /* Allocate temporary lists. */ tmp_r1 = reinterpret_cast<DBL *>(POV_MALLOC(number_of_segments * sizeof(DBL), "temp lathe data")); tmp_r2 = reinterpret_cast<DBL *>(POV_MALLOC(number_of_segments * sizeof(DBL), "temp lathe data")); tmp_h1 = reinterpret_cast<DBL *>(POV_MALLOC(number_of_segments * sizeof(DBL), "temp lathe data")); tmp_h2 = reinterpret_cast<DBL *>(POV_MALLOC(number_of_segments * sizeof(DBL), "temp lathe data")); /*************************************************************************** * Calculate segments. ****************************************************************************/ /* We want to know the size of the overall bounding cylinder. */ xmax = ymax = -BOUND_HUGE; xmin = ymin = BOUND_HUGE; for (i = segment = 0; segment < number_of_segments; ) { i1 = i + 1; i2 = i + 2; i3 = i + 3; switch (Spline_Type) { /************************************************************************* * Linear spline (nothing more than a simple polygon). **************************************************************************/ case LINEAR_SPLINE: /* Use linear interpolation. */ A = Vector2d(0.0); B = Vector2d(0.0); C = -1.0 * P[i] + 1.0 * P[i1]; D = 1.0 * P[i]; /* Get maximum coordinates in current segment. */ x[0] = x[2] = P[i][X]; x[1] = x[3] = P[i1][X]; y[0] = y[2] = P[i][Y]; y[1] = y[3] = P[i1][Y]; break; /************************************************************************* * Quadratic spline. **************************************************************************/ case QUADRATIC_SPLINE: /* Use quadratic interpolation. */ A = Vector2d(0.0); B = 0.5 * P[i] - 1.0 * P[i1] + 0.5 * P[i2]; C = -0.5 * P[i] + 0.5 * P[i2]; D = 1.0 * P[i1]; /* Get maximum coordinates in current segment. */ x[0] = x[2] = P[i1][X]; x[1] = x[3] = P[i2][X]; y[0] = y[2] = P[i1][Y]; y[1] = y[3] = P[i2][Y]; break; /************************************************************************* * Cubic spline. **************************************************************************/ case CUBIC_SPLINE: /* Use cubic interpolation. */ A = -0.5 * P[i] + 1.5 * P[i1] - 1.5 * P[i2] + 0.5 * P[i3]; B = P[i] - 2.5 * P[i1] + 2.0 * P[i2] - 0.5 * P[i3]; C = -0.5 * P[i] + 0.5 * P[i2]; D = P[i1]; /* Get maximum coordinates in current segment. */ x[0] = x[2] = P[i1][X]; x[1] = x[3] = P[i2][X]; y[0] = y[2] = P[i1][Y]; y[1] = y[3] = P[i2][Y]; break; /************************************************************************* * Bezier spline. **************************************************************************/ case BEZIER_SPLINE: /* Use Bernstein interpolation. */ A = P[i3] - 3.0 * P[i2] + 3.0 * P[i1] - P[i]; B = 3.0 * P[i2] - 6.0 * P[i1] + 3.0 * P[i]; C = 3.0 * P[i1] - 3.0 * P[i]; D = P[i]; x[0] = P[i][X]; x[1] = P[i1][X]; x[2] = P[i2][X]; x[3] = P[i3][X]; y[0] = P[i][Y]; y[1] = P[i1][Y]; y[2] = P[i2][Y]; y[3] = P[i3][Y]; break; default: throw POV_EXCEPTION_STRING("Unknown lathe type in Compute_Lathe()."); } Spline->Entry[segment].A = A; Spline->Entry[segment].B = B; Spline->Entry[segment].C = C; Spline->Entry[segment].D = D; if ((Spline_Type == QUADRATIC_SPLINE) || (Spline_Type == CUBIC_SPLINE)) { /* Get maximum coordinates in current segment. */ c[0] = 3.0 * A[X]; c[1] = 2.0 * B[X]; c[2] = C[X]; n = Solve_Polynomial(2, c, r, false, 0.0, Thread); while (n--) { if ((r[n] >= 0.0) && (r[n] <= 1.0)) { x[n] = r[n] * (r[n] * (r[n] * A[X] + B[X]) + C[X]) + D[X]; } } c[0] = 3.0 * A[Y]; c[1] = 2.0 * B[Y]; c[2] = C[Y]; n = Solve_Polynomial(2, c, r, false, 0.0, Thread); while (n--) { if ((r[n] >= 0.0) && (r[n] <= 1.0)) { y[n] = r[n] * (r[n] * (r[n] * A[Y] + B[Y]) + C[Y]) + D[Y]; } } } /* Set current segment's bounding cylinder. */ tmp_r1[segment] = min(min(x[0], x[1]), min(x[2], x[3])); tmp_r2[segment] = max(max(x[0], x[1]), max(x[2], x[3])); tmp_h1[segment] = min(min(y[0], y[1]), min(y[2], y[3])); tmp_h2[segment] = max(max(y[0], y[1]), max(y[2], y[3])); /* Keep track of overall bounding cylinder. */ xmin = min(xmin, tmp_r1[segment]); xmax = max(xmax, tmp_r2[segment]); ymin = min(ymin, tmp_h1[segment]); ymax = max(ymax, tmp_h2[segment]); /* fprintf(stderr, "bound spline segment %d: ", i); fprintf(stderr, "r = %f - %f, h = %f - %f\n", tmp_r1[segment], tmp_r2[segment], tmp_h1[segment], tmp_h2[segment]); */ /* Advance to next segment. */ switch (Spline_Type) { case LINEAR_SPLINE: case QUADRATIC_SPLINE: case CUBIC_SPLINE: i++; break; case BEZIER_SPLINE: i += 4; break; } segment++; } Number = number_of_segments; /* Set overall bounding cylinder. */ Radius1 = xmin; Radius2 = xmax; Height1 = ymin; Height2 = ymax; /* Get bounding cylinder. */ Spline->BCyl = Create_BCyl(Number, tmp_r1, tmp_r2, tmp_h1, tmp_h2); /* Get rid of temp. memory. */ POV_FREE(tmp_h2); POV_FREE(tmp_h1); POV_FREE(tmp_r2); POV_FREE(tmp_r1); }
bool Lathe::Inside(const Vector3d& IPoint, TraceThreadData *Thread) const { int i, n, NC; DBL r, k, w; DBL x[4]; DBL y[3]; DBL *height; Vector3d P; BCYL_ENTRY *entry; LATHE_SPLINE_ENTRY *Entry; height = Spline->BCyl->height; entry = Spline->BCyl->entry; /* Transform the point into the lathe space. */ MInvTransPoint(P, IPoint, Trans); /* Number of crossings. */ NC = 0; if ((P[Y] >= Height1) && (P[Y] <= Height2)) { r = sqrt(P[X] * P[X] + P[Z] * P[Z]); if ((r >= Radius1) && (r <= Radius2)) { for (i = 0; i < Number; i++) { Entry = &Spline->Entry[i]; /* Test if we are inside the segments cylindrical bound. */ if ((P[Y] >= height[entry[i].h1] - EPSILON) && (P[Y] <= height[entry[i].h2] + EPSILON)) { x[0] = Entry->A[Y]; x[1] = Entry->B[Y]; x[2] = Entry->C[Y]; x[3] = Entry->D[Y] - P[Y]; n = Solve_Polynomial(3, x, y, Test_Flag(this, STURM_FLAG), 0.0, Thread); while (n--) { w = y[n]; if ((w >= 0.0) && (w <= 1.0)) { k = w * (w * (w * Entry->A[X] + Entry->B[X]) + Entry->C[X]) + Entry->D[X] - r; if (k >= 0.0) { NC++; } } } } } } } if (NC & 1) { return(!Test_Flag(this, INVERTED_FLAG)); } else { return(Test_Flag(this, INVERTED_FLAG)); } }
bool Lathe::Intersect(const BasicRay& ray, IStack& Depth_Stack, TraceThreadData *Thread) { int cnt; int found, j, n1, n2; DBL k, len, r, m, w, Dy2, r0, Dylen; DBL x1[7]; DBL x2[3]; DBL y1[6]; DBL y2[2]; DBL best; Vector3d P, D; LATHE_SPLINE_ENTRY *Entry; // Transform the ray into the lathe space. MInvTransPoint(P, ray.Origin, Trans); MInvTransDirection(D, ray.Direction, Trans); len = D.length(); D /= len; Dylen = D[Y] * len; // TODO FIXME - why don't we do this *before* normalizing, saving us the multiplication by len? #ifdef LATHE_EXTRA_STATS Thread->Stats()[Lathe_Bound_Tests]++; #endif // Test if ray misses lathe's cylindrical bound. if(((D[Y] >= 0.0) && (P[Y] > Height2)) || ((D[Y] <= 0.0) && (P[Y] < Height1)) || ((D[X] >= 0.0) && (P[X] > Radius2)) || ((D[X] <= 0.0) && (P[X] < -Radius2))) return false; // Get distance r0 of the ray from rotation axis (i.e. y axis). r0 = fabs(P[X] * D[Z] - P[Z] * D[X]); r = D[X] * D[X] + D[Z] * D[Z]; if(r > 0.0) r0 /= sqrt(r); // Test if ray misses lathe's cylindrical bound. if(r0 > Radius2) return false; // Intersect all cylindrical bounds. BCYL_INT *intervals = reinterpret_cast<BCYL_INT *>(Thread->BCyl_Intervals) ; BCYL_INT *rint = reinterpret_cast<BCYL_INT *>(Thread->BCyl_RInt) ; BCYL_INT *hint = reinterpret_cast<BCYL_INT *>(Thread->BCyl_HInt) ; if((cnt = Intersect_BCyl(Spline->BCyl, intervals, rint, hint, P, D)) == 0) return false; #ifdef LATHE_EXTRA_STATS Thread->Stats()[Lathe_Bound_Tests_Succeeded]++; #endif // Precalculate some constants that are ray-dependant only. m = D[X] * P[X] + D[Z] * P[Z]; Dy2 = D[Y] * D[Y]; // Step through the list of intersections. found = false; best = BOUND_HUGE; for(j = 0; j < cnt; j++) { // Get current segment. Entry = &Spline->Entry[intervals[j].n]; // If we already have the best intersection we may exit. if(!(Type & IS_CHILD_OBJECT) && (intervals[j].d[0] > best)) break; // Init number of roots found. n1 = 0; // Intersect segment. switch(Spline_Type) { // Linear spline case LINEAR_SPLINE: // Solve 2th order polynomial. x1[0] = Entry->C[Y] * Entry->C[Y] * r - Entry->C[X] * Entry->C[X] * Dy2; x1[1] = 2.0 * (Entry->C[Y] * ((Entry->D[Y] - P[Y]) * r + D[Y] * m) - Entry->C[X] * Entry->D[X] * Dy2); x1[2] = (Entry->D[Y] - P[Y]) * ((Entry->D[Y] - P[Y]) * r + 2.0 * D[Y] * m) + Dy2 * (P[X] * P[X] + P[Z] * P[Z] - Entry->D[X] * Entry->D[X]); n1 = Solve_Polynomial(2, x1, y1, false, 0.0, Thread); break; // Quadratic spline case QUADRATIC_SPLINE: // Solve 4th order polynomial. x1[0] = Entry->B[Y] * Entry->B[Y] * r - Entry->B[X] * Entry->B[X] * Dy2; x1[1] = 2.0 * (Entry->B[Y] * Entry->C[Y] * r - Entry->B[X] * Entry->C[X] * Dy2); x1[2] = r * (2.0 * Entry->B[Y] * (Entry->D[Y] - P[Y]) + Entry->C[Y] * Entry->C[Y]) + 2.0 * Entry->B[Y] * D[Y] * m - (2.0 * Entry->B[X] * Entry->D[X] + Entry->C[X] * Entry->C[X]) * Dy2; x1[3] = 2.0 * (Entry->C[Y] * ((Entry->D[Y] - P[Y]) * r + D[Y] * m) - Entry->C[X] * Entry->D[X] * Dy2); x1[4] = (Entry->D[Y] - P[Y]) * ((Entry->D[Y] - P[Y]) * r + 2.0 * D[Y] * m) + Dy2 * (P[X] * P[X] + P[Z] * P[Z] - Entry->D[X] * Entry->D[X]); n1 = Solve_Polynomial(4, x1, y1, Test_Flag(this, STURM_FLAG), 0.0, Thread); break; // Cubic spline case BEZIER_SPLINE: case CUBIC_SPLINE: // Solve 6th order polynomial. x1[0] = Entry->A[Y] * Entry->A[Y] * r - Entry->A[X] * Entry->A[X] * Dy2; x1[1] = 2.0 * (Entry->A[Y] * Entry->B[Y] * r - Entry->A[X] * Entry->B[X] * Dy2); x1[2] = (2.0 * Entry->A[Y] * Entry->C[Y] + Entry->B[Y] * Entry->B[Y]) * r - (2.0 * Entry->A[X] * Entry->C[X] + Entry->B[X] * Entry->B[X]) * Dy2; x1[3] = 2.0 * ((Entry->A[Y] * Entry->D[Y] + Entry->B[Y] * Entry->C[Y] - Entry->A[Y] * P[Y]) * r + Entry->A[Y] * D[Y] * m - (Entry->A[X] * Entry->D[X] + Entry->B[X] * Entry->C[X]) * Dy2); x1[4] = (2.0 * Entry->B[Y] * (Entry->D[Y] - P[Y]) + Entry->C[Y] * Entry->C[Y]) * r + 2.0 * Entry->B[Y] * D[Y] * m - (2.0 * Entry->B[X] * Entry->D[X] + Entry->C[X] * Entry->C[X]) * Dy2; x1[5] = 2.0 * (Entry->C[Y] * ((Entry->D[Y] - P[Y]) * r + D[Y] * m) - Entry->C[X] * Entry->D[X] * Dy2); x1[6] = (Entry->D[Y] - P[Y]) * ((Entry->D[Y] - P[Y]) * r + 2.0 * D[Y] * m) + Dy2 * (P[X] * P[X] + P[Z] * P[Z] - Entry->D[X] * Entry->D[X]); n1 = Solve_Polynomial(6, x1, y1, Test_Flag(this, STURM_FLAG), 0.0, Thread); break; } // Test roots for valid intersections. while(n1--) { w = y1[n1]; if((w >= 0.0) && (w <= 1.0)) { if(fabs(D[Y]) > EPSILON) { k = (w * (w * (w * Entry->A[Y] + Entry->B[Y]) + Entry->C[Y]) + Entry->D[Y] - P[Y]); if (test_hit(ray, Depth_Stack, k / Dylen, w, intervals[j].n, Thread)) { found = true; k /= D[Y]; if (k < best) best = k; } } else { k = w * (w * (w * Entry->A[X] + Entry->B[X]) + Entry->C[X]) + Entry->D[X]; x2[0] = r; x2[1] = 2.0 * m; x2[2] = P[X] * P[X] + P[Z] * P[Z] - k * k; n2 = Solve_Polynomial(2, x2, y2, false, 0.0, Thread); while(n2--) { k = y2[n2]; if(test_hit(ray, Depth_Stack, k / len, w, intervals[j].n, Thread)) { found = true; if(k < best) best = k; } } } } } } return found; }
void Sor::Compute_Sor(Vector2d *P, TraceThreadData *Thread) { int i, n; DBL *tmp_r1; DBL *tmp_r2; DBL *tmp_h1; DBL *tmp_h2; DBL A, B, C, D, w; DBL xmax, xmin, ymax, ymin; DBL k[4], x[4]; DBL y[2], r[2]; DBL c[3]; MATRIX Mat; /* Allocate Number segments. */ if (Spline == NULL) { Spline = new SOR_SPLINE; Spline->References = 1; Spline->Entry = new SOR_SPLINE_ENTRY[Number]; } else { throw POV_EXCEPTION_STRING("Surface of revolution segments are already defined."); } /* Allocate temporary lists. */ tmp_r1 = new DBL[Number]; tmp_r2 = new DBL[Number]; tmp_h1 = new DBL[Number]; tmp_h2 = new DBL[Number]; /* We want to know the size of the overall bounding cylinder. */ xmax = ymax = -BOUND_HUGE; xmin = ymin = BOUND_HUGE; /* Calculate segments, i.e. cubic patches. */ for (i = 0; i < Number; i++) { if ((fabs(P[i+2][Y] - P[i][Y]) < EPSILON) || (fabs(P[i+3][Y] - P[i+1][Y]) < EPSILON)) { throw POV_EXCEPTION_STRING("Incorrect point in surface of revolution."); } /* Use cubic interpolation. */ k[0] = P[i+1][X] * P[i+1][X]; k[1] = P[i+2][X] * P[i+2][X]; k[2] = (P[i+2][X] - P[i][X]) / (P[i+2][Y] - P[i][Y]); k[3] = (P[i+3][X] - P[i+1][X]) / (P[i+3][Y] - P[i+1][Y]); k[2] *= 2.0 * P[i+1][X]; k[3] *= 2.0 * P[i+2][X]; w = P[i+1][Y]; Mat[0][0] = w * w * w; Mat[0][1] = w * w; Mat[0][2] = w; Mat[0][3] = 1.0; Mat[2][0] = 3.0 * w * w; Mat[2][1] = 2.0 * w; Mat[2][2] = 1.0; Mat[2][3] = 0.0; w = P[i+2][Y]; Mat[1][0] = w * w * w; Mat[1][1] = w * w; Mat[1][2] = w; Mat[1][3] = 1.0; Mat[3][0] = 3.0 * w * w; Mat[3][1] = 2.0 * w; Mat[3][2] = 1.0; Mat[3][3] = 0.0; MInvers(Mat, Mat); /* Calculate coefficients of cubic patch. */ A = k[0] * Mat[0][0] + k[1] * Mat[0][1] + k[2] * Mat[0][2] + k[3] * Mat[0][3]; B = k[0] * Mat[1][0] + k[1] * Mat[1][1] + k[2] * Mat[1][2] + k[3] * Mat[1][3]; C = k[0] * Mat[2][0] + k[1] * Mat[2][1] + k[2] * Mat[2][2] + k[3] * Mat[2][3]; D = k[0] * Mat[3][0] + k[1] * Mat[3][1] + k[2] * Mat[3][2] + k[3] * Mat[3][3]; if (fabs(A) < EPSILON) A = 0.0; if (fabs(B) < EPSILON) B = 0.0; if (fabs(C) < EPSILON) C = 0.0; if (fabs(D) < EPSILON) D = 0.0; Spline->Entry[i].A = A; Spline->Entry[i].B = B; Spline->Entry[i].C = C; Spline->Entry[i].D = D; /* Get minimum and maximum radius**2 in current segment. */ y[0] = P[i+1][Y]; y[1] = P[i+2][Y]; x[0] = x[2] = P[i+1][X]; x[1] = x[3] = P[i+2][X]; c[0] = 3.0 * A; c[1] = 2.0 * B; c[2] = C; n = Solve_Polynomial(2, c, r, false, 0.0, Thread); while (n--) { if ((r[n] >= y[0]) && (r[n] <= y[1])) { x[n] = sqrt(r[n] * (r[n] * (r[n] * A + B) + C) + D); } } /* Set current segment's bounding cylinder. */ tmp_r1[i] = min(min(x[0], x[1]), min(x[2], x[3])); tmp_r2[i] = max(max(x[0], x[1]), max(x[2], x[3])); tmp_h1[i] = y[0]; tmp_h2[i] = y[1]; /* Keep track of overall bounding cylinder. */ xmin = min(xmin, tmp_r1[i]); xmax = max(xmax, tmp_r2[i]); ymin = min(ymin, tmp_h1[i]); ymax = max(ymax, tmp_h2[i]); /* fprintf(stderr, "bound spline segment %d: ", i); fprintf(stderr, "r = %f - %f, h = %f - %f\n", tmp_r1[i], tmp_r2[i], tmp_h1[i], tmp_h2[i]); */ } /* Set overall bounding cylinder. */ Radius1 = xmin; Radius2 = xmax; Height1 = ymin; Height2 = ymax; /* Get cap radius. */ w = tmp_h2[Number-1]; A = Spline->Entry[Number-1].A; B = Spline->Entry[Number-1].B; C = Spline->Entry[Number-1].C; D = Spline->Entry[Number-1].D; if ((Cap_Radius_Squared = w * (w * (A * w + B) + C) + D) < 0.0) { Cap_Radius_Squared = 0.0; } /* Get base radius. */ w = tmp_h1[0]; A = Spline->Entry[0].A; B = Spline->Entry[0].B; C = Spline->Entry[0].C; D = Spline->Entry[0].D; if ((Base_Radius_Squared = w * (w * (A * w + B) + C) + D) < 0.0) { Base_Radius_Squared = 0.0; } /* Get bounding cylinder. */ Spline->BCyl = Create_BCyl(Number, tmp_r1, tmp_r2, tmp_h1, tmp_h2); /* Get rid of temp. memory. */ delete[] tmp_h2; delete[] tmp_h1; delete[] tmp_r2; delete[] tmp_r1; }
bool Sor::Intersect(const BasicRay& ray, IStack& Depth_Stack, TraceThreadData *Thread) { int cnt; int found, j, n; DBL a, b, k, h, len, u, v, r0; DBL x[4]; DBL y[3]; DBL best; Vector3d P, D; SOR_SPLINE_ENTRY *Entry; /* Transform the ray into the surface of revolution space. */ MInvTransPoint(P, ray.Origin, Trans); MInvTransDirection(D, ray.Direction, Trans); len = D.length(); D /= len; /* Test if ray misses object's bounds. */ #ifdef SOR_EXTRA_STATS Thread->Stats()[Sor_Bound_Tests]++; #endif if (((D[Y] >= 0.0) && (P[Y] > Height2)) || ((D[Y] <= 0.0) && (P[Y] < Height1)) || ((D[X] >= 0.0) && (P[X] > Radius2)) || ((D[X] <= 0.0) && (P[X] < -Radius2))) { return(false); } /* Get distance of the ray from rotation axis (= y axis). */ r0 = P[X] * D[Z] - P[Z] * D[X]; if ((a = D[X] * D[X] + D[Z] * D[Z]) > 0.0) { r0 /= sqrt(a); } /* Test if ray misses object's bounds. */ if (r0 > Radius2) { return(false); } /* Test base/cap plane. */ found = false; best = BOUND_HUGE; if (Test_Flag(this, CLOSED_FLAG) && (fabs(D[Y]) > EPSILON)) { /* Test base plane. */ if (Base_Radius_Squared > DEPTH_TOLERANCE) { k = (Height1 - P[Y]) / D[Y]; u = P[X] + k * D[X]; v = P[Z] + k * D[Z]; b = u * u + v * v; if (b <= Base_Radius_Squared) { if (test_hit(ray, Depth_Stack, k / len, k, BASE_PLANE, 0, Thread)) { found = true; if (k < best) { best = k; } } } } /* Test cap plane. */ if (Cap_Radius_Squared > DEPTH_TOLERANCE) { k = (Height2 - P[Y]) / D[Y]; u = P[X] + k * D[X]; v = P[Z] + k * D[Z]; b = u * u + v * v; if (b <= Cap_Radius_Squared) { if (test_hit(ray, Depth_Stack, k / len, k, CAP_PLANE, 0, Thread)) { found = true; if (k < best) { best = k; } } } } } /* Intersect all cylindrical bounds. */ vector<BCYL_INT>& intervals = Thread->BCyl_Intervals; vector<BCYL_INT>& rint = Thread->BCyl_RInt; vector<BCYL_INT>& hint = Thread->BCyl_HInt; if ((cnt = Intersect_BCyl(Spline->BCyl, intervals, rint, hint, P, D)) == 0) { #ifdef SOR_EXTRA_STATS if (found) Thread->Stats()[Sor_Bound_Tests_Succeeded]++; #endif return(found); } #ifdef SOR_EXTRA_STATS Thread->Stats()[Sor_Bound_Tests_Succeeded]++; #endif /* Step through the list of intersections. */ for (j = 0; j < cnt; j++) { /* Get current segment. */ Entry = &Spline->Entry[intervals[j].n]; /* If we already have the best intersection we may exit. */ if (!(Type & IS_CHILD_OBJECT) && (intervals[j].d[0] > best)) { break; } /* Cubic curve. */ x[0] = Entry->A * D[Y] * D[Y] * D[Y]; /* x[1] = D[Y] * D[Y] * (3.0 * Entry->A * P[Y] + Entry->B) - D[X] * D[X] - D[Z] * D[Z]; */ x[1] = D[Y] * D[Y] * (3.0 * Entry->A * P[Y] + Entry->B) - a; x[2] = D[Y] * (P[Y] * (3.0 * Entry->A * P[Y] + 2.0 * Entry->B) + Entry->C) - 2.0 * (P[X] * D[X] + P[Z] * D[Z]); x[3] = P[Y] * (P[Y] * (Entry->A * P[Y] + Entry->B) + Entry->C) + Entry->D - P[X] * P[X] - P[Z] * P[Z]; n = Solve_Polynomial(3, x, y, Test_Flag(this, STURM_FLAG), 0.0, Thread); while (n--) { k = y[n]; h = P[Y] + k * D[Y]; if ((h >= Spline->BCyl->height[Spline->BCyl->entry[intervals[j].n].h1]) && (h <= Spline->BCyl->height[Spline->BCyl->entry[intervals[j].n].h2])) { if (test_hit(ray, Depth_Stack, k / len, k, CURVE, intervals[j].n, Thread)) { found = true; if (y[n] < best) { best = k; } } } } } return(found); }