/*****************************************************************************
* bbcyl.c
*
* This file contains all functions for bounding
* cylinders used by lathe and sor objects.
*
* 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.
*
*****************************************************************************/
#include "frame.h"
#include "vector.h"
#include "povproto.h"
#include "bcyl.h"
/*****************************************************************************
* Local preprocessor defines
******************************************************************************/
/*****************************************************************************
* Local typedefs
******************************************************************************/
/*****************************************************************************
* Static functions
******************************************************************************/
static int intersect_thick_cylinder PARAMS((BCYL *BCyl, BCYL_ENTRY *Entry, DBL *dist));
static void insert_hit PARAMS((BCYL_INT *Element, BCYL_INT *intervals, int *cnt));
static void intersect_bound_elements PARAMS((BCYL *BCyl, VECTOR P, VECTOR D));
/*****************************************************************************
* Local variables
******************************************************************************/
/*****************************************************************************
*
* FUNCTION
*
* intersect_thick_cylinder
*
* INPUT
*
* BCyl - Pointer to lathe structure
* Entry - Segment whos bounding cylinder to intersect
* dist - List of sorted intersection depths
*
* OUTPUT
*
* RETURNS
*
* int - number of intersections found
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Find all intersections of the current ray with the bounding
* cylinder of the given segment. The intersection tests are
* done in intersect_bound_elements() and the results stored
* in the lathe structure are evaluated here.
*
* CHANGES
*
* Oct 1996 : Creation.
*
******************************************************************************/
static int intersect_thick_cylinder(BCyl, Entry, dist)
BCYL *BCyl;
BCYL_ENTRY *Entry;
DBL *dist;
{
int i, j, n;
DBL k, r, h;
n = 0;
/* Intersect ray with the cap-plane. */
if (BCyl->hint[Entry->h2].n)
{
r = BCyl->hint[Entry->h2].w[0];
if ((r >= BCyl->radius[Entry->r1]) &&
(r <= BCyl->radius[Entry->r2]))
{
dist[n++] = BCyl->hint[Entry->h2].d[0];
}
}
/* Intersect ray with the base-plane. */
if (BCyl->hint[Entry->h1].n)
{
r = BCyl->hint[Entry->h1].w[0];
if ((r >= BCyl->radius[Entry->r1]) &&
(r <= BCyl->radius[Entry->r2]))
{
dist[n++] = BCyl->hint[Entry->h1].d[0];
}
}
/* Intersect with inner cylinder. */
if (BCyl->rint[Entry->r1].n)
{
h = BCyl->rint[Entry->r1].w[0];
if ((h >= BCyl->height[Entry->h1]) &&
(h <= BCyl->height[Entry->h2]))
{
dist[n++] = BCyl->rint[Entry->r1].d[0];
}
h = BCyl->rint[Entry->r1].w[1];
if ((h >= BCyl->height[Entry->h1]) &&
(h <= BCyl->height[Entry->h2]))
{
dist[n++] = BCyl->rint[Entry->r1].d[1];
}
}
/* Intersect with outer cylinder. */
if (BCyl->rint[Entry->r2].n)
{
h = BCyl->rint[Entry->r2].w[0];
if ((h >= BCyl->height[Entry->h1]) &&
(h <= BCyl->height[Entry->h2]))
{
dist[n++] = BCyl->rint[Entry->r2].d[0];
}
h = BCyl->rint[Entry->r2].w[1];
if ((h >= BCyl->height[Entry->h1]) &&
(h <= BCyl->height[Entry->h2]))
{
dist[n++] = BCyl->rint[Entry->r2].d[1];
}
}
/* Sort intersections. */
for (i = 0; i < n; i++)
{
for (j = 0; j < n - i - 1; j++)
{
if (dist[j] > dist[j+1])
{
k = dist[j];
dist[j] = dist[j+1];
dist[j+1] = k;
}
}
}
return(n);
}
/*****************************************************************************
*
* FUNCTION
*
* intersect_bound_elements
*
* INPUT
*
* BCyl - Pointer to lathe structure
* P, D - Current ray
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Intersect all bounding discs and cylinders and store
* the intersections found in the lathe structure.
*
* By intersecting all different discs and cylinders once
* we avoid testing the same cylinders and discs more than
* once. This happened when we tested one bounding cylinder
* after the other.
*
* CHANGES
*
* Oct 1996 : Creation.
*
******************************************************************************/
static void intersect_bound_elements(BCyl, P, D)
BCYL *BCyl;
VECTOR P, D;
{
int i;
DBL a, b, bb, b2, c, d, k;
/* Init constants. */
a = D[X] * D[X] + D[Z] * D[Z];
b = P[X] * D[X] + P[Z] * D[Z];
bb = b * b;
b2 = 2.0 * b;
c = P[X] * P[X] + P[Z] * P[Z];
/* Intersect all rings. */
if ((D[Y] < -EPSILON) || (D[Y] > EPSILON))
{
for (i = 0; i < BCyl->nheight; i++)
{
k = (BCyl->height[i] - P[Y]) / D[Y];
BCyl->hint[i].n = 1;
BCyl->hint[i].d[0] = k;
BCyl->hint[i].w[0] = k * (a * k + b2) + c;
}
}
else
{
for (i = 0; i < BCyl->nheight; i++)
{
BCyl->hint[i].n = 0;
}
}
/* Intersect all cylinders. */
for (i = 0; i < BCyl->nradius; i++)
{
BCyl->rint[i].n = 0;
if (BCyl->radius[i] > EPSILON)
{
d = bb - a * (c - BCyl->radius[i]);
if (d > 0.0)
{
d = sqrt(d);
k = (-b + d) / a;
BCyl->rint[i].n = 2;
BCyl->rint[i].d[0] = k;
BCyl->rint[i].w[0] = P[Y] + k * D[Y];
k = (-b - d) / a;
BCyl->rint[i].d[1] = k;
BCyl->rint[i].w[1] = P[Y] + k * D[Y];
}
}
}
}
/*****************************************************************************
*
* FUNCTION
*
* insert_hit
*
* INPUT
*
* element - Intersection to insert
* intervals - List of intervals
* cnt - Number of elements in the list
*
* OUTPUT
*
* intervals, cnt
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Insert an intersection into the depth sorted intersection list.
*
* CHANGES
*
* Oct 1996 : Creation.
*
******************************************************************************/
static void insert_hit(element, intervals, cnt)
BCYL_INT *element, *intervals;
int *cnt;
{
int k;
intervals[*cnt] = *element;
for (k = 0; element->d[0] > intervals[k].d[0]; k++);
if (k < *cnt)
{
POV_MEMMOVE(&intervals[k+1], &intervals[k], (*cnt-k)*sizeof(BCYL_INT));
intervals[k] = *element;
}
(*cnt)++;
}
/*****************************************************************************
*
* FUNCTION
*
* Intersect_All_Bounds
*
* INPUT
*
* BCyl - Pointer to lathe structure
* P, D - Current ray
* intervals - List of intervals
* cnt - Number of elements in the list
*
* OUTPUT
*
* intervals, cnt
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Intersect given ray with all bounding cylinders of the given lathe
* and return a sorted list of intersection depths and segments hit.
*
* CHANGES
*
* Oct 1996 : Creation.
*
******************************************************************************/
int Intersect_BCyl(BCyl, P, D)
BCYL *BCyl;
VECTOR P, D;
{
int i, cnt;
DBL dist[8];
BCYL_INT Inter;
BCYL_INT *intervals;
BCYL_ENTRY *Entry;
cnt = 0;
Inter.d[1] = 0.0;
/* Intersect all cylinder and plane elements. */
intersect_bound_elements(BCyl, P, D);
/* Intersect all spline segments. */
intervals = BCyl->intervals;
for (i = 0; i < BCyl->number; i++)
{
Entry = &BCyl->entry[i];
switch (intersect_thick_cylinder(BCyl, Entry, dist))
{
case 2:
if (dist[0] > EPSILON)
{
Inter.d[0] = dist[0];
Inter.n = i;
insert_hit(&Inter, intervals, &cnt);
}
else
{
if (dist[1] > EPSILON)
{
Inter.d[0] = 0.0;
Inter.n = i;
insert_hit(&Inter, intervals, &cnt);
}
}
break;
case 4:
if (dist[0] > EPSILON)
{
Inter.d[0] = dist[0];
Inter.n = i;
insert_hit(&Inter, intervals, &cnt);
}
else
{
if (dist[1] > EPSILON)
{
Inter.d[0] = 0.0;
Inter.n = i;
insert_hit(&Inter, intervals, &cnt);
}
else
{
if (dist[2] > EPSILON)
{
Inter.d[0] = dist[2];
Inter.n = i;
insert_hit(&Inter, intervals, &cnt);
}
else
{
if (dist[3] > EPSILON)
{
Inter.d[0] = 0.0;
Inter.n = i;
insert_hit(&Inter, intervals, &cnt);
}
}
}
}
break;
default:
/*
* We weren't able to find an even number of intersections. Thus
* we can't tell where the ray enters and leaves the bounding
* cylinder. To avoid problems we assume that the ray is always
* inside the cylinder in that case.
*/
Inter.d[0] = dist[0];
Inter.n = i;
insert_hit(&Inter, intervals, &cnt);
break;
}
}
return(cnt);
}
/*****************************************************************************
*
* FUNCTION
*
* Create_BCyl
*
* INPUT
*
* number - number of cylindrical segments
* r1, r2 - list of segment radii
* h1, h2 - list of segment heights
*
* OUTPUT
*
* RETURNS
*
* BCYL * - created bounding cylinder.
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create a bounding cylinder data structure from the given
* radii and heights.
*
* CHANGES
*
* Oct 1996 : Creation.
*
******************************************************************************/
BCYL *Create_BCyl(number, tmp_r1, tmp_r2, tmp_h1, tmp_h2)
int number;
DBL *tmp_r1, *tmp_r2, *tmp_h1, *tmp_h2;
{
int i, j, nr, nh;
int *tmp_r1_index;
int *tmp_r2_index;
int *tmp_h1_index;
int *tmp_h2_index;
DBL *tmp_radius;
DBL *tmp_height;
BCYL *bcyl;
/* Allocate bounding cylinder. */
bcyl = (BCYL *)POV_MALLOC(sizeof(BCYL), "bounding cylinder");
/* Allocate entries. */
bcyl->number = number;
bcyl->entry = (BCYL_ENTRY *)POV_MALLOC(bcyl->number*sizeof(BCYL_ENTRY), "bounding cylinder data");
/* Allocate intersection lists. */
bcyl->hint = (BCYL_INT *)POV_MALLOC(2*bcyl->number*sizeof(BCYL_INT), "lathe intersection list");
bcyl->rint = (BCYL_INT *)POV_MALLOC(2*bcyl->number*sizeof(BCYL_INT), "lathe intersection list");
bcyl->intervals = (BCYL_INT *)POV_MALLOC(4*bcyl->number*sizeof(BCYL_INT), "lathe intersection list");
/* Allocate temporary lists. */
tmp_r1_index = (int *)POV_MALLOC(bcyl->number * sizeof(int), "temp lathe data");
tmp_r2_index = (int *)POV_MALLOC(bcyl->number * sizeof(int), "temp lathe data");
tmp_h1_index = (int *)POV_MALLOC(bcyl->number * sizeof(int), "temp lathe data");
tmp_h2_index = (int *)POV_MALLOC(bcyl->number * sizeof(int), "temp lathe data");
tmp_radius = (DBL *)POV_MALLOC(2 * bcyl->number * sizeof(DBL), "temp lathe data");
tmp_height = (DBL *)POV_MALLOC(2 * bcyl->number * sizeof(DBL), "temp lathe data");
/* Get different bounding radii and heights. */
nr = 0;
nh = 0;
for (i = 0; i < bcyl->number; i++)
{
tmp_r1_index[i] = -1;
tmp_r2_index[i] = -1;
tmp_h1_index[i] = -1;
tmp_h2_index[i] = -1;
for (j = 0; j < nr; j++)
{
if (tmp_r1[i] == tmp_radius[j])
{
break;
}
}
if (j == nr)
{
tmp_radius[nr++] = tmp_r1[i];
}
tmp_r1_index[i] = j;
for (j = 0; j < nr; j++)
{
if (tmp_r2[i] == tmp_radius[j])
{
break;
}
}
if (j == nr)
{
tmp_radius[nr++] = tmp_r2[i];
}
tmp_r2_index[i] = j;
for (j = 0; j < nh; j++)
{
if (tmp_h1[i] == tmp_height[j])
{
break;
}
}
if (j == nh)
{
tmp_height[nh++] = tmp_h1[i];
}
tmp_h1_index[i] = j;
for (j = 0; j < nh; j++)
{
if (tmp_h2[i] == tmp_height[j])
{
break;
}
}
if (j == nh)
{
tmp_height[nh++] = tmp_h2[i];
}
tmp_h2_index[i] = j;
}
/* Copy lists into the lathe. */
bcyl->radius = (DBL *)POV_MALLOC(nr * sizeof(DBL), "bounding cylinder data");
bcyl->height = (DBL *)POV_MALLOC(nh * sizeof(DBL), "bounding cylinder data");
for (i = 0; i < nr; i++)
{
bcyl->radius[i] = Sqr(tmp_radius[i]);
}
for (i = 0; i < nh; i++)
{
bcyl->height[i] = tmp_height[i];
}
/* Assign height and radius indices. */
bcyl->nradius = nr;
bcyl->nheight = nh;
for (i = 0; i < bcyl->number; i++)
{
bcyl->entry[i].r1 = tmp_r1_index[i];
bcyl->entry[i].r2 = tmp_r2_index[i];
bcyl->entry[i].h1 = tmp_h1_index[i];
bcyl->entry[i].h2 = tmp_h2_index[i];
}
/*
fprintf(stderr, "number of different radii = %d\n", nr);
fprintf(stderr, "number of different heights = %d\n", nh);
*/
/* Get rid of temp. memory. */
POV_FREE(tmp_height);
POV_FREE(tmp_radius);
POV_FREE(tmp_h2_index);
POV_FREE(tmp_h1_index);
POV_FREE(tmp_r2_index);
POV_FREE(tmp_r1_index);
return(bcyl);
}
/*****************************************************************************
*
* FUNCTION
*
* Destroy_BCyl
*
* INPUT
*
* BCyl - bounding cylinder
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Destroy a bounding cylinder.
*
* CHANGES
*
* Oct 1996 : Creation.
*
******************************************************************************/
void Destroy_BCyl(BCyl)
BCYL *BCyl;
{
POV_FREE(BCyl->entry);
POV_FREE(BCyl->radius);
POV_FREE(BCyl->height);
POV_FREE(BCyl->rint);
POV_FREE(BCyl->hint);
POV_FREE(BCyl->intervals);
POV_FREE(BCyl);
}