home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Professional
/
OS2PRO194.ISO
/
os2
/
graphic
/
csg_rt
/
shape.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-02-11
|
30KB
|
1,245 lines
/*
SHAPE.C General shapes
*/
/*...sincludes:0:*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <malloc.h>
#include <memory.h>
#include <math.h>
#include "standard.h"
#include "rt.h"
#include "fio.h"
#include "tex.h"
#include "vector.h"
#include "rgbvec.h"
#include "col.h"
#include "surf.h"
#include "sil.h"
#include "plane.h"
#include "sphere.h"
#include "quad.h"
#define _SHAPE_
#include "shape.h"
/*...vrt\46\h:0:*/
/*...vfio\46\h:0:*/
/*...vtex\46\h:0:*/
/*...vvector\46\h:0:*/
/*...vrgbvec\46\h:0:*/
/*...vcol\46\h:0:*/
/*...vsurf\46\h:0:*/
/*...vsil\46\h:0:*/
/*...vplane\46\h:0:*/
/*...vsphere\46\h:0:*/
/*...vquad\46\h:0:*/
/*...vshape\46\h:0:*/
/*...e*/
/*...screate_plane_shape \45\ create a shape tree consisting of a single plane:0:*/
SHAPE *create_plane_shape(PLANE *plane, SURF *surf)
{
SHAPE *shape;
if ( (shape = malloc(sizeof(SHAPE))) == NULL )
return ( NULL );
shape -> stype = STYPE_PLANE;
shape -> u.plane = plane;
shape -> surf = surf;
return ( shape );
}
/*...e*/
/*...screate_sphere_shape \45\ create a shape tree consisting of a single sphere:0:*/
SHAPE *create_sphere_shape(SPHERE *sphere, SURF *surf)
{
SHAPE *shape;
if ( (shape = malloc(sizeof(SHAPE))) == NULL )
return ( NULL );
shape -> stype = STYPE_SPHERE;
shape -> u.sphere = sphere;
shape -> surf = surf;
return ( shape );
}
/*...e*/
/*...screate_quad_shape \45\ create a shape tree consisting of a single quadratic:0:*/
SHAPE *create_quad_shape(QUAD *quad, SURF *surf)
{
SHAPE *shape;
if ( (shape = malloc(sizeof(SHAPE))) == NULL )
return ( NULL );
shape -> stype = STYPE_QUAD;
shape -> u.quad = quad;
shape -> surf = surf;
return ( shape );
}
/*...e*/
/*...screate_bin_shape \45\ create a binary shape tree made of 2 shapes:0:*/
SHAPE *create_bin_shape(STYPE stype, SHAPE *shape0, SHAPE *shape1)
{
SHAPE *shape;
if ( (shape = malloc(sizeof(SHAPE))) == NULL )
return ( NULL );
shape -> stype = stype;
shape -> u.shapes [0] = shape0;
shape -> u.shapes [1] = shape1;
return ( shape );
}
/*...e*/
/*...scopy_shape \45\ make a complete copy of a shape tree:0:*/
SHAPE *copy_shape(SHAPE *shape)
{
SHAPE *copy;
if ( (copy = malloc(sizeof(SHAPE))) == NULL )
return ( NULL );
copy -> stype = shape -> stype;
if ( shape -> stype <= STYPE_QUAD )
/* Leaf node */
{
if ( (copy -> surf = copy_surf(shape -> surf)) == NULL )
{
free(copy);
return ( NULL );
}
switch ( shape -> stype )
{
/*...sSTYPE_PLANE:24:*/
case STYPE_PLANE:
if ( (copy -> u.plane = copy_plane(shape -> u.plane)) == NULL )
{
destroy_surf(copy -> surf);
free(copy);
return ( NULL );
}
break;
/*...e*/
/*...sSTYPE_SPHERE:24:*/
case STYPE_SPHERE:
if ( (copy -> u.sphere = copy_sphere(shape -> u.sphere)) == NULL )
{
destroy_surf(copy -> surf);
free(copy);
return ( NULL );
}
break;
/*...e*/
/*...sSTYPE_QUAD:24:*/
case STYPE_QUAD:
if ( (copy -> u.quad = copy_quad(shape -> u.quad)) == NULL )
{
destroy_surf(copy -> surf);
free(copy);
return ( NULL );
}
break;
/*...e*/
}
}
else
/*...sboolean combinations:16:*/
{
if ( (copy -> u.shapes [0] = copy_shape(shape -> u.shapes [0])) == NULL )
{
free(copy);
return ( NULL );
}
if ( (copy -> u.shapes [1] = copy_shape(shape -> u.shapes [1])) == NULL )
{
free(copy -> u.shapes [0]);
free(copy);
return ( NULL );
}
}
/*...e*/
return ( copy );
}
/*...e*/
/*...sdestroy_shape \45\ delete a shape tree:0:*/
void destroy_shape(SHAPE *shape)
{
if ( shape -> stype <= STYPE_QUAD )
/* Leaf node */
{
destroy_surf(shape -> surf);
switch ( shape -> stype )
{
/*...sSTYPE_PLANE:24:*/
case STYPE_PLANE:
destroy_plane(shape -> u.plane);
break;
/*...e*/
/*...sSTYPE_SPHERE:24:*/
case STYPE_SPHERE:
destroy_sphere(shape -> u.sphere);
break;
/*...e*/
/*...sSTYPE_QUAD:24:*/
case STYPE_QUAD:
destroy_quad(shape -> u.quad);
break;
/*...e*/
}
}
else
{
destroy_shape(shape -> u.shapes [0]);
destroy_shape(shape -> u.shapes [1]);
}
free(shape);
}
/*...e*/
/*...ssphere_to_quad \45\ convert sphere to quad:0:*/
/*
Spheres cannot be rescaled in each direction and remain a sphere.
Hence this code will convert a sphere into the equivelent quadratic.
2 2 2
Sphere: (x-a) +(y-b) +(z-c) -d <=0
2 2 2 2 2 2
x -2ax+a +y -2by+b +z -2cz+c -d <= 0
2 2 2
Quadratic: ax +by +cz +dxy+eyz+fzx+gx+hy+iz+j<=0
*/
static BOOLEAN sphere_to_quad(SHAPE *shape)
{
SPHERE *sphere = shape -> u.sphere;
QUAD *quad;
if ( (quad = create_quad(1.0, 1.0, 1.0, 0.0, 0.0, 0.0,
-2.0 * sphere -> a, -2.0 * sphere -> b, -2.0 * sphere -> c,
- sphere -> d)) == NULL )
return ( FALSE );
destroy_sphere(shape -> u.sphere);
shape -> stype = STYPE_QUAD;
shape -> u.quad = quad;
return ( TRUE );
}
/*...e*/
/*...strans_x \45\ translate by amount in x direction:0:*/
void trans_x(SHAPE *shape, double t)
{
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
trans_x_plane(shape -> u.plane, t);
break;
case STYPE_SPHERE:
trans_x_sphere(shape -> u.sphere, t);
break;
case STYPE_QUAD:
trans_x_quad(shape -> u.quad, t);
break;
}
trans_x_surf(shape -> surf, t);
}
else
{
trans_x(shape -> u.shapes [0], t);
trans_x(shape -> u.shapes [1], t);
}
}
/*...e*/
/*...strans_y \45\ translate by amount in y direction:0:*/
void trans_y(SHAPE *shape, double t)
{
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
trans_y_plane(shape -> u.plane, t);
break;
case STYPE_SPHERE:
trans_y_sphere(shape -> u.sphere, t);
break;
case STYPE_QUAD:
trans_y_quad(shape -> u.quad, t);
break;
}
trans_y_surf(shape -> surf, t);
}
else
{
trans_y(shape -> u.shapes [0], t);
trans_y(shape -> u.shapes [1], t);
}
}
/*...e*/
/*...strans_z \45\ translate by amount in z direction:0:*/
void trans_z(SHAPE *shape, double t)
{
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
trans_z_plane(shape -> u.plane, t);
break;
case STYPE_SPHERE:
trans_z_sphere(shape -> u.sphere, t);
break;
case STYPE_QUAD:
trans_z_quad(shape -> u.quad, t);
break;
}
trans_z_surf(shape -> surf, t);
}
else
{
trans_z(shape -> u.shapes [0], t);
trans_z(shape -> u.shapes [1], t);
}
}
/*...e*/
/*...strans \45\ translate by vector:0:*/
void trans(SHAPE *shape, VECTOR v)
{
trans_x(shape, v.x);
trans_y(shape, v.y);
trans_z(shape, v.z);
}
/*...e*/
/*...sscale_x \45\ scale by factor in x direction:0:*/
BOOLEAN scale_x(SHAPE *shape, double factor)
{
if ( factor == 1.0 )
return ( TRUE );
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
scale_x_plane(shape -> u.plane, factor);
break;
case STYPE_SPHERE:
if ( !sphere_to_quad(shape) )
return ( FALSE );
scale_x_quad(shape -> u.quad, factor);
break;
case STYPE_QUAD:
scale_x_quad(shape -> u.quad, factor);
break;
}
scale_x_surf(shape -> surf, factor);
}
else
{
if ( !scale_x(shape -> u.shapes [0], factor) ||
!scale_x(shape -> u.shapes [1], factor) )
return ( FALSE );
}
return ( TRUE );
}
/*...e*/
/*...sscale_y \45\ scale by factor in y direction:0:*/
BOOLEAN scale_y(SHAPE *shape, double factor)
{
if ( factor == 1.0 )
return ( TRUE );
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
scale_y_plane(shape -> u.plane, factor);
break;
case STYPE_SPHERE:
if ( !sphere_to_quad(shape) )
return ( FALSE );
scale_y_quad(shape -> u.quad, factor);
break;
case STYPE_QUAD:
scale_y_quad(shape -> u.quad, factor);
break;
}
scale_y_surf(shape -> surf, factor);
}
else
{
if ( !scale_y(shape -> u.shapes [0], factor) ||
!scale_y(shape -> u.shapes [1], factor) )
return ( FALSE );
}
return ( TRUE );
}
/*...e*/
/*...sscale_z \45\ scale by factor in z direction:0:*/
BOOLEAN scale_z(SHAPE *shape, double factor)
{
if ( factor == 1.0 )
return ( TRUE );
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
scale_z_plane(shape -> u.plane, factor);
break;
case STYPE_SPHERE:
if ( !sphere_to_quad(shape) )
return ( FALSE );
scale_z_quad(shape -> u.quad, factor);
break;
case STYPE_QUAD:
scale_z_quad(shape -> u.quad, factor);
break;
}
scale_z_surf(shape -> surf, factor);
}
else
{
if ( !scale_z(shape -> u.shapes [0], factor) ||
!scale_z(shape -> u.shapes [1], factor) )
return ( FALSE );
}
return ( TRUE );
}
/*...e*/
/*...sscale \45\ scale by vector factor:0:*/
BOOLEAN scale(SHAPE *shape, VECTOR factor)
{
if ( shape -> stype == STYPE_SPHERE &&
factor.x == factor.y && factor.y == factor.z )
{
scale_sphere(shape -> u.sphere, factor.x);
scale_x_surf(shape -> surf, factor.x);
scale_y_surf(shape -> surf, factor.y);
scale_z_surf(shape -> surf, factor.z);
}
else
{
if ( !scale_x(shape, factor.x) ||
!scale_y(shape, factor.y) ||
!scale_z(shape, factor.z) )
return ( FALSE );
}
return ( TRUE );
}
/*...e*/
/*...srot_x \45\ rotate about x axis by given angle:0:*/
void rot_x(SHAPE *shape, double angle)
{
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
rot_x_plane(shape -> u.plane, angle);
break;
case STYPE_SPHERE:
rot_x_sphere(shape -> u.sphere, angle);
break;
case STYPE_QUAD:
rot_x_quad(shape -> u.quad, angle);
break;
}
rot_x_surf(shape -> surf, angle);
}
else
{
rot_x(shape -> u.shapes [0], angle);
rot_x(shape -> u.shapes [1], angle);
}
}
/*...e*/
/*...srot_y \45\ rotate about y axis by given angle:0:*/
void rot_y(SHAPE *shape, double angle)
{
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
rot_y_plane(shape -> u.plane, angle);
break;
case STYPE_SPHERE:
rot_y_sphere(shape -> u.sphere, angle);
break;
case STYPE_QUAD:
rot_y_quad(shape -> u.quad, angle);
break;
}
rot_y_surf(shape -> surf, angle);
}
else
{
rot_y(shape -> u.shapes [0], angle);
rot_y(shape -> u.shapes [1], angle);
}
}
/*...e*/
/*...srot_z \45\ rotate about z axis by given angle:0:*/
void rot_z(SHAPE *shape, double angle)
{
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
case STYPE_PLANE:
rot_z_plane(shape -> u.plane, angle);
break;
case STYPE_SPHERE:
rot_z_sphere(shape -> u.sphere, angle);
break;
case STYPE_QUAD:
rot_z_quad(shape -> u.quad, angle);
break;
}
rot_z_surf(shape -> surf, angle);
}
else
{
rot_z(shape -> u.shapes [0], angle);
rot_z(shape -> u.shapes [1], angle);
}
}
/*...e*/
/*...sresurf \45\ change the surface of a shape:0:*/
BOOLEAN resurf(SHAPE *shape, SURF *surf)
{
if ( shape -> stype <= STYPE_QUAD )
{
SURF *surf_copy;
if ( (surf_copy = copy_surf(surf)) == NULL )
return ( FALSE );
destroy_surf(shape -> surf);
shape -> surf = surf_copy;
}
else
{
if ( !resurf(shape -> u.shapes [0], surf) )
return ( FALSE );
if ( !resurf(shape -> u.shapes [1], surf) )
return ( FALSE );
}
return ( TRUE );
}
/*...e*/
/*...sis_empty_isectl \45\ is intersection list empty:0:*/
BOOLEAN is_empty_isectl(ISECTL *il)
{
return ( il -> n_isects == 0 );
}
/*...e*/
/*...sis_solid_isectl \45\ is intersection list solid:0:*/
/*
We will allow t from -INFINITE onwards or t from -INFINITE to INFINITE.
*/
BOOLEAN is_solid_isectl(ISECTL *il)
{
if ( il -> n_isects > 2 )
return ( FALSE );
if ( il -> isects [0].t != -INFINITE )
return ( FALSE );
return ( il -> n_isects == 1 || il -> isects [1].t == INFINITE );
}
/*...e*/
/*...st_after_isectl \45\ eliminate all intersections before a t value:0:*/
void t_after_isectl(ISECTL *il, double t)
{
int i;
for ( i = 0; i < il -> n_isects; i++ )
if ( il -> isects [i].t >= t )
break;
if ( i == il -> n_isects )
/* All behind t */
il -> n_isects = 0;
else if ( i != 0 )
/* Some behind and some after t */
{
int j = 0;
if ( il -> isects [i].entering == FALSE )
/* Had better make an isect case first */
{
il -> isects [j ] = il -> isects [i - 1];
il -> isects [j++].t = t;
}
while ( i < il -> n_isects )
il -> isects [j++] = il -> isects [i++];
il -> n_isects = j;
}
}
/*...e*/
/*...screate_isectl \45\ create an isectl:0:*/
ISECTL *create_isectl(int n_isects)
{
return ( malloc(sizeof(ISECTL) + (n_isects - 1) * sizeof(ISECT)) );
}
/*...e*/
/*...sdestroy_isectl \45\ destroy an isectl:0:*/
void destroy_isectl(ISECTL *il)
{
free(il);
}
/*...e*/
/*...spreprocess_shape \45\ preprocess shape tree to save work when tracing:0:*/
/*...sextent_shape:0:*/
/*
If we can find shapes that do not overlap, we can avoid tracing time later.
This type of function is only made possible because the code that implements
spheres etc. export their internal representations of the shapes.
*/
/*...sspheres_overlap:0:*/
/*
2 spheres overlap if the sum of the radii > distance between the centres.
We square both sides of this equation.
*/
static BOOLEAN spheres_overlap(SPHERE *sphere_a, SPHERE *sphere_b)
{
double dx = sphere_a -> a - sphere_b -> a;
double dy = sphere_a -> b - sphere_b -> b;
double dz = sphere_a -> c - sphere_b -> c;
double rr = sphere_a -> d + sphere_b -> d;
return ( rr*rr >= dx*dx + dy*dy + dz*dz );
}
/*...e*/
typedef struct { VECTOR min, max; } EXTENT;
/*...sextent_infinite:0:*/
static EXTENT extent_infinite(void)
{
EXTENT extent;
extent.min.x = extent.min.y = extent.min.z = -INFINITE;
extent.max.x = extent.max.y = extent.max.z = INFINITE;
return ( extent );
}
/*...e*/
/*...sextent_of_sphere:0:*/
static EXTENT extent_of_sphere(SPHERE *sphere)
{
double a = sphere -> a;
double b = sphere -> b;
double c = sphere -> c;
double d = sphere -> d;
EXTENT extent;
extent.min.x = a - d; extent.max.x = a + d;
extent.min.y = b - d; extent.max.y = b + d;
extent.min.z = c - d; extent.max.z = c + d;
return ( extent );
}
/*...e*/
/*...sextents_overlap:0:*/
static BOOLEAN extents_overlap(EXTENT *extent_a, EXTENT *extent_b)
{
return ( extent_a -> max.x > extent_b -> min.x &&
extent_a -> max.y > extent_b -> min.y &&
extent_a -> max.z > extent_b -> min.z &&
extent_b -> max.x > extent_a -> min.x &&
extent_b -> max.y > extent_a -> min.y &&
extent_b -> max.z > extent_a -> min.z );
}
/*...e*/
/*...sextents_union:0:*/
static EXTENT extents_union(EXTENT *extent_a, EXTENT *extent_b)
{
EXTENT extent;
extent.min.x = min(extent_a -> min.x, extent_b -> min.x);
extent.min.y = min(extent_a -> min.y, extent_b -> min.y);
extent.min.z = min(extent_a -> min.z, extent_b -> min.z);
extent.max.x = max(extent_a -> max.x, extent_b -> max.x);
extent.max.y = max(extent_a -> max.y, extent_b -> max.y);
extent.max.z = max(extent_a -> max.z, extent_b -> max.z);
return ( extent );
}
/*...e*/
/*...sextents_isect:0:*/
static EXTENT extents_isect(EXTENT *extent_a, EXTENT *extent_b)
{
EXTENT extent;
extent.min.x = max(extent_a -> min.x, extent_b -> min.x);
extent.min.y = max(extent_a -> min.y, extent_b -> min.y);
extent.min.z = max(extent_a -> min.z, extent_b -> min.z);
extent.max.x = min(extent_a -> max.x, extent_b -> max.x);
extent.max.y = min(extent_a -> max.y, extent_b -> max.y);
extent.max.z = min(extent_a -> max.z, extent_b -> max.z);
return ( extent );
}
/*...e*/
static EXTENT extent_shape(SHAPE *shape)
{
static EXTENT dummy_extent = { { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } };
if ( shape -> stype <= STYPE_QUAD )
{
switch ( shape -> stype )
{
/*...sSTYPE_PLANE\44\ STYPE_QUAD \45\ too difficult:24:*/
case STYPE_PLANE:
case STYPE_QUAD:
return ( extent_infinite() );
/*...e*/
/*...sSTYPE_SPHERE:24:*/
case STYPE_SPHERE:
return ( extent_of_sphere(shape -> u.sphere) );
/*...e*/
}
}
else if ( shape -> stype == STYPE_EXTENT )
{
EXTENT extent;
extent = extent_shape(shape -> u.shapes [0]);
/* Performed for side effect of extent_shape() call */
return ( extent_shape(shape -> u.shapes [1]) );
}
else
{
SHAPE *shape_a = shape -> u.shapes [0];
SHAPE *shape_b = shape -> u.shapes [1];
EXTENT extent_a;
EXTENT extent_b;
EXTENT extent;
extent_a = extent_shape(shape_a);
extent_b = extent_shape(shape_b);
switch ( shape -> stype )
{
/*...sSTYPE_UNION\44\ STYPE_SDIFF:24:*/
case STYPE_UNION:
case STYPE_SDIFF:
extent = extents_union(&extent_a, &extent_b);
break;
/*...e*/
/*...sSTYPE_ISECT:24:*/
case STYPE_ISECT:
extent = extents_isect(&extent_a, &extent_b);
break;
/*...e*/
/*...sSTYPE_DIFF:24:*/
case STYPE_DIFF:
extent = extent_a;
break;
/*...e*/
}
if ( shape_a -> stype == STYPE_SPHERE &&
shape_b -> stype == STYPE_SPHERE )
shape -> overlap = spheres_overlap(
shape_a -> u.sphere, shape_b -> u.sphere);
else
shape -> overlap = extents_overlap(&extent_a, &extent_b);
return ( extent );
}
return ( dummy_extent ); /* Keep fussy C compiler happy */
}
/*...e*/
/*...sn_isectls_reqd_shape:0:*/
/*
We use this function so that we can work out how many intersection lists
we will need to pre-allocate for tracing the shape.
*/
int n_isectls_reqd_shape(SHAPE *shape)
{
int nest0, nest1;
if ( shape -> stype <= STYPE_QUAD )
return ( 1 );
nest0 = n_isectls_reqd_shape(shape -> u.shapes [0]);
nest1 = n_isectls_reqd_shape(shape -> u.shapes [1]);
return ( 2 + max(nest0, nest1) );
}
/*...e*/
/*...sisects_reqd_shape:0:*/
/*
Determine largest needed intersection list.
*/
int isects_reqd_shape(SHAPE *shape)
{
if ( shape -> stype <= STYPE_QUAD )
switch ( shape -> stype )
{
/*...sSTYPE_PLANE:24:*/
case STYPE_PLANE:
return ( isects_reqd_plane(shape -> u.plane) );
/*...e*/
/*...sSTYPE_SPHERE:24:*/
case STYPE_SPHERE:
return ( isects_reqd_sphere(shape -> u.sphere) );
/*...e*/
/*...sSTYPE_QUAD:24:*/
case STYPE_QUAD:
return ( isects_reqd_quad(shape -> u.quad) );
/*...e*/
}
else
{
int reqd0 = isects_reqd_shape(shape -> u.shapes [0]);
int reqd1 = isects_reqd_shape(shape -> u.shapes [1]);
switch ( shape -> stype )
{
/*...sSTYPE_UNION:24:*/
case STYPE_UNION:
return ( reqd0 + reqd1 );
/*...e*/
/*...sSTYPE_ISECT:24:*/
case STYPE_ISECT:
return ( ( shape -> overlap ) ? reqd0 + reqd1 : 0 );
/*...e*/
/*...sSTYPE_DIFF:24:*/
case STYPE_DIFF:
return ( ( shape -> overlap ) ? reqd0 + reqd1 : reqd0 );
/*...e*/
/*...sSTYPE_SDIFF:24:*/
case STYPE_SDIFF:
return ( reqd0 + reqd1 );
/*...e*/
/*...sSTYPE_EXTENT:24:*/
case STYPE_EXTENT:
return ( max(reqd0, reqd1) );
/*...e*/
}
}
return ( 0 ); /* Keep fussy C compiler happy */
}
/*...e*/
void preprocess_shape(SHAPE *shape, int *n_isectls, int *n_isects)
{
EXTENT extent;
extent = extent_shape(shape);
/* The side effect is to label the shape tree */
*n_isectls = n_isectls_reqd_shape(shape);
*n_isects = isects_reqd_shape(shape);
}
/*...e*/
/*...sintersect_shape \45\ intersect with a shape to give a ISECTL:0:*/
/*
Given a shape tree, find the intersection list of a vector with it.
If the shape tree is not a leaf node, then combine the intersection lists of
the subtrees. We can make several optimisations in this area.
*/
/*...smake_empty_isectl:0:*/
static void make_empty_isectl(ISECTL *il)
{
il -> n_isects = 0;
}
/*...e*/
/*...scopy_isectl:0:*/
static void copy_isectl(ISECTL *il_dst, ISECTL *il_src)
{
int i;
il_dst -> n_isects = il_src -> n_isects;
for ( i = 0; i < il_dst -> n_isects; i++ )
il_dst -> isects [i] = il_src -> isects [i];
}
/*...e*/
/*...scombine_isectl:0:*/
/*
Given 2 intersection lists, produce a new one that is a combination of them.
The in_old flag is used to determine if the point of intersection changes
meaning from in->out to out->in, or vice-versa. If this happens, the
sense of the normal vector must change :-
..... ..... .....
. . . . .
. A . . B . . A-B .
. . . . . .
<-. <-. .-> .-> <-. .-> This vector changes direction!
. . . . . .
. . . . . .
. . . . .
..... ..... .....
*/
static void combine_isectl(
BOOLEAN (*combine)(BOOLEAN in_a, BOOLEAN in_b),
ISECTL *il_a, ISECTL *il_b, ISECTL *il)
{
BOOLEAN in_a = FALSE; /* When t = -INFINITE, in_a = FALSE */
BOOLEAN in_b = FALSE; /* When t = -INFINITE, in_b = FALSE */
BOOLEAN in = FALSE; /* Therefore combination starts as FALSE */
int ptr_a = 0;
int ptr_b = 0;
BOOLEAN in_new, in_old;
il -> n_isects = 0;
/* Work through both a and b, looking at nearest ones first */
while ( ptr_a < il_a -> n_isects && ptr_b < il_b -> n_isects )
{
ISECT *isect;
double t_a = il_a -> isects [ptr_a].t;
double t_b = il_b -> isects [ptr_b].t;
if ( t_a < t_b )
{
in_old = in_a = !in_a;
isect = &(il_a -> isects [ptr_a++]);
}
else if ( t_a > t_b )
{
in_old = in_b = !in_b;
isect = &(il_b -> isects [ptr_b++]);
}
else
/* Two surfaces at exactly the same place */
/* Not a very frequent event, but problematical */
/* Just label intersection arbitrarily as with B */
{
in_a = !in_a; ptr_a++;
in_old = in_b = !in_b;
isect = &(il_b -> isects [ptr_b++]);
}
if ( (in_new = (*combine)(in_a, in_b)) != in )
/* Need to keep a record of this transition */
{
il -> isects [il -> n_isects] = *isect;
il -> isects [il -> n_isects].entering = in = in_new;
if ( in_new != in_old )
il -> isects [il -> n_isects].negate_normal ^= TRUE;
il -> n_isects++;
}
}
/* Either a or b is exhausted, so one of a or b may be left */
while ( ptr_a < il_a -> n_isects )
{
in_old = in_a = !in_a;
if ( (in_new = (*combine)(in_a, in_b)) != in )
/* Need to keep a record of this transition */
{
il -> isects [il -> n_isects] = il_a -> isects [ptr_a];
il -> isects [il -> n_isects].entering = in = in_new;
if ( in_new != in_old )
il -> isects [il -> n_isects].negate_normal ^= TRUE;
il -> n_isects++;
}
ptr_a++;
}
while ( ptr_b < il_b -> n_isects )
{
in_old = in_b = !in_b;
if ( (in_new = (*combine)(in_a, in_b)) != in )
/* Need to keep a record of this transition */
{
il -> isects [il -> n_isects] = il_b -> isects [ptr_b];
il -> isects [il -> n_isects].entering = in = in_new;
if ( in_new != in_old )
il -> isects [il -> n_isects].negate_normal ^= TRUE;
il -> n_isects++;
}
ptr_b++;
}
}
/*...e*/
/*...sunion_isectl:0:*/
/*...scombine_union:0:*/
static BOOLEAN combine_union(BOOLEAN in_a, BOOLEAN in_b)
{
return ( in_a || in_b );
}
/*...e*/
static void union_isectl(ISECTL *il_a, ISECTL *il_b, ISECTL *il)
{
combine_isectl(combine_union, il_a, il_b, il);
}
/*...e*/
/*...sisect_isectl:0:*/
/*...scombine_isect:0:*/
static BOOLEAN combine_isect(BOOLEAN in_a, BOOLEAN in_b)
{
return ( in_a && in_b );
}
/*...e*/
static void isect_isectl(ISECTL *il_a, ISECTL *il_b, ISECTL *il)
{
combine_isectl(combine_isect, il_a, il_b, il);
}
/*...e*/
/*...sdiff_isectl:0:*/
/*...scombine_diff:0:*/
static BOOLEAN combine_diff(BOOLEAN in_a, BOOLEAN in_b)
{
return ( in_a && !in_b );
}
/*...e*/
static void diff_isectl(ISECTL *il_a, ISECTL *il_b, ISECTL *il)
{
combine_isectl(combine_diff, il_a, il_b, il);
}
/*...e*/
/*...ssdiff_isectl:0:*/
/*...scombine_sdiff:0:*/
static BOOLEAN combine_sdiff(BOOLEAN in_a, BOOLEAN in_b)
{
return ( in_a ^ in_b );
}
/*...e*/
static void sdiff_isectl(ISECTL *il_a, ISECTL *il_b, ISECTL *il)
{
combine_isectl(combine_sdiff, il_a, il_b, il);
}
/*...e*/
/*...sconcat_isectl:0:*/
/*
This is a quick case of unioning two intersection lists for use when it is
known that they do not overlap.
*/
static void concat_isectl(ISECTL *il_a, ISECTL *il_b, ISECTL *il)
{
ISECTL *il_rest;
int i, j;
if ( il_a -> n_isects == 0 )
{
copy_isectl(il, il_b);
return;
}
if ( il_b -> n_isects == 0 )
{
copy_isectl(il, il_a);
return;
}
if ( il_a -> isects [0].t < il_b -> isects [0].t )
{
copy_isectl(il, il_a);
il_rest = il_b;
}
else
{
copy_isectl(il, il_b);
il_rest = il_a;
}
for ( i = 0, j = il -> n_isects; i < il_rest -> n_isects; i++, j++ )
il -> isects [j] = il_rest -> isects [i];
il -> n_isects = j;
}
/*...e*/
void intersect_shape(SHAPE *shape, VECTOR p, VECTOR q, ISECTL *ils [])
{
if ( shape -> stype <= STYPE_QUAD )
/* Is a leaf node */
{
SIL sil;
int i;
switch ( shape -> stype )
{
/*...sSTYPE_PLANE:24:*/
case STYPE_PLANE:
intersect_plane(shape -> u.plane, p, q, &sil);
break;
/*...e*/
/*...sSTYPE_SPHERE:24:*/
case STYPE_SPHERE:
intersect_sphere(shape -> u.sphere, p, q, &sil);
break;
/*...e*/
/*...sSTYPE_QUAD:24:*/
case STYPE_QUAD:
intersect_quad(shape -> u.quad, p, q, &sil);
break;
/*...e*/
}
ils [0] -> n_isects = sil.n_sis;
for ( i = 0; i < sil.n_sis; i++ )
{
ils [0] -> isects [i].t = sil.sis [i].t;
ils [0] -> isects [i].entering = sil.sis [i].entering;
ils [0] -> isects [i].shape = shape;
ils [0] -> isects [i].negate_normal = FALSE;
}
}
else
/* Binary combination of two subtrees */
{
SHAPE *shape_a = shape -> u.shapes [0];
SHAPE *shape_b = shape -> u.shapes [1];
switch ( shape -> stype )
{
/*...sSTYPE_UNION:24:*/
case STYPE_UNION:
if ( shape -> overlap )
{
intersect_shape(shape_b, p, q, ils + 1);
if ( is_empty_isectl(ils [1]) )
intersect_shape(shape_a, p, q, ils);
else if ( is_solid_isectl(ils [1]) )
copy_isectl(ils [0], ils [1]);
else
{
intersect_shape(shape_a, p, q, ils + 2);
union_isectl(ils [2], ils [1], ils [0]);
}
}
else
/* No overlap, treat like concatentation */
{
intersect_shape(shape_a, p, q, ils + 1);
intersect_shape(shape_b, p, q, ils + 2);
concat_isectl(ils [1], ils [2], ils [0]);
}
break;
/*...e*/
/*...sSTYPE_ISECT:24:*/
case STYPE_ISECT:
if ( shape -> overlap )
{
intersect_shape(shape_b, p, q, ils + 1);
if ( is_empty_isectl(ils [1]) )
make_empty_isectl(ils [0]);
else if ( is_solid_isectl(ils [1]) )
intersect_shape(shape_a, p, q, ils);
else
{
intersect_shape(shape_a, p, q, ils + 2);
isect_isectl(ils [2], ils [1], ils [0]);
}
}
else
make_empty_isectl(ils [0]);
break;
/*...e*/
/*...sSTYPE_DIFF:24:*/
case STYPE_DIFF:
if ( shape -> overlap )
{
intersect_shape(shape_b, p, q, ils + 1);
if ( is_empty_isectl(ils [1]) )
intersect_shape(shape_a, p, q, ils);
else if ( is_solid_isectl(ils [1]) )
make_empty_isectl(ils [0]);
else
{
intersect_shape(shape_a, p, q, ils + 2);
diff_isectl(ils [2], ils [1], ils [0]);
}
}
else
intersect_shape(shape_a, p, q, ils);
break;
/*...e*/
/*...sSTYPE_SDIFF:24:*/
case STYPE_SDIFF:
if ( shape -> overlap )
{
intersect_shape(shape_b, p, q, ils + 1);
if ( is_empty_isectl(ils [1]) )
intersect_shape(shape_a, p, q, ils);
else
{
intersect_shape(shape_a, p, q, ils + 2);
sdiff_isectl(ils [2], ils [1], ils [0]);
}
}
else
/* No overlap, treat like concatentation */
{
intersect_shape(shape_a, p, q, ils + 1);
intersect_shape(shape_b, p, q, ils + 2);
concat_isectl(ils [1], ils [2], ils [0]);
}
break;
/*...e*/
/*...sSTYPE_EXTENT:24:*/
case STYPE_EXTENT:
intersect_shape(shape_b, p, q, ils);
if ( !is_empty_isectl(ils [0]) )
intersect_shape(shape_a, p, q, ils);
break;
/*...e*/
}
}
}
/*...e*/
/*...snormal_to_shape \45\ find normal at point on edge of shape:0:*/
VECTOR normal_to_shape(SHAPE *shape, VECTOR p)
{
static VECTOR dummy_vector = { 0.0, 0.0, 0.0 };
switch ( shape -> stype )
{
case STYPE_PLANE:
return ( normal_to_plane(shape -> u.plane) );
case STYPE_SPHERE:
return ( normal_to_sphere(shape -> u.sphere, p) );
case STYPE_QUAD:
return ( normal_to_quad(shape -> u.quad, p) );
}
return ( dummy_vector ); /* Keep fussy C compiler happy */
}
/*...e*/