You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/lprof/cmshull.cpp

1481 lines
38 KiB

/* */
/* Little cms - profiler construction set */
/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
/* */
/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
/* */
/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
/* OF THIS SOFTWARE. */
/* */
/* This file is free software; you can redistribute it and/or modify it */
/* under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
/* General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
/* */
/* As a special exception to the GNU General Public License, if you */
/* distribute this file as part of a program that contains a */
/* configuration script generated by Autoconf, you may include it under */
/* the same distribution terms that you use for the rest of that program. */
/* */
/* Version 1.09a */
#include "lcmsprf.h"
/* Convex hull management */
LCMSHANDLE cdecl cmsxHullInit(void);
void cdecl cmsxHullDone(LCMSHANDLE hHull);
BOOL cdecl cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z);
BOOL cdecl cmsxHullComputeHull(LCMSHANDLE hHull);
char cdecl cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z);
BOOL cdecl cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname);
/* --------------------------------------------------------------------- */
/* This method is described in "Computational Geometry in C" Chapter 4. */
/* */
/* -------------------------------------------------------------------- */
/* This code is Copyright 1998 by Joseph O'Rourke. It may be freely */
/* redistributed in its entirety provided that this copyright notice is */
/* not removed. */
/* -------------------------------------------------------------------- */
#define SWAP(t,x,y) { t = x; x = y; y = t; }
#define XFREE(p)
/* if (p) { free ((char *) p); p = NULL; } */
#define ADD( head, p ) if ( head ) { \
p->Next = head; \
p->Prev = head->Prev; \
head->Prev = p; \
p->Prev->Next = p; \
} \
else { \
head = p; \
head->Next = head->Prev = p; \
}
#define XDELETE( head, p ) if ( head ) { \
if ( head == head->Next ) \
head = NULL; \
else if ( p == head ) \
head = head->Next; \
p->Next->Prev = p->Prev; \
p->Prev->Next = p->Next; \
XFREE( p ); \
}
/* Define Vertex indices. */
#define X 0
#define Y 1
#define Z 2
/* Define structures for vertices, edges and faces */
typedef struct _vertex_struct VERTEX,FAR *LPVERTEX;
typedef struct _edge_struct EDGE, FAR *LPEDGE;
typedef struct _face_struct FACE, FAR *LPFACE;
struct _edge_struct {
LPFACE AdjFace[2];
LPVERTEX EndPts[2];
LPFACE NewFace; /* pointer to incident cone face. */
BOOL DoDelete; /* T iff Edge should be delete. */
LPEDGE Next, Prev;
};
struct _face_struct {
LPEDGE Edge[3];
LPVERTEX Vertex[3];
BOOL Visible; /* T iff face Visible from new point. */
LPFACE Next, Prev;
};
struct _vertex_struct {
int v[3];
int vnum;
LPEDGE duplicate; /* pointer to incident cone Edge (or NULL) */
BOOL onhull; /* T iff point on hull. */
BOOL mark; /* T iff point already processed. */
LPVERTEX Next, Prev;
};
/* Define flags */
#define ONHULL true
#define REMOVED true
#define VISIBLE true
#define PROCESSED true
#define SAFE 1000000 /* Range of safe coord values. */
#define DIM 3 /* Dimension of points */
typedef int VEC3I[DIM]; /* Type integer point */
#define PMAX 10000 /* Max # of pts */
typedef struct {
/* Global variable definitions */
LPVERTEX vertices;
LPEDGE edges;
LPFACE faces;
VEC3I Vertices[PMAX]; /* All the points */
VEC3I Faces[PMAX]; /* Each triangle face is 3 indices */
VEC3I Box[PMAX][2]; /* Box around each face */
VEC3I bmin, bmax;
int radius;
int vnumCounter;
int nfaces;
int nvertex;
} HULL, FAR* LPHULL;
/* static HULL Global; */
/*---------------------------------------------------------------------
MakeNullVertex: Makes a Vertex, nulls out fields.
---------------------------------------------------------------------*/
static
LPVERTEX MakeNullVertex(LPHULL hull)
{
LPVERTEX v;
v = (LPVERTEX) malloc(sizeof(VERTEX));
if (!v) return NULL;
v->duplicate = NULL;
v->onhull = !ONHULL;
v->mark = !PROCESSED;
ADD( hull->vertices, v );
return v;
}
/*---------------------------------------------------------------------
MakeNullEdge creates a new cell and initializes all pointers to NULL
and sets all flags to off. It returns a pointer to the empty cell.
---------------------------------------------------------------------*/
static
LPEDGE MakeNullEdge(LPHULL hull)
{
LPEDGE e;
e = (LPEDGE) malloc(sizeof(EDGE));
if (!e) return NULL;
e->AdjFace[0] = e->AdjFace[1] = e->NewFace = NULL;
e->EndPts[0] = e->EndPts[1] = NULL;
e->DoDelete = !REMOVED;
ADD( hull->edges, e );
return e;
}
/*--------------------------------------------------------------------
MakeNullFace creates a new face structure and initializes all of its
flags to NULL and sets all the flags to off. It returns a pointer
to the empty cell.
---------------------------------------------------------------------*/
static
LPFACE MakeNullFace(LPHULL hull)
{
LPFACE f;
int i;
f = (LPFACE) malloc(sizeof(FACE));
if (!f) return NULL;
for ( i=0; i < 3; ++i ) {
f->Edge[i] = NULL;
f->Vertex[i] = NULL;
}
f->Visible = !VISIBLE;
ADD( hull->faces, f );
return f;
}
/*---------------------------------------------------------------------
MakeFace creates a new face structure from three vertices (in ccw
order). It returns a pointer to the face.
---------------------------------------------------------------------*/
static
LPFACE MakeFace(LPHULL hull, LPVERTEX v0, LPVERTEX v1, LPVERTEX v2, LPFACE fold)
{
LPFACE f;
LPEDGE e0, e1, e2;
/* Create edges of the initial triangle. */
if( !fold ) {
e0 = MakeNullEdge(hull);
e1 = MakeNullEdge(hull);
e2 = MakeNullEdge(hull);
}
else { /* Copy from fold, in reverse order. */
e0 = fold->Edge[2];
e1 = fold->Edge[1];
e2 = fold->Edge[0];
}
e0->EndPts[0] = v0; e0->EndPts[1] = v1;
e1->EndPts[0] = v1; e1->EndPts[1] = v2;
e2->EndPts[0] = v2; e2->EndPts[1] = v0;
/* Create face for triangle. */
f = MakeNullFace(hull);
f->Edge[0] = e0; f->Edge[1] = e1; f->Edge[2] = e2;
f->Vertex[0] = v0; f->Vertex[1] = v1; f->Vertex[2] = v2;
/* Link edges to face. */
e0->AdjFace[0] = e1->AdjFace[0] = e2->AdjFace[0] = f;
return f;
}
/*---------------------------------------------------------------------
Collinear checks to see if the three points given are collinear,
by checking to see if each element of the cross product is zero.
---------------------------------------------------------------------*/
static
BOOL Collinear( LPVERTEX a, LPVERTEX b, LPVERTEX c )
{
return
( c->v[Z] - a->v[Z] ) * ( b->v[Y] - a->v[Y] ) -
( b->v[Z] - a->v[Z] ) * ( c->v[Y] - a->v[Y] ) == 0
&& ( b->v[Z] - a->v[Z] ) * ( c->v[X] - a->v[X] ) -
( b->v[X] - a->v[X] ) * ( c->v[Z] - a->v[Z] ) == 0
&& ( b->v[X] - a->v[X] ) * ( c->v[Y] - a->v[Y] ) -
( b->v[Y] - a->v[Y] ) * ( c->v[X] - a->v[X] ) == 0 ;
}
/*---------------------------------------------------------------------
VolumeSign returns the sign of the volume of the tetrahedron determined by f
and p. VolumeSign is +1 iff p is on the negative side of f,
where the positive side is determined by the rh-rule. So the volume
is positive if the ccw normal to f points outside the tetrahedron.
The final fewer-multiplications form is due to Bob Williamson.
---------------------------------------------------------------------*/
int VolumeSign( LPFACE f, LPVERTEX p )
{
double vol;
double ax, ay, az, bx, by, bz, cx, cy, cz;
ax = f->Vertex[0]->v[X] - p->v[X];
ay = f->Vertex[0]->v[Y] - p->v[Y];
az = f->Vertex[0]->v[Z] - p->v[Z];
bx = f->Vertex[1]->v[X] - p->v[X];
by = f->Vertex[1]->v[Y] - p->v[Y];
bz = f->Vertex[1]->v[Z] - p->v[Z];
cx = f->Vertex[2]->v[X] - p->v[X];
cy = f->Vertex[2]->v[Y] - p->v[Y];
cz = f->Vertex[2]->v[Z] - p->v[Z];
vol = ax * (by*cz - bz*cy)
+ ay * (bz*cx - bx*cz)
+ az * (bx*cy - by*cx);
/* The volume should be an integer. */
if ( vol > 0.5 ) return 1;
else if ( vol < -0.5 ) return -1;
else return 0;
}
/*---------------------------------------------------------------------
CleanEdges runs through the Edge list and cleans up the structure.
If there is a NewFace then it will put that face in place of the
Visible face and NULL out NewFace. It also deletes so marked edges.
---------------------------------------------------------------------*/
static
void CleanEdges(LPHULL hull)
{
LPEDGE e; /* Primary index into Edge list. */
LPEDGE t; /* Temporary Edge pointer. */
/* Integrate the NewFace's into the data structure. */
/* Check every Edge. */
e = hull ->edges;
do {
if ( e->NewFace ) {
if ( e->AdjFace[0]->Visible )
e->AdjFace[0] = e->NewFace;
else
e->AdjFace[1] = e->NewFace;
e->NewFace = NULL;
}
e = e->Next;
} while ( e != hull ->edges );
/* Delete any edges marked for deletion. */
while ( hull ->edges && hull ->edges->DoDelete ) {
e = hull ->edges;
XDELETE( hull ->edges, e );
}
e = hull ->edges->Next;
do {
if ( e->DoDelete ) {
t = e;
e = e->Next;
XDELETE( hull ->edges, t );
}
else e = e->Next;
} while ( e != hull ->edges );
}
/*---------------------------------------------------------------------
CleanFaces runs through the face list and deletes any face marked Visible.
---------------------------------------------------------------------*/
static
void CleanFaces(LPHULL hull)
{
LPFACE f; /* Primary pointer into face list. */
LPFACE t; /* Temporary pointer, for deleting. */
while ( hull ->faces && hull ->faces->Visible ) {
f = hull ->faces;
XDELETE( hull ->faces, f );
}
f = hull ->faces->Next;
do {
if ( f->Visible ) {
t = f;
f = f->Next;
XDELETE( hull ->faces, t );
}
else f = f->Next;
} while ( f != hull ->faces );
}
/*---------------------------------------------------------------------
CleanVertices runs through the Vertex list and deletes the
vertices that are marked as processed but are not incident to any
undeleted edges.
---------------------------------------------------------------------*/
static
void CleanVertices(LPHULL hull)
{
LPEDGE e;
LPVERTEX v, t;
/* Mark all vertices incident to some undeleted Edge as on the hull. */
e = hull ->edges;
do {
e->EndPts[0]->onhull = e->EndPts[1]->onhull = ONHULL;
e = e->Next;
} while (e != hull ->edges);
/* Delete all vertices that have been processed but
are not on the hull. */
while ( hull ->vertices && hull->vertices->mark && !hull ->vertices->onhull ) {
v = hull ->vertices;
XDELETE(hull ->vertices, v );
}
v = hull ->vertices->Next;
do {
if (v->mark && !v->onhull ) {
t = v;
v = v->Next;
XDELETE(hull ->vertices, t )
}
else
v = v->Next;
} while ( v != hull ->vertices );
/* Reset flags. */
v = hull ->vertices;
do {
v->duplicate = NULL;
v->onhull = !ONHULL;
v = v->Next;
} while (v != hull->vertices );
}
/*---------------------------------------------------------------------
MakeCcw puts the vertices in the face structure in counterclock wise
order. We want to store the vertices in the same
order as in the Visible face. The third Vertex is always p.
Although no specific ordering of the edges of a face are used
by the code, the following condition is maintained for each face f:
one of the two endpoints of f->Edge[i] matches f->Vertex[i].
But note that this does not imply that f->Edge[i] is between
f->Vertex[i] and f->Vertex[(i+1)%3]. (Thanks to Bob Williamson.)
---------------------------------------------------------------------*/
static
void MakeCcw(LPFACE f, LPEDGE e, LPVERTEX p)
{
LPFACE fv; /* The Visible face adjacent to e */
int i; /* Index of e->endpoint[0] in fv. */
LPEDGE s; /* Temporary, for swapping */
if (e->AdjFace[0]->Visible)
fv = e->AdjFace[0];
else
fv = e->AdjFace[1];
/* Set Vertex[0] & [1] of f to have the same orientation
as do the corresponding vertices of fv. */
for ( i=0; fv->Vertex[i] != e->EndPts[0]; ++i )
;
/* Orient f the same as fv. */
if ( fv->Vertex[ (i+1) % 3 ] != e->EndPts[1] ) {
f->Vertex[0] = e->EndPts[1];
f->Vertex[1] = e->EndPts[0];
}
else {
f->Vertex[0] = e->EndPts[0];
f->Vertex[1] = e->EndPts[1];
SWAP( s, f->Edge[1], f->Edge[2] );
}
/* This swap is tricky. e is Edge[0]. Edge[1] is based on endpt[0],
Edge[2] on endpt[1]. So if e is oriented "forwards," we
need to move Edge[1] to follow [0], because it precedes. */
f->Vertex[2] = p;
}
/*---------------------------------------------------------------------
MakeConeFace makes a new face and two new edges between the
Edge and the point that are passed to it. It returns a pointer to
the new face.
---------------------------------------------------------------------*/
static
LPFACE MakeConeFace(LPHULL hull, LPEDGE e, LPVERTEX p)
{
LPEDGE new_edge[2];
LPFACE new_face;
int i, j;
/* Make two new edges (if don't already exist). */
for ( i=0; i < 2; ++i )
/* If the Edge exists, copy it into new_edge. */
if ( !( new_edge[i] = e->EndPts[i]->duplicate) ) {
/* Otherwise (duplicate is NULL), MakeNullEdge. */
new_edge[i] = MakeNullEdge(hull);
new_edge[i]->EndPts[0] = e->EndPts[i];
new_edge[i]->EndPts[1] = p;
e->EndPts[i]->duplicate = new_edge[i];
}
/* Make the new face. */
new_face = MakeNullFace(hull);
new_face->Edge[0] = e;
new_face->Edge[1] = new_edge[0];
new_face->Edge[2] = new_edge[1];
MakeCcw( new_face, e, p );
/* Set the adjacent face pointers. */
for ( i=0; i < 2; ++i )
for ( j=0; j < 2; ++j )
/* Only one NULL link should be set to new_face. */
if ( !new_edge[i]->AdjFace[j] ) {
new_edge[i]->AdjFace[j] = new_face;
break;
}
return new_face;
}
/*---------------------------------------------------------------------
AddOne is passed a Vertex. It first determines all faces Visible from
that point. If none are Visible then the point is marked as not
onhull. Next is a loop over edges. If both faces adjacent to an Edge
are Visible, then the Edge is marked for deletion. If just one of the
adjacent faces is Visible then a new face is constructed.
---------------------------------------------------------------------*/
static
BOOL AddOne(LPHULL hull, LPVERTEX p)
{
LPFACE f;
LPEDGE e, temp;
int vol;
BOOL vis = false;
/* Mark faces Visible from p. */
f = hull -> faces;
do {
vol = VolumeSign(f, p);
if ( vol < 0 ) {
f->Visible = VISIBLE;
vis = true;
}
f = f->Next;
} while ( f != hull ->faces );
/* If no faces are Visible from p, then p is inside the hull. */
if ( !vis ) {
p->onhull = !ONHULL;
return false;
}
/* Mark edges in interior of Visible region for deletion.
Erect a NewFace based on each border Edge. */
e = hull ->edges;
do {
temp = e->Next;
if ( e->AdjFace[0]->Visible && e->AdjFace[1]->Visible )
/* e interior: mark for deletion. */
e->DoDelete = REMOVED;
else
if ( e->AdjFace[0]->Visible || e->AdjFace[1]->Visible )
/* e border: make a new face. */
e->NewFace = MakeConeFace(hull, e, p );
e = temp;
} while ( e != hull ->edges );
return true;
}
/*---------------------------------------------------------------------
DoubleTriangle builds the initial double triangle. It first finds 3
noncollinear points and makes two faces out of them, in opposite order.
It then finds a fourth point that is not coplanar with that face. The
vertices are stored in the face structure in counterclockwise order so
that the volume between the face and the point is negative. Lastly, the
3 newfaces to the fourth point are constructed and the data structures
are cleaned up.
---------------------------------------------------------------------*/
static
BOOL DoubleTriangle(LPHULL hull)
{
LPVERTEX v0, v1, v2, v3;
LPFACE f0, f1 = NULL;
int vol;
/* Find 3 noncollinear points. */
v0 = hull ->vertices;
while ( Collinear( v0, v0->Next, v0->Next->Next ) )
if ( ( v0 = v0->Next ) == hull->vertices )
return false; /* All points are Collinear! */
v1 = v0->Next;
v2 = v1->Next;
/* Mark the vertices as processed. */
v0->mark = PROCESSED;
v1->mark = PROCESSED;
v2->mark = PROCESSED;
/* Create the two "twin" faces. */
f0 = MakeFace(hull, v0, v1, v2, f1 );
f1 = MakeFace(hull, v2, v1, v0, f0 );
/* Link adjacent face fields. */
f0->Edge[0]->AdjFace[1] = f1;
f0->Edge[1]->AdjFace[1] = f1;
f0->Edge[2]->AdjFace[1] = f1;
f1->Edge[0]->AdjFace[1] = f0;
f1->Edge[1]->AdjFace[1] = f0;
f1->Edge[2]->AdjFace[1] = f0;
/* Find a fourth, noncoplanar point to form tetrahedron. */
v3 = v2->Next;
vol = VolumeSign( f0, v3 );
while ( !vol ) {
if ( ( v3 = v3->Next ) == v0 )
return false; /* All points are coplanar! */
vol = VolumeSign( f0, v3 );
}
/* Insure that v3 will be the first added. */
hull ->vertices = v3;
return true;
}
/*---------------------------------------------------------------------
ConstructHull adds the vertices to the hull one at a time. The hull
vertices are those in the list marked as onhull.
---------------------------------------------------------------------*/
static
void ConstructHull(LPHULL hull)
{
LPVERTEX v, vnext;
BOOL changed; /* T if addition changes hull; not used. */
v = hull->vertices;
do {
vnext = v->Next;
changed = false;
if (!v->mark ) {
v->mark = PROCESSED;
changed = AddOne(hull, v );
CleanEdges(hull);
CleanFaces(hull);
CleanVertices(hull);
}
v = vnext;
} while (v != hull->vertices );
}
/*-------------------------------------------------------------------*/
static
void AddVec( VEC3I q, VEC3I ray )
{
int i;
for( i = 0; i < DIM; i++ )
ray[i] = q[i] + ray[i];
}
/*---------------------------------------------------------------------
a - b ==> c.
---------------------------------------------------------------------*/
static
void SubVec( VEC3I a, VEC3I b, VEC3I c )
{
int i;
for( i = 0; i < DIM; i++ )
c[i] = a[i] - b[i];
}
/*---------------------------------------------------------------------
Returns the dot product of the two input vectors.
---------------------------------------------------------------------*/
static
double Dot( VEC3I a, LPVEC3 b )
{
int i;
double sum = 0.0;
for( i = 0; i < DIM; i++ )
sum += a[i] * b->n[i];
return sum;
}
/*---------------------------------------------------------------------
Compute the cross product of (b-a)x(c-a) and place into N.
---------------------------------------------------------------------*/
static
void NormalVec( VEC3I a, VEC3I b, VEC3I c, LPVEC3 N )
{
N->n[X] = ( c[Z] - a[Z] ) * ( b[Y] - a[Y] ) -
( b[Z] - a[Z] ) * ( c[Y] - a[Y] );
N->n[Y] = ( b[Z] - a[Z] ) * ( c[X] - a[X] ) -
( b[X] - a[X] ) * ( c[Z] - a[Z] );
N->n[Z] = ( b[X] - a[X] ) * ( c[Y] - a[Y] ) -
( b[Y] - a[Y] ) * ( c[X] - a[X] );
}
static
int InBox( VEC3I q, VEC3I bmin, VEC3I bmax )
{
if( ( bmin[X] <= q[X] ) && ( q[X] <= bmax[X] ) &&
( bmin[Y] <= q[Y] ) && ( q[Y] <= bmax[Y] ) &&
( bmin[Z] <= q[Z] ) && ( q[Z] <= bmax[Z] ) )
return true;
return false;
}
/*
This function returns a char:
'0': the segment [ab] does not intersect (completely misses) the
bounding box surrounding the n-th triangle T. It lies
strictly to one side of one of the six supporting planes.
'?': status unknown: the segment may or may not intersect T.
*/
static
char BoxTest(LPHULL hull, int n, VEC3I a, VEC3I b)
{
int i; /* Coordinate index */
int w;
for ( i=0; i < DIM; i++ ) {
w = hull ->Box[ n ][0][i]; /* min: lower left */
if ( (a[i] < w) && (b[i] < w) ) return '0';
w = hull ->Box[ n ][1][i]; /* max: upper right */
if ( (a[i] > w) && (b[i] > w) ) return '0';
}
return '?';
}
/* Return a random ray endpoint */
static
void RandomRay( VEC3I ray, int radius )
{
double x, y, z, w, t;
/* Generate a random point on a sphere of radius 1. */
/* the sphere is sliced at z, and a random point at angle t
generated on the circle of intersection. */
z = 2.0 * (double) rand() / RAND_MAX - 1.0;
t = 2.0 * M_PI * (double) rand() / RAND_MAX;
w = sqrt( 1 - z*z );
x = w * cos( t );
y = w * sin( t );
ray[X] = (int) ( radius * x );
ray[Y] = (int) ( radius * y );
ray[Z] = (int) ( radius * z );
}
static
int ComputeBox(LPHULL hull, int F, VEC3I bmin, VEC3I bmax )
{
int i, j;
double radius;
for( i = 0; i < F; i++ )
for( j = 0; j < DIM; j++ ) {
if( hull ->Vertices[i][j] < bmin[j] )
bmin[j] = hull ->Vertices[i][j];
if( hull ->Vertices[i][j] > bmax[j] )
bmax[j] = hull ->Vertices[i][j];
}
radius = sqrt( pow( (double)(bmax[X] - bmin[X]), 2.0 ) +
pow( (double)(bmax[Y] - bmin[Y]), 2.0 ) +
pow( (double)(bmax[Z] - bmin[Z]), 2.0 ) );
return (int)( radius +1 ) + 1;
}
/*---------------------------------------------------------------------
Computes N & D and returns index m of largest component.
---------------------------------------------------------------------*/
static
int PlaneCoeff(LPHULL hull, VEC3I T, LPVEC3 N, double *D )
{
int i;
double t; /* Temp storage */
double biggest = 0.0; /* Largest component of normal vector. */
int m = 0; /* Index of largest component. */
NormalVec(hull ->Vertices[T[0]], hull ->Vertices[T[1]], hull ->Vertices[T[2]], N );
*D = Dot( hull ->Vertices[T[0]], N );
/* Find the largest component of N. */
for ( i = 0; i < DIM; i++ ) {
t = fabs( N->n[i] );
if ( t > biggest ) {
biggest = t;
m = i;
}
}
return m;
}
/*---------------------------------------------------------------------
'p': The segment lies wholly within the plane.
'q': The q endpoint is on the plane (but not 'p').
'r': The r endpoint is on the plane (but not 'p').
'0': The segment lies strictly to one side or the other of the plane.
'1': The segement intersects the plane, and 'p' does not hold.
---------------------------------------------------------------------*/
static
char SegPlaneInt(LPHULL hull, VEC3I T, VEC3I q, VEC3I r, LPVEC3 p, int *m)
{
VEC3 N; double D;
VEC3I rq;
double num, denom, t;
int i;
*m = PlaneCoeff(hull, T, &N, &D );
num = D - Dot( q, &N );
SubVec( r, q, rq );
denom = Dot( rq, &N );
if ( denom == 0.0 ) { /* Segment is parallel to plane. */
if ( num == 0.0 ) /* q is on plane. */
return 'p';
else
return '0';
}
else
t = num / denom;
for( i = 0; i < DIM; i++ )
p->n[i] = q[i] + t * ( r[i] - q[i] );
if ( (0.0 < t) && (t < 1.0) )
return '1';
else if ( num == 0.0 ) /* t == 0 */
return 'q';
else if ( num == denom ) /* t == 1 */
return 'r';
else return '0';
}
static
int AreaSign( VEC3I a, VEC3I b, VEC3I c )
{
double area2;
area2 = ( b[0] - a[0] ) * (double)( c[1] - a[1] ) -
( c[0] - a[0] ) * (double)( b[1] - a[1] );
/* The area should be an integer. */
if ( area2 > 0.5 ) return 1;
else if ( area2 < -0.5 ) return -1;
else return 0;
}
static
char InTri2D( VEC3I Tp[3], VEC3I pp )
{
int area0, area1, area2;
/* compute three AreaSign() values for pp w.r.t. each Edge of the face in 2D */
area0 = AreaSign( pp, Tp[0], Tp[1] );
area1 = AreaSign( pp, Tp[1], Tp[2] );
area2 = AreaSign( pp, Tp[2], Tp[0] );
if ( (( area0 == 0 ) && ( area1 > 0 ) && ( area2 > 0 )) ||
(( area1 == 0 ) && ( area0 > 0 ) && ( area2 > 0 )) ||
(( area2 == 0 ) && ( area0 > 0 ) && ( area1 > 0 )) )
return 'E';
if ( (( area0 == 0 ) && ( area1 < 0 ) && ( area2 < 0 )) ||
(( area1 == 0 ) && ( area0 < 0 ) && ( area2 < 0 )) ||
(( area2 == 0 ) && ( area0 < 0 ) && ( area1 < 0 )))
return 'E';
if ( (( area0 > 0 ) && ( area1 > 0 ) && ( area2 > 0 )) ||
(( area0 < 0 ) && ( area1 < 0 ) && ( area2 < 0 )))
return 'F';
if ( ( area0 == 0 ) && ( area1 == 0 ) && ( area2 == 0 ) )
return '?'; /* Error in InTriD */
if ( (( area0 == 0 ) && ( area1 == 0 )) ||
(( area0 == 0 ) && ( area2 == 0 )) ||
(( area1 == 0 ) && ( area2 == 0 )) )
return 'V';
else
return '0';
}
/* Assumption: p lies in the plane containing T.
Returns a char:
'V': the query point p coincides with a Vertex of triangle T.
'E': the query point p is in the relative interior of an Edge of triangle T.
'F': the query point p is in the relative interior of a Face of triangle T.
'0': the query point p does not intersect (misses) triangle T.
*/
static
char InTri3D(LPHULL hull, VEC3I T, int m, VEC3I p )
{
int i; /* Index for X,Y,Z */
int j; /* Index for X,Y */
int k; /* Index for triangle Vertex */
VEC3I pp; /* projected p */
VEC3I Tp[3]; /* projected T: three new vertices */
/* Project out coordinate m in both p and the triangular face */
j = 0;
for ( i = 0; i < DIM; i++ ) {
if ( i != m ) { /* skip largest coordinate */
pp[j] = p[i];
for ( k = 0; k < 3; k++ )
Tp[k][j] = hull->Vertices[T[k]][i];
j++;
}
}
return( InTri2D( Tp, pp ) );
}
static
int VolumeSign2( VEC3I a, VEC3I b, VEC3I c, VEC3I d )
{
double vol;
double ax, ay, az, bx, by, bz, cx, cy, cz, dx, dy, dz;
double bxdx, bydy, bzdz, cxdx, cydy, czdz;
ax = a[X];
ay = a[Y];
az = a[Z];
bx = b[X];
by = b[Y];
bz = b[Z];
cx = c[X];
cy = c[Y];
cz = c[Z];
dx = d[X];
dy = d[Y];
dz = d[Z];
bxdx=bx-dx;
bydy=by-dy;
bzdz=bz-dz;
cxdx=cx-dx;
cydy=cy-dy;
czdz=cz-dz;
vol = (az-dz) * (bxdx*cydy - bydy*cxdx)
+ (ay-dy) * (bzdz*cxdx - bxdx*czdz)
+ (ax-dx) * (bydy*czdz - bzdz*cydy);
/* The volume should be an integer. */
if ( vol > 0.5 ) return 1;
else if ( vol < -0.5 ) return -1;
else return 0;
}
/*---------------------------------------------------------------------
The signed volumes of three tetrahedra are computed, determined
by the segment qr, and each Edge of the triangle.
Returns a char:
'v': the open segment includes a Vertex of T.
'e': the open segment includes a point in the relative interior of an Edge
of T.
'f': the open segment includes a point in the relative interior of a face
of T.
'0': the open segment does not intersect triangle T.
---------------------------------------------------------------------*/
static
char SegTriCross(LPHULL hull, VEC3I T, VEC3I q, VEC3I r )
{
int vol0, vol1, vol2;
vol0 = VolumeSign2( q, hull->Vertices[ T[0] ], hull->Vertices[ T[1] ], r );
vol1 = VolumeSign2( q, hull->Vertices[ T[1] ], hull->Vertices[ T[2] ], r );
vol2 = VolumeSign2( q, hull->Vertices[ T[2] ], hull->Vertices[ T[0] ], r );
/* Same sign: segment intersects interior of triangle. */
if ( ( ( vol0 > 0 ) && ( vol1 > 0 ) && ( vol2 > 0 ) ) ||
( ( vol0 < 0 ) && ( vol1 < 0 ) && ( vol2 < 0 ) ) )
return 'f';
/* Opposite sign: no intersection between segment and triangle */
if ( ( ( vol0 > 0 ) || ( vol1 > 0 ) || ( vol2 > 0 ) ) &&
( ( vol0 < 0 ) || ( vol1 < 0 ) || ( vol2 < 0 ) ) )
return '0';
else if ( ( vol0 == 0 ) && ( vol1 == 0 ) && ( vol2 == 0 ) )
return '?'; /* Error 1 in SegTriCross */
/* Two zeros: segment intersects Vertex. */
else if ( ( ( vol0 == 0 ) && ( vol1 == 0 ) ) ||
( ( vol0 == 0 ) && ( vol2 == 0 ) ) ||
( ( vol1 == 0 ) && ( vol2 == 0 ) ) )
return 'v';
/* One zero: segment intersects Edge. */
else if ( ( vol0 == 0 ) || ( vol1 == 0 ) || ( vol2 == 0 ) )
return 'e';
else
return '?'; /* Error 2 in SegTriCross */
}
static
char SegTriInt(LPHULL hull, VEC3I T, VEC3I q, VEC3I r, LPVEC3 p )
{
int code;
int m = -1;
code = SegPlaneInt(hull, T, q, r, p, &m );
if ( code == '0') return '0';
else if ( code == 'q') return InTri3D(hull, T, m, q );
else if ( code == 'r') return InTri3D(hull, T, m, r );
else if ( code == 'p') return 'p';
else if ( code == '1' ) return SegTriCross(hull, T, q, r );
else
return code; /* Error */
}
/*
This function returns a char:
'i': the query point a is strictly interior to polyhedron P.
'o': the query point a is strictly exterior to( or outside of) polyhedron P.
*/
char InPolyhedron(LPHULL hull, VEC3I q)
{
int F = hull->nfaces;
VEC3I Ray; /* Ray endpoint. */
VEC3 p; /* Intersection point; not used. */
int f, k = 0, crossings = 0;
char code = '?';
/* If query point is outside bounding box, finished. */
if ( !InBox( q, hull->bmin, hull->bmax ) )
return 'o';
LOOP:
while( k++ < F ) {
crossings = 0;
RandomRay(Ray, hull->radius );
AddVec( q, Ray );
for ( f = 0; f < F; f++ ) { /* Begin check each face */
if ( BoxTest(hull, f, q, Ray ) == '0' ) {
code = '0';
}
else code = SegTriInt(hull, hull->Faces[f], q, Ray, &p );
/* If ray is degenerate, then goto outer while to generate another. */
if ( code == 'p' || code == 'v' || code == 'e' ) {
goto LOOP;
}
/* If ray hits face at interior point, increment crossings. */
else if ( code == 'f' ) {
crossings++;
}
/* If query endpoint q sits on a V/E/F, return inside. */
else if ( code == 'V' || code == 'E' || code == 'F' )
return code; /* 'i'; MM2 */
/* If ray misses triangle, do nothing. */
else if ( code == '0' )
;
else
return '?'; /* Error */
}
/* No degeneracies encountered: ray is generic, so finished. */
break;
} /* End while loop */
/* q strictly interior to polyhedron iff an odd number of crossings. */
if( ( crossings % 2 ) == 1 )
return 'i';
else return 'o';
}
/*/ ---------------------------------------------------------------------------------- */
static
void StoreResults(LPHULL hull)
{
int i, w;
LPVERTEX v;
LPFACE f;
int V = 0, F = 0;
int j, k;
/* Vertices */
v = hull ->vertices;
V = 0;
do {
v -> vnum = V;
hull ->Vertices[V][X] = v -> v[X];
hull ->Vertices[V][Y] = v -> v[Y];
hull ->Vertices[V][Z] = v -> v[Z];
v = v->Next;
V++;
} while ( v != hull ->vertices );
hull ->nvertex = V;
/* Faces */
f = hull ->faces;
F = 0;
do {
hull ->Faces[F][0] = f->Vertex[0]->vnum;
hull ->Faces[F][1] = f->Vertex[1]->vnum;
hull ->Faces[F][2] = f->Vertex[2]->vnum;
for ( j=0; j < 3; j++ ) {
hull ->Box[F][0][j] = hull ->Vertices[ hull ->Faces[F][0] ][j];
hull ->Box[F][1][j] = hull ->Vertices[ hull ->Faces[F][0] ][j];
}
/* Check k=1,2 vertices of face. */
for ( k=1; k < 3; k++ )
for ( j=0; j < 3; j++ ) {
w = hull ->Vertices[ hull ->Faces[F][k] ][j];
if ( w < hull ->Box[F][0][j] ) hull ->Box[F][0][j] = w;
if ( w > hull ->Box[F][1][j] ) hull ->Box[F][1][j] = w;
}
f = f->Next; F++;
} while ( f != hull ->faces );
hull ->nfaces = F;
/* Initialize the bounding box */
for ( i = 0; i < DIM; i++ )
hull ->bmin[i] = hull ->bmax[i] = hull ->Vertices[0][i];
hull ->radius = ComputeBox(hull, V, hull ->bmin, hull ->bmax );
}
LCMSHANDLE cmsxHullInit(void)
{
LPHULL hull = (LPHULL) malloc(sizeof(HULL));
ZeroMemory(hull, sizeof(HULL));
hull->vnumCounter = 0;
hull->vertices = NULL;
hull->edges = NULL;
hull->faces = NULL;
hull->nfaces = 0;
hull->nvertex = 0;
return (LCMSHANDLE) (LPSTR) hull;
}
void cmsxHullDone(LCMSHANDLE hHull)
{
LPHULL hull = (LPHULL) (LPSTR) hHull;
if (hull)
free((LPVOID) hull);
}
BOOL cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z)
{
LPVERTEX v;
LPHULL hull = (LPHULL) (LPSTR) hHull;
v = MakeNullVertex(hull);
v->v[X] = x;
v->v[Y] = y;
v->v[Z] = z;
v->vnum = hull->vnumCounter++;
return true;
}
BOOL cmsxHullComputeHull(LCMSHANDLE hHull)
{
LPHULL hull = (LPHULL) (LPSTR) hHull;
if (!DoubleTriangle(hull)) return false;
ConstructHull(hull);
StoreResults(hull);
return true;
}
char cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z)
{
VEC3I q;
LPHULL hull = (LPHULL) (LPSTR) hHull;
q[X] = x; q[Y] = y; q[Z] = z;
return InPolyhedron(hull, q ) ;
}
BOOL cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname)
{
FILE* fp;
int i;
LPHULL hull = (LPHULL) (LPSTR) hHull;
fp = fopen (fname, "wt");
if (fp == NULL)
return false;
fprintf (fp, "#VRML V2.0 utf8\n");
/* set the viewing orientation and distance */
fprintf (fp, "DEF CamTest Group {\n");
fprintf (fp, "\tchildren [\n");
fprintf (fp, "\t\tDEF Cameras Group {\n");
fprintf (fp, "\t\t\tchildren [\n");
fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n");
fprintf (fp, "\t\t\t\t\tposition 0 0 340\n");
fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n");
fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n");
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t]\n");
fprintf (fp, "\t\t},\n");
fprintf (fp, "\t]\n");
fprintf (fp, "}\n");
/* Output the background stuff */
fprintf (fp, "Background {\n");
fprintf (fp, "\tskyColor [\n");
fprintf (fp, "\t\t.5 .5 .5\n");
fprintf (fp, "\t]\n");
fprintf (fp, "}\n");
/* Output the shape stuff */
fprintf (fp, "Transform {\n");
fprintf (fp, "\tscale 8 8 8\n");
fprintf (fp, "\tchildren [\n");
/* Draw the axes as a shape: */
fprintf (fp, "\t\tShape {\n");
fprintf (fp, "\t\t\tappearance Appearance {\n");
fprintf (fp, "\t\t\t\tmaterial Material {\n");
fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n");
fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n");
fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
fprintf (fp, "\t\t\t\t\tpoint [\n");
fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n");
fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0);
fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0);
fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0);
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t\tcoordIndex [\n");
fprintf (fp, "\t\t\t\t\t0, 1, -1\n");
fprintf (fp, "\t\t\t\t\t0, 2, -1\n");
fprintf (fp, "\t\t\t\t\t0, 3, -1]\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t}\n");
/* Draw the triangles as a shape: */
fprintf (fp, "\t\tShape {\n");
fprintf (fp, "\t\t\tappearance Appearance {\n");
fprintf (fp, "\t\t\t\tmaterial Material {\n");
fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
fprintf (fp, "\t\t\t\t\temissiveColor 0 0 0\n");
fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t\tgeometry IndexedFaceSet {\n");
fprintf (fp, "\t\t\t\tsolid false\n");
/* fill in the points here */
fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
fprintf (fp, "\t\t\t\t\tpoint [\n");
for (i = 0; i < hull->nvertex; ++i)
{
fprintf (fp, "\t\t\t\t\t%g %g %g%c\n",
(double) hull->Vertices[i][X], (double) hull->Vertices[i][Y], (double) hull->Vertices[i][Z],
i == hull->nvertex-1? ']': ',');
}
fprintf (fp, "\t\t\t\t}\n");
/* fill in the Vertex indices (followed by -1) */
fprintf (fp, "\t\t\t\tcoordIndex [\n");
for (i = 0; i < hull->nfaces; ++i)
{
fprintf (fp, "\t\t\t\t\t%d, %d, %d, -1\n",
hull->Faces[i][0], hull->Faces[i][1], hull->Faces[i][2]);
}
fprintf (fp, "]\n");
/* fill in the face colors */
fprintf (fp, "\t\t\t\tcolor Color {\n");
fprintf (fp, "\t\t\t\t\tcolor [\n");
for (i = 0; i < hull->nfaces; ++i)
{
int vx, vy, vz;
double r, g, b;
vx = hull->Faces[i][0]; vy = hull->Faces[i][1]; vz = hull->Faces[i][2];
r = (double) (hull->Vertices[vx][X] + hull->Vertices[vy][X] + hull->Vertices[vz][X]) / (3* 255);
g = (double) (hull->Vertices[vx][Y] + hull->Vertices[vy][Y] + hull->Vertices[vz][Y]) / (3* 255);
b = (double) (hull->Vertices[vx][Z] + hull->Vertices[vy][Z] + hull->Vertices[vz][Z]) / (3* 255);
fprintf (fp, "\t\t\t\t\t%g %g %g%c\n", r, g, b,
i == hull->nfaces-1? ']': ',');
}
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t\tcolorPerVertex false\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t}\n");
fprintf (fp, "\t]\n");
fprintf (fp, "}\n");
fclose (fp);
return true;
}