/**************************************************************************** * prism.c * * This module implements functions that manipulate prisms. * * This module was written by Dieter Bayer [DB]. * * from Persistence of Vision(tm) Ray Tracer * Copyright 1996 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ /**************************************************************************** * * Explanation: * * The prism primitive is defined by a set of points in 2d space which * are interpolated by linear, quadratic, or cubic splines. The resulting * 2d curve is swept along a straight line to form the final prism object. * * All calculations are done in the object's (u,v,w)-coordinate system * with the (w)-axis being the sweep axis. * * One spline segment in the (u,v)-plane is given by the equations * * fu(t) = Au * t * t * t + Bu * t * t + Cu * t + Du and * fv(t) = Av * t * t * t + Bv * t * t + Cv * t + Dv, * * with the parameter t ranging from 0 to 1. * * To intersect a ray R = P + k * D transformed into the object's * coordinate system with the linear swept prism object, the equation * * Dv * fu(t) - Du * fv(t) - (Pu * Dv - Pv * Du) = 0 * * has to be solved for t. For valid intersections (0 <= t <= 1) * the corresponding k can be calculated from equation * * Pu + k * Du = fu(t) or Pv + k * Dv = fv(t). * * In the case of conic sweeping the equation * * (Pv * Dw - Pw * Dv) * fu(t) - (Pu * Dw - Pw * Du) * fv(t) * + (Pu * Dv - Pv * Du) = 0 * * has to be solved for t. For valid intersections (0 <= t <= 1) * the corresponding k can be calculated from equation * * Pu + k * Du = (Pw + k * Dw) * fu(t) or * Pv + k * Dv = (Pw + k * Dw) * fv(t). * * Note that the polynomal to solve has the same degree as the * spline segments used. * * Note that Pu, Pv, Pw and Du, Dv, Dw denote the coordinates * of the vectors P and D. * * Syntax: * * prism { * [ linear_sweep | cubic_sweep ] * [ linear_spline | quadratic_spline | cubic_spline ] * * height1, height2, * number_of_points * *
,
, ...,
* * [ open ] * [ sturm ] * } * * Note that the P(i) are 2d vectors. * * --- * * Ideas for the prism was taken from: * * James T. Kajiya, "New techniques for ray tracing procedurally * defined objects", Computer Graphics, 17(3), July 1983, pp. 91-102 * * Kirk ... * * --- * * May 1994 : Creation. [DB] * *****************************************************************************/ #include "frame.h" #include "povray.h" #include "vector.h" #include "povproto.h" #include "bbox.h" #include "matrices.h" #include "objects.h" #include "polysolv.h" #include "prism.h" /***************************************************************************** * Local preprocessor defines ******************************************************************************/ /* Minimal intersection depth for a valid intersection. */ #define DEPTH_TOLERANCE 1.0e-4 /* |Coefficients| < COEFF_LIMIT are regarded to be 0. */ /*#define COEFF_LIMIT 1.0e-20 */ #define COEFF_LIMIT 1.0e-16 /*changed by CEY 11/18/95 */ /* Part of the prism hit. */ #define BASE_HIT 1 #define CAP_HIT 2 #define SPLINE_HIT 3 /***************************************************************************** * Local typedefs ******************************************************************************/ /***************************************************************************** * Static functions ******************************************************************************/ static int intersect_prism PARAMS((RAY *Ray, PRISM *Prism, PRISM_INT *Intersection)); static int in_curve PARAMS((PRISM *Prism, DBL u, DBL v)); static int test_rectangle PARAMS((VECTOR P, VECTOR D, DBL x1, DBL y1, DBL x2, DBL y2)); static int All_Prism_Intersections PARAMS((OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack)); static int Inside_Prism PARAMS((VECTOR point, OBJECT *Object)); static void Prism_Normal PARAMS((VECTOR Result, OBJECT *Object, INTERSECTION *Inter)); static void *Copy_Prism PARAMS((OBJECT *Object)); static void Translate_Prism PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Rotate_Prism PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Scale_Prism PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Transform_Prism PARAMS((OBJECT *Object, TRANSFORM *Trans)); static void Invert_Prism PARAMS((OBJECT *Object)); static void Destroy_Prism PARAMS((OBJECT *Object)); /***************************************************************************** * Local variables ******************************************************************************/ static METHODS Prism_Methods = { All_Prism_Intersections, Inside_Prism, Prism_Normal, Copy_Prism, Translate_Prism, Rotate_Prism, Scale_Prism, Transform_Prism, Invert_Prism, Destroy_Prism }; /***************************************************************************** * * FUNCTION * * All_Prism_Intersections * * INPUT * * Object - Object * Ray - Ray * Depth_Stack - Intersection stack * * OUTPUT * * Depth_Stack * * RETURNS * * int - TRUE, if a intersection was found * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Determine ray/prism intersection and clip intersection found. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int All_Prism_Intersections(Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { int i, max_i, Found; PRISM_INT *Inter; VECTOR IPoint; Found = FALSE; Inter = ((PRISM *)Object)->Intersections; max_i = intersect_prism(Ray, (PRISM *)Object, Inter); if (max_i) { for (i = 0; i < max_i; i++) { if ((Inter[i].d > DEPTH_TOLERANCE) && (Inter[i].d < Max_Distance)) { VEvaluateRay(IPoint, Ray->Initial, Inter[i].d, Ray->Direction); if (Point_In_Clip(IPoint, Object->Clip)) { push_entry_i1_i2_d1(Inter[i].d,IPoint,Object,Inter[i].t,Inter[i].n,Inter[i].w,Depth_Stack); Found = TRUE; } } } } return(Found); } /***************************************************************************** * * FUNCTION * * intersect_prism * * INPUT * * Ray - Ray * Prism - Prism * Int - Prism intersection structure * * OUTPUT * * Int * * RETURNS * * int - Number of intersections found * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Determine ray/prism intersection. * * NOTE: Order reduction cannot be used. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int intersect_prism(Ray, Prism, Intersection) RAY *Ray; PRISM *Prism; PRISM_INT *Intersection; { int i, j, n; DBL k, u, v, w, h, len; DBL x[4], y[3]; DBL k1, k2, k3; VECTOR P, D; PRISM_SPLINE_ENTRY Entry; /* Don't test degenerate prisms. */ if (Test_Flag(Prism, DEGENERATE_FLAG)) { return(FALSE); } Increase_Counter(stats[Ray_Prism_Tests]); /* Init intersection structure. */ Intersection->d = Intersection->w = 0.0; Intersection->n = Intersection->t = 0; /* Transform the ray into the prism space */ MInvTransPoint(P, Ray->Initial, Prism->Trans); MInvTransDirection(D, Ray->Direction, Prism->Trans); VLength(len, D); VInverseScaleEq(D, len); /* Test overall bounding rectangle. */ #ifdef PRISM_EXTRA_STATS Increase_Counter(stats[Prism_Bound_Tests]); #endif if (((D[X] >= 0.0) && (P[X] > Prism->x2)) || ((D[X] <= 0.0) && (P[X] < Prism->x1)) || ((D[Z] >= 0.0) && (P[Z] > Prism->y2)) || ((D[Z] <= 0.0) && (P[Z] < Prism->y1))) { return(FALSE); } #ifdef PRISM_EXTRA_STATS Increase_Counter(stats[Prism_Bound_Tests_Succeeded]); #endif /* Number of intersections found. */ i = 0; /* What kind of sweep is used? */ switch (Prism->Sweep_Type) { /************************************************************************* * Linear sweep. **************************************************************************/ case LINEAR_SWEEP : if (fabs(D[Y]) < EPSILON) { if ((P[Y] < Prism->Height1) || (P[Y] > Prism->Height2)) { return(FALSE); } } else { if (Test_Flag(Prism, CLOSED_FLAG)) { /* Intersect ray with the cap-plane. */ k = (Prism->Height2 - P[Y]) / D[Y]; if ((k > DEPTH_TOLERANCE) && (k < Max_Distance)) { u = P[X] + k * D[X]; v = P[Z] + k * D[Z]; if (in_curve(Prism, u, v)) { Intersection[i].t = CAP_HIT; Intersection[i++].d = k / len; } } /* Intersect ray with the base-plane. */ k = (Prism->Height1 - P[Y]) / D[Y]; if ((k > DEPTH_TOLERANCE) && (k < Max_Distance)) { u = P[X] + k * D[X]; v = P[Z] + k * D[Z]; if (in_curve(Prism, u, v)) { Intersection[i].t = BASE_HIT; Intersection[i++].d = k / len; } } } } /* Intersect ray with all spline segments. */ if ((fabs(D[X]) > EPSILON) || (fabs(D[Z]) > EPSILON)) { for (j = 0; j < Prism->Number; j++) { Entry = Prism->Spline->Entry[j]; /* Test spline's bounding rectangle (modified Cohen-Sutherland). */ #ifdef PRISM_EXTRA_STATS Increase_Counter(stats[Prism_Bound_Tests]); #endif if (((D[X] >= 0.0) && (P[X] > Entry.x2)) || ((D[X] <= 0.0) && (P[X] < Entry.x1)) || ((D[Z] >= 0.0) && (P[Z] > Entry.y2)) || ((D[Z] <= 0.0) && (P[Z] < Entry.y1))) { continue; } /* Number of roots found. */ n = 0; switch (Prism->Spline_Type) { case LINEAR_SPLINE : #ifdef PRISM_EXTRA_STATS Increase_Counter(stats[Prism_Bound_Tests_Succeeded]); #endif /* Solve linear equation. */ x[0] = Entry.C[X] * D[Z] - Entry.C[Y] * D[X]; x[1] = D[Z] * (Entry.D[X] - P[X]) - D[X] * (Entry.D[Y] - P[Z]); if (fabs(x[0]) > EPSILON) { y[n++] = -x[1] / x[0]; } break; case QUADRATIC_SPLINE : #ifdef PRISM_EXTRA_STATS Increase_Counter(stats[Prism_Bound_Tests_Succeeded]); #endif /* Solve quadratic equation. */ x[0] = Entry.B[X] * D[Z] - Entry.B[Y] * D[X]; x[1] = Entry.C[X] * D[Z] - Entry.C[Y] * D[X]; x[2] = D[Z] * (Entry.D[X] - P[X]) - D[X] * (Entry.D[Y] - P[Z]); n = Solve_Polynomial(2, x, y, FALSE, 0.0); break; case CUBIC_SPLINE : if (test_rectangle(P, D, Entry.x1, Entry.y1, Entry.x2, Entry.y2)) { #ifdef PRISM_EXTRA_STATS Increase_Counter(stats[Prism_Bound_Tests_Succeeded]); #endif /* Solve cubic equation. */ x[0] = Entry.A[X] * D[Z] - Entry.A[Y] * D[X]; x[1] = Entry.B[X] * D[Z] - Entry.B[Y] * D[X]; x[2] = Entry.C[X] * D[Z] - Entry.C[Y] * D[X]; x[3] = D[Z] * (Entry.D[X] - P[X]) - D[X] * (Entry.D[Y] - P[Z]); n = Solve_Polynomial(3, x, y, Test_Flag(Prism, STURM_FLAG), 0.0); } break; } /* Test roots for valid intersections. */ while (n--) { w = y[n]; if ((w >= 0.0) && (w <= 1.0)) { if (fabs(D[X]) > EPSILON) { k = (w * (w * (w * Entry.A[X] + Entry.B[X]) + Entry.C[X]) + Entry.D[X] - P[X]) / D[X]; } else { k = (w * (w * (w * Entry.A[Y] + Entry.B[Y]) + Entry.C[Y]) + Entry.D[Y] - P[Z]) / D[Z]; } /* Verify that intersection height is valid. */ h = P[Y] + k * D[Y]; if ((h >= Prism->Height1) && (h <= Prism->Height2)) { Intersection[i].t = SPLINE_HIT; Intersection[i].n = j; Intersection[i].w = w; Intersection[i++].d = k / len; } } } } } break; /************************************************************************* * Conic sweep. **************************************************************************/ case CONIC_SWEEP : if (fabs(D[Y]) < EPSILON) { if ((P[Y] < Prism->Height1) || (P[Y] > Prism->Height2)) { return(FALSE); } } else { /* Intersect ray with the cap-plane. */ k = (Prism->Height2 - P[Y]) / D[Y]; if ((k > DEPTH_TOLERANCE) && (k < Max_Distance)) { u = (P[X] + k * D[X]) / Prism->Height2; v = (P[Z] + k * D[Z]) / Prism->Height2; if (in_curve(Prism, u, v)) { Intersection[i].t = CAP_HIT; Intersection[i++].d = k / len; } } /* Intersect ray with the base-plane. */ if (Prism->Height1 > 0.0) { k = (Prism->Height1 - P[Y]) / D[Y]; if ((k > DEPTH_TOLERANCE) && (k < Max_Distance)) { u = (P[X] + k * D[X]) / Prism->Height1; v = (P[Z] + k * D[Z]) / Prism->Height1; if (in_curve(Prism, u, v)) { Intersection[i].t = BASE_HIT; Intersection[i++].d = k / len; } } } } /* Precompute ray-only dependant constants. */ k1 = P[Z] * D[Y] - P[Y] * D[Z]; k2 = P[Y] * D[X] - P[X] * D[Y]; k3 = P[X] * D[Z] - P[Z] * D[X]; /* Intersect ray with the spline segments. */ if ((fabs(D[X]) > EPSILON) || (fabs(D[Z]) > EPSILON)) { for (j = 0; j < Prism->Number; j++) { Entry = Prism->Spline->Entry[j]; /* Test spline's bounding rectangle (modified Cohen-Sutherland). */ if (((D[X] >= 0.0) && (P[X] > Entry.x2)) || ((D[X] <= 0.0) && (P[X] < Entry.x1)) || ((D[Z] >= 0.0) && (P[Z] > Entry.y2)) || ((D[Z] <= 0.0) && (P[Z] < Entry.y1))) { continue; } /* Number of roots found. */ n = 0; switch (Prism->Spline_Type) { case LINEAR_SPLINE : /* Solve linear equation. */ x[0] = Entry.C[X] * k1 + Entry.C[Y] * k2; x[1] = Entry.D[X] * k1 + Entry.D[Y] * k2 + k3; if (fabs(x[0]) > EPSILON) { y[n++] = -x[1] / x[0]; } break; case QUADRATIC_SPLINE : /* Solve quadratic equation. */ x[0] = Entry.B[X] * k1 + Entry.B[Y] * k2; x[1] = Entry.C[X] * k1 + Entry.C[Y] * k2; x[2] = Entry.D[X] * k1 + Entry.D[Y] * k2 + k3; n = Solve_Polynomial(2, x, y, FALSE, 0.0); break; case CUBIC_SPLINE : /* Solve cubic equation. */ x[0] = Entry.A[X] * k1 + Entry.A[Y] * k2; x[1] = Entry.B[X] * k1 + Entry.B[Y] * k2; x[2] = Entry.C[X] * k1 + Entry.C[Y] * k2; x[3] = Entry.D[X] * k1 + Entry.D[Y] * k2 + k3; n = Solve_Polynomial(3, x, y, Test_Flag(Prism, STURM_FLAG), 0.0); break; } /* Test roots for valid intersections. */ 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]; h = D[X] - k * D[Y]; if (fabs(h) > EPSILON) { k = (k * P[Y] - P[X]) / h; } else { k = w * (w * (w * Entry.A[Y] + Entry.B[Y]) + Entry.C[Y]) + Entry.D[Y]; h = D[Z] - k * D[Y]; if (fabs(h) > EPSILON) { k = (k * P[Y] - P[Z]) / h; } else { /* This should never happen! */ continue; } } /* Verify that intersection height is valid. */ h = P[Y] + k * D[Y]; if ((h >= Prism->Height1) && (h <= Prism->Height2)) { Intersection[i].t = SPLINE_HIT; Intersection[i].n = j; Intersection[i].w = w; Intersection[i++].d = k / len; } } } } } break; default: Error("Unknown sweep type in intersect_prism().\n"); } if (i) { Increase_Counter(stats[Ray_Prism_Tests_Succeeded]); } return(i); } /***************************************************************************** * * FUNCTION * * Inside_Prism * * INPUT * * IPoint - Intersection point * Object - Object * * OUTPUT * * RETURNS * * int - TRUE if inside * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Test if point lies inside a prism. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int Inside_Prism(IPoint, Object) VECTOR IPoint; OBJECT *Object; { VECTOR P; PRISM *Prism = (PRISM *)Object; /* Transform the point into the prism space. */ MInvTransPoint(P, IPoint, Prism->Trans); if ((P[Y] >= Prism->Height1) && (P[Y] < Prism->Height2)) { if (Prism->Sweep_Type == CONIC_SWEEP) { /* Scale x and z coordinate. */ if (P[Y] > 0.0) { P[X] /= P[Y]; P[Z] /= P[Y]; } else { P[X] = P[Z] = HUGE_VAL; } } if (in_curve(Prism, P[X], P[Z])) { return(!Test_Flag(Prism, INVERTED_FLAG)); } } return(Test_Flag(Prism, INVERTED_FLAG)); } /***************************************************************************** * * FUNCTION * * Prism_Normal * * INPUT * * Result - Normal vector * Object - Object * Inter - Intersection found * * OUTPUT * * Result * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Calculate the normal of the prism in a given point. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Prism_Normal(Result, Object, Inter) OBJECT *Object; VECTOR Result; INTERSECTION *Inter; { DBL r; VECTOR P; PRISM_SPLINE_ENTRY Entry; PRISM *Prism = (PRISM *)Object; VECTOR N; Make_Vector(N, 0.0, 1.0, 0.0); if (Inter->i1 == SPLINE_HIT) { Entry = Prism->Spline->Entry[Inter->i2]; switch (Prism->Sweep_Type) { case LINEAR_SWEEP: N[X] = Inter->d1 * (3.0 * Entry.A[Y] * Inter->d1 + 2.0 * Entry.B[Y]) + Entry.C[Y]; N[Y] = 0.0; N[Z] = -(Inter->d1 * (3.0 * Entry.A[X] * Inter->d1 + 2.0 * Entry.B[X]) + Entry.C[X]); break; case CONIC_SWEEP: /* Transform the point into the prism space. */ MInvTransPoint(P, Inter->IPoint, Prism->Trans); if (P[Y] > 0.0) { r = sqrt(P[X] * P[X] + P[Z] * P[Z]); N[X] = Inter->d1 * (3.0 * Entry.A[Y] * Inter->d1 + 2.0 * Entry.B[Y]) + Entry.C[Y]; N[Y] = -r / P[Y]; N[Z] = -(Inter->d1 * (3.0 * Entry.A[X] * Inter->d1 + 2.0 * Entry.B[X]) + Entry.C[X]); } break; default: Error("Unknown sweep type in Prism_Normal().\n"); } } /* Transform the normalt out of the prism space. */ MTransNormal(Result, N, Prism->Trans); VNormalize(Result, Result); } /***************************************************************************** * * FUNCTION * * Translate_Prism * * INPUT * * Object - Object * Vector - Translation vector * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Translate a prism. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Translate_Prism(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { Transform_Prism(Object, Trans); } /***************************************************************************** * * FUNCTION * * Rotate_Prism * * INPUT * * Object - Object * Vector - Rotation vector * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Rotate a prism. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Rotate_Prism(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { Transform_Prism(Object, Trans); } /***************************************************************************** * * FUNCTION * * Scale_Prism * * INPUT * * Object - Object * Vector - Scaling vector * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Scale a prism. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Scale_Prism(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { Transform_Prism(Object, Trans); } /***************************************************************************** * * FUNCTION * * Transform_Prism * * INPUT * * Object - Object * Trans - Transformation to apply * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Transform a prism and recalculate its bounding box. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Transform_Prism(Object, Trans) OBJECT *Object; TRANSFORM *Trans; { Compose_Transforms(((PRISM *)Object)->Trans, Trans); Compute_Prism_BBox((PRISM *)Object); } /***************************************************************************** * * FUNCTION * * Invert_Prism * * INPUT * * Object - Object * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Invert a prism. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Invert_Prism(Object) OBJECT *Object; { Invert_Flag(Object, INVERTED_FLAG); } /***************************************************************************** * * FUNCTION * * Create_Prism * * INPUT * * OUTPUT * * RETURNS * * PRISM * - new prism * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Create a new prism. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ PRISM *Create_Prism() { PRISM *New; New = (PRISM *)POV_MALLOC(sizeof(PRISM), "prism"); INIT_OBJECT_FIELDS(New,PRISM_OBJECT,&Prism_Methods) New->Trans = Create_Transform(); New->x1 = New->x2 = New->y1 = New->y2 = New->Height1 = New->Height2 = 0.0; New->Number = 0; New->Spline_Type = LINEAR_SPLINE; New->Sweep_Type = LINEAR_SWEEP; New->Spline = NULL; New->Intersections = NULL; Set_Flag(New, CLOSED_FLAG); return(New); } /***************************************************************************** * * FUNCTION * * Copy_Prism * * INPUT * * Object - Object * * OUTPUT * * RETURNS * * void * - New prism * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Copy a prism structure. * * NOTE: The splines are not copied, only the number of references is * counted, so that Destray_Prism() knows if they can be destroyed. * * CHANGES * * May 1994 : Creation. * * Sep 1994 : fixed memory leakage [DB] * ******************************************************************************/ static void *Copy_Prism(Object) OBJECT *Object; { PRISM *New, *Prism = (PRISM *)Object; New = Create_Prism(); /* Get rid of the transformation created in Create_Prism(). */ Destroy_Transform(New->Trans); /* Copy prism. */ *New = *Prism; New->Trans = Copy_Transform(((PRISM *)Object)->Trans); New->Spline->References++; Prism->Intersections = (PRISM_INT *)POV_MALLOC((Prism->Number+2)*sizeof(PRISM_INT), "prism intersection list"); return(New); } /***************************************************************************** * * FUNCTION * * Destroy_Prism * * INPUT * * Object - Object * * OUTPUT * * Object * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Destroy a prism. * * NOTE: The splines are destroyed if they are no longer used by any copy. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static void Destroy_Prism (Object) OBJECT *Object; { PRISM *Prism = (PRISM *)Object; Destroy_Transform(Prism->Trans); POV_FREE(Prism->Intersections); if (--(Prism->Spline->References) == 0) { POV_FREE(Prism->Spline->Entry); POV_FREE(Prism->Spline); } POV_FREE(Object); } /***************************************************************************** * * FUNCTION * * Compute_Prism_BBox * * INPUT * * Prism - Prism * * OUTPUT * * Prism * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Calculate the bounding box of a prism. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ void Compute_Prism_BBox(Prism) PRISM *Prism; { Make_BBox(Prism->BBox, Prism->x1, Prism->Height1, Prism->y1, Prism->x2 - Prism->x1, Prism->Height2 - Prism->Height1, Prism->y2 - Prism->y1); Recompute_BBox(&Prism->BBox, Prism->Trans); } /***************************************************************************** * * FUNCTION * * in_curve * * INPUT * * Prism - Prism to test * u, v - Coordinates * * OUTPUT * * RETURNS * * int - TRUE if inside * * AUTHOR * * Dieter Bayer, June 1994 * * DESCRIPTION * * Test if a 2d point lies inside a prism's spline curve. * * We travel from the current point in positive u direction * and count the number of crossings with the curve. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int in_curve(Prism, u, v) PRISM *Prism; DBL u, v; { int i, n, NC; DBL k, w; DBL x[4], y[3]; PRISM_SPLINE_ENTRY Entry; NC = 0; /* First test overall bounding rectangle. */ if ((u >= Prism->x1) && (u <= Prism->x2) && (v >= Prism->y1) && (v <= Prism->y2)) { for (i = 0; i < Prism->Number; i++) { Entry = Prism->Spline->Entry[i]; /* Test if current segment can be hit. */ if ((v >= Entry.y1) && (v <= Entry.y2) && (u <= Entry.x2)) { x[0] = Entry.A[Y]; x[1] = Entry.B[Y]; x[2] = Entry.C[Y]; x[3] = Entry.D[Y] - v; n = Solve_Polynomial(3, x, y, Test_Flag(Prism, STURM_FLAG), 0.0); 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] - u; if (k >= 0.0) { NC++; } } } } } } return(NC & 1); } /***************************************************************************** * * FUNCTION * * test_rectangle * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Dieter Bayer, July 1994 * * DESCRIPTION * * Test if the 2d ray (= P + t * D) intersects a rectangle. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ static int test_rectangle(P, D, x1, z1, x2, z2) VECTOR P, D; DBL x1, z1, x2, z2; { DBL dmin, dmax, tmin, tmax; if (fabs(D[X]) > EPSILON) { if (D[X] > 0.0) { dmin = (x1 - P[X]) / D[X]; dmax = (x2 - P[X]) / D[X]; if (dmax < EPSILON) { return(FALSE); } } else { dmax = (x1 - P[X]) / D[X]; if (dmax < EPSILON) { return(FALSE); } dmin = (x2 - P[X]) / D[X]; } if (dmin > dmax) { return(FALSE); } } else { if ((P[X] < x1) || (P[X] > x2)) { return(FALSE); } else { dmin = -BOUND_HUGE; dmax = BOUND_HUGE; } } if (fabs(D[Z]) > EPSILON) { if (D[Z] > 0.0) { tmin = (z1 - P[Z]) / D[Z]; tmax = (z2 - P[Z]) / D[Z]; } else { tmax = (z1 - P[Z]) / D[Z]; tmin = (z2 - P[Z]) / D[Z]; } if (tmax < dmax) { if (tmax < EPSILON) { return(FALSE); } if (tmin > dmin) { if (tmin > tmax) { return(FALSE); } dmin = tmin; } else { if (dmin > tmax) { return(FALSE); } } /*dmax = tmax; */ /*not needed CEY[1/95]*/ } else { if (tmin > dmin) { if (tmin > dmax) { return(FALSE); } /* dmin = tmin; */ /*not needed CEY[1/95]*/ } } } else { if ((P[Z] < z1) || (P[Z] > z2)) { return(FALSE); } } return(TRUE); } /***************************************************************************** * * FUNCTION * * Compute_Prism * * INPUT * * Prism - Prism * P - Points defining prism * * OUTPUT * * Prism * * RETURNS * * AUTHOR * * Dieter Bayer, June 1994 * * DESCRIPTION * * Calculate the spline segments of a prism from a set of points. * * CHANGES * * May 1994 : Creation. * ******************************************************************************/ void Compute_Prism(Prism, P) PRISM *Prism; UV_VECT *P; { int i, n, number_of_splines; int i1, i2, i3; DBL x[4], xmin, xmax; DBL y[4], ymin, ymax; DBL c[3], r[2]; UV_VECT A, B, C, D, First; /* Allocate Object->Number segments. */ if (Prism->Spline == NULL) { Prism->Spline = (PRISM_SPLINE *)POV_MALLOC(sizeof(PRISM_SPLINE), "spline segments of prism"); Prism->Spline->References = 1; Prism->Spline->Entry = (PRISM_SPLINE_ENTRY *)POV_MALLOC(Prism->Number*sizeof(PRISM_SPLINE_ENTRY), "spline segments of prism"); } else { /* This should never happen! */ Error("Prism segments are already defined.\n"); } /* Allocate prism intersections list. */ Prism->Intersections = (PRISM_INT *)POV_MALLOC((Prism->Number+2)*sizeof(PRISM_INT), "prism intersection list"); /*************************************************************************** * Calculate segments. ****************************************************************************/ /* We want to know the size of the overall bounding rectangle. */ xmax = ymax = -BOUND_HUGE; xmin = ymin = BOUND_HUGE; /* Get first segment point. */ switch (Prism->Spline_Type) { case LINEAR_SPLINE: Assign_UV_Vect(First, P[0]); break; case QUADRATIC_SPLINE: case CUBIC_SPLINE: Assign_UV_Vect(First, P[1]); break; } for (i = number_of_splines = 0; i < Prism->Number-1; i++) { /* Get indices of previous and next two points. */ i1 = i + 1; i2 = i + 2; i3 = i + 3; switch (Prism->Spline_Type) { /************************************************************************* * Linear spline (nothing more than a simple polygon). **************************************************************************/ case LINEAR_SPLINE : if (i1 >= Prism->Number) { Error("Too few points in prism. Prism not closed? Control points missing?\n"); } /* Use linear interpolation. */ A[X] = 0.0; B[X] = 0.0; C[X] = -1.0 * P[i][X] + 1.0 * P[i1][X]; D[X] = 1.0 * P[i][X]; A[Y] = 0.0; B[Y] = 0.0; C[Y] = -1.0 * P[i][Y] + 1.0 * P[i1][Y]; D[Y] = 1.0 * P[i][Y]; 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 : if (i2 >= Prism->Number) { Error("Too few points in prism.\n"); } /* Use quadratic interpolation. */ A[X] = 0.0; B[X] = 0.5 * P[i][X] - 1.0 * P[i1][X] + 0.5 * P[i2][X]; C[X] = -0.5 * P[i][X] + 0.5 * P[i2][X]; D[X] = 1.0 * P[i1][X]; A[Y] = 0.0; B[Y] = 0.5 * P[i][Y] - 1.0 * P[i1][Y] + 0.5 * P[i2][Y]; C[Y] = -0.5 * P[i][Y] + 0.5 * P[i2][Y]; D[Y] = 1.0 * P[i1][Y]; 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 : if (i3 >= Prism->Number) { Error("Too few points in prism.\n"); } /* Use cubic interpolation. */ A[X] = -0.5 * P[i][X] + 1.5 * P[i1][X] - 1.5 * P[i2][X] + 0.5 * P[i3][X]; B[X] = P[i][X] - 2.5 * P[i1][X] + 2.0 * P[i2][X] - 0.5 * P[i3][X]; C[X] = -0.5 * P[i][X] + 0.5 * P[i2][X]; D[X] = P[i1][X]; A[Y] = -0.5 * P[i][Y] + 1.5 * P[i1][Y] - 1.5 * P[i2][Y] + 0.5 * P[i3][Y]; B[Y] = P[i][Y] - 2.5 * P[i1][Y] + 2.0 * P[i2][Y] - 0.5 * P[i3][Y]; C[Y] = -0.5 * P[i][Y] + 0.5 * P[i2][Y]; D[Y] = P[i1][Y]; 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; default: Error("Unknown spline type in Compute_Prism().\n"); } Assign_UV_Vect(Prism->Spline->Entry[number_of_splines].A, A); Assign_UV_Vect(Prism->Spline->Entry[number_of_splines].B, B); Assign_UV_Vect(Prism->Spline->Entry[number_of_splines].C, C); Assign_UV_Vect(Prism->Spline->Entry[number_of_splines].D, D); /* 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); 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); 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 rectangle. */ Prism->Spline->Entry[number_of_splines].x1 = min(min(x[0], x[1]), min(x[2], x[3])); Prism->Spline->Entry[number_of_splines].x2 = max(max(x[0], x[1]), max(x[2], x[3])); Prism->Spline->Entry[number_of_splines].y1 = min(min(y[0], y[1]), min(y[2], y[3])); Prism->Spline->Entry[number_of_splines].y2 = max(max(y[0], y[1]), max(y[2], y[3])); /* Keep track of overall bounding rectangle. */ xmin = min(xmin, Prism->Spline->Entry[number_of_splines].x1); xmax = max(xmax, Prism->Spline->Entry[number_of_splines].x2); ymin = min(ymin, Prism->Spline->Entry[number_of_splines].y1); ymax = max(ymax, Prism->Spline->Entry[number_of_splines].y2); number_of_splines++; /* Check if end of sub-prism is reached. */ switch (Prism->Spline_Type) { case LINEAR_SPLINE: if ((fabs(P[i1][X] - First[X]) < EPSILON) && (fabs(P[i1][Y] - First[Y]) < EPSILON)) { i++; if (i+1 < Prism->Number) { Assign_UV_Vect(First, P[i+1]); } } break; case QUADRATIC_SPLINE: if ((fabs(P[i2][X] - First[X]) < EPSILON) && (fabs(P[i2][Y] - First[Y]) < EPSILON)) { i += 2; if (i+2 < Prism->Number) { Assign_UV_Vect(First, P[i+2]); } } break; case CUBIC_SPLINE: if ((fabs(P[i2][X] - First[X]) < EPSILON) && (fabs(P[i2][Y] - First[Y]) < EPSILON)) { i += 3; if (i+2 < Prism->Number) { Assign_UV_Vect(First, P[i+2]); } } break; } } Prism->Number = number_of_splines; /* Set overall bounding rectangle. */ Prism->x1 = xmin; Prism->x2 = xmax; Prism->y1 = ymin; Prism->y2 = ymax; if (Prism->Sweep_Type == CONIC_SWEEP) { /* Recalculate bounding rectangles. */ for (i = 0; i < Prism->Number; i++) { x[0] = Prism->Spline->Entry[i].x1 * Prism->Height1; x[1] = Prism->Spline->Entry[i].x1 * Prism->Height2; x[2] = Prism->Spline->Entry[i].x2 * Prism->Height1; x[3] = Prism->Spline->Entry[i].x2 * Prism->Height2; xmin = min(min(x[0], x[1]), min(x[2], x[3])); xmax = max(max(x[0], x[1]), max(x[2], x[3])); Prism->Spline->Entry[i].x1 = xmin; Prism->Spline->Entry[i].x2 = xmax; y[0] = Prism->Spline->Entry[i].y1 * Prism->Height1; y[1] = Prism->Spline->Entry[i].y1 * Prism->Height2; y[2] = Prism->Spline->Entry[i].y2 * Prism->Height1; y[3] = Prism->Spline->Entry[i].y2 * Prism->Height2; ymin = min(min(y[0], y[1]), min(y[2], y[3])); ymax = max(max(y[0], y[1]), max(y[2], y[3])); Prism->Spline->Entry[i].y1 = ymin; Prism->Spline->Entry[i].y2 = ymax; } /* Recalculate overall bounding rectangle. */ x[0] = Prism->x1 * Prism->Height1; x[1] = Prism->x1 * Prism->Height2; x[2] = Prism->x2 * Prism->Height1; x[3] = Prism->x2 * Prism->Height2; xmin = min(min(x[0], x[1]), min(x[2], x[3])); xmax = max(max(x[0], x[1]), max(x[2], x[3])); Prism->x1 = xmin; Prism->x2 = xmax; y[0] = Prism->y1 * Prism->Height1; y[1] = Prism->y1 * Prism->Height2; y[2] = Prism->y2 * Prism->Height1; y[3] = Prism->y2 * Prism->Height2; ymin = min(min(y[0], y[1]), min(y[2], y[3])); ymax = max(max(y[0], y[1]), max(y[2], y[3])); Prism->y1 = ymin; Prism->y2 = ymax; } }