⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 partition.c

📁 图形处理算法合集3:包括图形处理、帧缓存技术、渲染、矩阵运算、建模方法
💻 C
字号:
/* partition.c: module to partition 3D convex face with an arbitrary plane. * Copyright (c) Norman Chin */ #include <stdio.h>		/* NULL */#include <assert.h>		/* assert() */#include <malloc.h>		/* malloc() */#include <math.h>		/* fabs() */#include "GraphicsGems.h"	#include "partition.h"/* local functions */static VERTEX *findNextIntersection(/* VERTEX *vstart, double aa,				       double bb, double cc, double dd,				       double *ixx, double *iyy, double *izz,				       SIGN *sign */);static SIGN anyEdgeIntersectWithPlane(/* double x1, double y1, double z1,					 double x2, double y2, double z2,					 double aa, double bb, double cc,					 double dd, 					 double *ixx, double *iyy, double *izz				       */);static FACE *createOtherFace(/* FACE *face, VERTEX *v1, double ixx1, 				double iyy1, double izz1, VERTEX *v2,				double ixx2, double iyy2, double izz2 */);static SIGN whichSideIsFaceWRTplane(/* FACE *face, double aa, double bb,				       double cc, double dd */);static VERTEX *allocVertex(/* double xx, double yy, double zz */);static FACE *allocFace(/* FACE *face, VERTEX *vhead */);/* Partitions a 3D convex polygon (face) with an arbitrary plane into its  * negative and positive fragments, if any, w.r.t. the partitioning plane. * Note that inputFace is unusable afterwards since its vertex list has been * parceled out to the other faces. It's set to null to avoid dangling * pointer problem.  */void partitionFaceWithPlane(aa,bb,cc,dd,inputFace,faceOn,faceNeg,facePos)double aa,bb,cc,dd;		/* partitioning plane's coefficients */FACE **inputFace;		/* face to be partitioned */FACE **faceOn;			/* returns face embedded in plane, if any  */FACE **faceNeg;			/* returns face on negative side, if any */FACE **facePos;			/* returns face on positive side, if any */{   VERTEX *v1, *v2;   double ixx1,iyy1,izz1, ixx2,iyy2,izz2;   SIGN signV1, signV2;   *faceOn= *faceNeg= *facePos= NULL_FACE;   /* find first intersection */   v1= findNextIntersection((*inputFace)->vhead,aa,bb,cc,dd, 			    &ixx1,&iyy1,&izz1,&signV1);   if (v1 != NULL_VERTEX) {      assert(signV1 != ZERO);      /* first one found, find the 2nd one, if any */      v2= findNextIntersection(v1->vnext,aa,bb,cc,dd, 			       &ixx2,&iyy2,&izz2,&signV2);      /* Due to numerical instabilities, both intersection points may       * have the same sign such as in the case when splitting very close       * to a vertex. This should not count as a split.       */      if (v2 != NULL_VERTEX && signV1 == signV2) v2= NULL_VERTEX;    }   else v2= NULL_VERTEX;	/* No first intersection found,                                 * therefore no second intersection.				 */                                   /* an intersection? */   if (v1 != NULL_VERTEX && v2 != NULL_VERTEX) { /* yes, intersection */      FACE *newOtherFace;      /* inputFace's vertex list will be modified */      newOtherFace= createOtherFace(*inputFace,v1,ixx1,iyy1,izz1,				               v2,ixx2,iyy2,izz2);      /* return split faces on appropriate lists */      if (signV1 == NEGATIVE) { 	 *faceNeg= *inputFace;          *facePos= newOtherFace;      }      else {	 assert(signV1 == POSITIVE);	 *faceNeg= newOtherFace;	 *facePos= *inputFace;      }				       }   else {			/* no intersection  */      SIGN side;      /* Face is embedded or wholly to one side of partitioning plane. */      side= whichSideIsFaceWRTplane(*inputFace,aa,bb,cc,dd);      if (side == NEGATIVE)	 *faceNeg= *inputFace;      else if (side == POSITIVE)	 *facePos= *inputFace;      else {	 assert(side == ZERO);	 *faceOn= *inputFace;      }   }   /* inputFace's vertex list has been parceled out to other lists so    * set this to null.    */   *inputFace= NULL_FACE;		   /* If a face is embedded in plane then there must not be any faces on    *    either side.    * Otherwise the face isn't embedded in plane so there must be at least     *    one face on either side or both.    */   assert( (*faceOn != NULL_FACE) ? 	    (*faceNeg == NULL_FACE &&	     *facePos == NULL_FACE) :	    (*faceNeg != NULL_FACE ||	     *facePos != NULL_FACE) );} /* partitionFaceWithPlane() */			    /* Finds next intersection on or after vstart.  *  * If an intersection is found,  *    a pointer to first vertex of the edge is returned,  *    the intersection point (ixx,iyy,izz) and its sign is updated.  * Otherwise a null pointer is returned. */static VERTEX *findNextIntersection(vstart,aa,bb,cc,dd,ixx,iyy,izz,sign)VERTEX *vstart;			/* where to start searching on vertex list */double aa,bb,cc,dd;		/* plane's coefficients */double *ixx,*iyy,*izz;		/* returned intersection point */SIGN *sign;		/* returned classification of intersection point */{   VERTEX *vtrav;   /* for all edges starting from vstart ... */   for (vtrav= vstart; vtrav->vnext != NULL_VERTEX; vtrav= vtrav->vnext) {      if ((*sign= anyEdgeIntersectWithPlane(vtrav->xx,vtrav->yy,vtrav->zz,					    vtrav->vnext->xx,vtrav->vnext->yy,					    vtrav->vnext->zz,aa,bb,cc,dd,					    ixx,iyy,izz))) {	 return(vtrav);      }   }   return(NULL_VERTEX);} /* findNextIntersection() *//* Memory allocated for split face's vertices and pointers tediously updated. */static FACE *createOtherFace(face,v1,ixx1,iyy1,izz1,v2,ixx2,iyy2,izz2)FACE *face;			/* face to be split */VERTEX *v1;	/* 1st vertex of edge of where 1st intersection was found */double ixx1,iyy1,izz1;		/* 1st intersection  */VERTEX *v2;     /* 1st vertex of edge of where 2nd intersection was found */double ixx2,iyy2,izz2;		/* 2nd intersection */{   VERTEX *i1p1, *i2p1;		/* new vertices for original face  */   VERTEX *i1p2, *i2p2;		/* new vertices for new face */   VERTEX *p2end;		/* new vertex for end of new face */   VERTEX *vtemp;		/* place holders */   register VERTEX *beforeV2;	/* place holders */   FACE *newFace;		/* newly allocated face */   /* new intersection vertices */   i1p1= allocVertex(ixx1,iyy1,izz1);    i2p1= allocVertex(ixx2,iyy2,izz2);   i1p2= allocVertex(ixx1,iyy1,izz1);   i2p2= allocVertex(ixx2,iyy2,izz2);    /* duplicate 1st vertex of 2nd list to close it up */   p2end= allocVertex(v2->xx,v2->yy,v2->zz);   vtemp= v1->vnext;   /* merge both intersection vertices i1p1 & i2p1 into 1st list */   if (ISVERTEX_EQ(i2p1,v2->vnext)) { /* intersection vertex coincident? */      FREEVERTEX(i2p1);		/* yes, so free it */      i1p1->vnext= v2->vnext;   }   else {      i2p1->vnext= v2->vnext;	/* attach intersection list onto 1st list */      i1p1->vnext= i2p1;	/* attach both intersection vertices */   }   v1->vnext= i1p1; /* attach front of 1st list to intersection vertices */   /* merge intersection vertices i1p2, i2p2 & p2end into second list */   i2p2->vnext= i1p2;		/* attach both intersection vertices */   v2->vnext= i2p2;		/* attach 2nd list to interection vertices */   if (vtemp == v2) {      i1p2->vnext= p2end;	/* close up 2nd list */   }   else {      if (ISVERTEX_EQ(i1p2,vtemp)) { /* intersection vertex coincident? */	 FREEVERTEX(i1p2);	/* yes, so free it */	 i2p2->vnext= vtemp;	/* attach intersection vertex to 2nd list */      }      else {	 i1p2->vnext= vtemp;	/* attach intersection list to 2nd list */      }      /* find previous vertex to v2 */      for (beforeV2= vtemp; beforeV2->vnext != v2; beforeV2= beforeV2->vnext)	 ;			/* lone semi-colon */      beforeV2->vnext= p2end;	/* and attach it to complete the 2nd list */   }   /* copy original face info but with new vertex list */   newFace= allocFace(face,v2);   return(newFace);} /* createOtherFace() *//* Determines which side a face is with respect to a plane with coefficient * (aa,bb,cc,dd). * * However, due to numerical problems, when a face is very close to the plane, * some vertices may be misclassified.  * There are several solutions, two of which are mentioned here: *    1- classify the one vertex furthest away from the plane, (note that *       one need not compute the actual distance) and use that side. *    2- count how many vertices lie on either side and pick the side *       with the maximum. (this is the one implemented). */static SIGN whichSideIsFaceWRTplane(face,aa,bb,cc,dd)FACE *face;double aa,bb,cc,dd;{   register VERTEX *vtrav;   double value;   boolean isNeg, isPos;   isNeg= isPos= FALSE;      for (vtrav= face->vhead; vtrav->vnext != NULL_VERTEX; vtrav= vtrav->vnext){      value= (aa*vtrav->xx) + (bb*vtrav->yy) + (cc*vtrav->zz) + dd;      if (value < -TOLER) 	 isNeg= TRUE;      else if (value > TOLER)	 isPos= TRUE;      else assert(ISDOUBLE_EQ(value,0.0));   } /* for vtrav */    /* in the very rare case that some vertices slipped thru to other side of    * plane due to round-off errors, execute the above again but count the     * vertices on each side instead and pick the maximum.    */   if (isNeg && isPos) {	/* yes so handle this numerical problem */      int countNeg, countPos;            /* count how many vertices are on either side */      countNeg= countPos= 0;      for (vtrav= face->vhead; vtrav->vnext != NULL_VERTEX; 	   vtrav= vtrav->vnext) {	 value= (aa*vtrav->xx) + (bb*vtrav->yy) + (cc*vtrav->zz) + dd;	 if (value < -TOLER)	    countNeg++;	 else if (value > TOLER)	    countPos++;	 else assert(ISDOUBLE_EQ(value,0.0));      } /* for */      /* return the side corresponding to the maximum */      if (countNeg > countPos) return(NEGATIVE);      else if (countPos > countNeg) return(POSITIVE);      else return(ZERO);   }   else {			/* this is the usual case */      if (isNeg) return(NEGATIVE);      else if (isPos) return(POSITIVE);      else return(ZERO);   }} /* whichSideIsFaceWRTplane() *//* Determines if an edge bounded by (x1,y1,z1)->(x2,y2,z2) intersects * the plane with the coefficients (aa,bb,cc,dd). *  * If there's an intersection,  *    the sign of (x1,y1,z1), NEGATIVE or POSITIVE, w.r.t. the plane is *    returned with the intersection (ixx,iyy,izz) updated. * Otherwise ZERO is returned.     */static SIGN anyEdgeIntersectWithPlane(x1,y1,z1,x2,y2,z2,aa,bb,cc,dd,					ixx,iyy,izz)double x1,y1,z1, x2,y2,z2;	/* both vertices of edge */double aa,bb,cc,dd;		/* partitioning plane's coefficients */double *ixx,*iyy,*izz;		/* returned intersection point, if any */{   double temp1, temp2;   int sign1, sign2;		/* must be int since gonna do a bitwise ^ */   /* get signs */   temp1= (aa*x1) + (bb*y1) + (cc*z1) + dd;   if (temp1 < -TOLER)      sign1= -1;   else if (temp1 > TOLER)      sign1= 1;   else {      /* edges beginning with a 0 sign are not considered valid intersections       * case 1 & 6 & 7, see text       */      assert(ISDOUBLE_EQ(temp1,0.0));      return(ZERO);   }   temp2= (aa*x2) + (bb*y2) + (cc*z2) + dd;   if (temp2 < -TOLER)      sign2= -1;   else if (temp2 > TOLER)      sign2= 1;   else {			/* case 8 & 9, see text  */      assert(ISDOUBLE_EQ(temp2,0.0));      *ixx= x2;      *iyy= y2;      *izz= z2;      return( (sign1 == -1) ? NEGATIVE : POSITIVE);   }   /* signs different?     * recall: -1^1 == 1^-1 ==> 1    case 4 & 5, see text    *         -1^-1 == 1^1 ==> 0    case 2 & 3, see text    */   if (sign1 ^ sign2) {      double dx,dy,dz;      double denom, tt;      /* compute intersection point */      dx= x2-x1;      dy= y2-y1;      dz= z2-z1;      denom= (aa*dx) + (bb*dy) + (cc*dz);      tt= - ((aa*x1) + (bb*y1) + (cc*z1) + dd) / denom;      *ixx= x1 + (tt * dx);      *iyy= y1 + (tt * dy);      *izz= z1 + (tt * dz);      assert(sign1 == 1 || sign1 == -1);      return( (sign1 == -1) ? NEGATIVE : POSITIVE );   }   else return(ZERO);} /* anyEdgeIntersectWithPlane() *//* allocates vertex with coordinates (xx,yy,zz) */static VERTEX *allocVertex(xx,yy,zz)double xx,yy,zz;{   VERTEX *newVertex;   newVertex= NEWTYPE(VERTEX); assert(newVertex != NULL_VERTEX);   newVertex->xx= xx; newVertex->yy= yy; newVertex->zz= zz;    newVertex->vnext= NULL_VERTEX;       return(newVertex);} /* allocVertex() *//* allocates face with list of vertices, vhead */static FACE *allocFace(face,vhead)FACE *face;VERTEX *vhead;{   FACE *newFace;   newFace= NEWTYPE(FACE); assert(newFace != NULL_FACE);    *newFace= *face;   newFace->vhead= vhead;   return(newFace);} /* allocFace() */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -