ftp.nice.ch/pub/next/graphics/3d/geomview.1.4.1.s.tar.gz#/Geomview/src/lib/geometry/point3/polyint.c

This is polyint.c in view mode; [Download] [Up]

#include <math.h>
#include "point3.h"
#include "transform.h"
#include "ooglutil.h"
#include "polyint.h"

static char msg[] = "polyint.c";

#define FUDGE 1.e-6

#define PT3SUB_XY(a, b, dst) { (dst).x = (a).x - (b).x; \
			       (dst).y = (a).y - (b).y; \
			       (dst).z = 0.0; }
#define PT3LENGTH_XY(a) sqrt((a).x*(a).x + (a).y*(a).y)
#define PT3DOT_XY(a, b) ((a).x*(b).x + (a).y*(b).y)

/*
 * Internal routines.  These should not need to be called directly.
 */
static int PolyInt_InBBox(int n_verts, Point3 *verts, float tol);
static float PolyInt_Angle(Point3 *pt1, Point3 *pt2);
static int PolyInt_Origin(int n_verts, Point3 *verts, Point3 *origin);

int PolyZInt(int n_verts, Point3 *verts, float tol,
	     vvec *ip, vvec *vertices, vvec *edges, vvec *ep)
{
  int i, j, flag;
  int n_oldhits, n_hits = 0;

  Point3 v1, v2, pt, bma;
  float d, t, len, f;

  float angsum = 0.0;

  /* Do test for trivial rejection */
  if (!PolyInt_InBBox(n_verts, verts, tol)) return 0; 

  n_hits = n_oldhits = VVCOUNT(*ip);

  /* Go through the edges of the polygon looking for edge and vertex hits. */
  for (i = 0; i < n_verts; i++) {
    j = (i + 1) % n_verts;

    Pt3From(&v1, (float)verts[i].x, (float)verts[i].y, (float)0.0);
    Pt3From(&v2, (float)verts[j].x, (float)verts[j].y, (float)0.0);

    Pt3Sub(&verts[j], &verts[i], &bma);
    len = PT3LENGTH_XY(bma);
    t = - PT3DOT_XY(verts[i], bma) / len;      
    
    f = t / len;
    Pt3From(&pt, (float)(verts[i].x + f * bma.x), 
	    (float)(verts[i].y + f * bma.y), (float)(verts[i].z + f * bma.z));
    d = PT3LENGTH_XY(pt);

    if (d < tol && t > -tol) {
      if (t < tol) {
	*VVINDEX(*vertices, int, n_hits) = i;
	*VVINDEX(*edges, int, n_hits) = -1;
	*VVINDEX(*ip, Point3, n_hits) = verts[i];
	n_hits++;
      } else if (t < len - tol) {
	*VVINDEX(*ip, Point3, n_hits) = pt;
	*VVINDEX(*vertices, int, n_hits) = -1;
	*VVINDEX(*edges, int, n_hits) = i;
	*VVINDEX(*ep, Point3, n_hits) = pt;
	n_hits++;
      }
    }

    angsum += PolyInt_Angle(&v1, &v2);

  }

  /* Look for the face hits */
  if (!(n_hits - n_oldhits) && n_verts > 2 && fabs(angsum / M_2_PI) > 0.5
	&& PolyInt_Origin(n_verts, verts, &pt)) {
    *VVINDEX(*ip, Point3, n_hits) = pt;
    *VVINDEX(*vertices, int, n_hits) = -1;
    *VVINDEX(*edges, int, n_hits) = -1;
    n_hits++;
  }

  VVCOUNT(*ip) = n_hits;
  VVCOUNT(*vertices) = n_hits;
  VVCOUNT(*edges) = n_hits;

  return(n_hits - n_oldhits);

}


int PolyNearPosZInt(int n_verts, Point3 *verts, float tol,
		    Point3 *ip, int *vertex, int *edge, Point3 *ep) {
  int i, closest;
  vvec v_ip, v_vertices, v_edges, v_ep;
  float zmax;

  VVINIT(v_ip, Point3, 4);
  VVINIT(v_vertices, int, 4);
  VVINIT(v_edges, int, 4);
  VVINIT(v_ep, Point3, 4);

  closest = -1;

  if (!PolyZInt(n_verts, verts, tol, &v_ip, &v_vertices, &v_edges, &v_ep))
	return 0;

  for (i = 0; i < VVCOUNT(v_ip); i++) 
    if (VVINDEX(v_ip, Point3, i)->z > -1.0 && 
	(closest == -1 || VVINDEX(v_ip, Point3, i)->z > zmax)) {
      closest = i;
      zmax = VVINDEX(v_ip, Point3, i)->z;
    }

  if (closest >= 0) {
    Pt3Copy(VVINDEX(v_ip, Point3, closest), ip);
    *vertex = *VVINDEX(v_vertices, int, closest);
    *edge = *VVINDEX(v_edges, int, closest);
    Pt3Copy(VVINDEX(v_ep, Point3, closest), ep);
  }

  vvfree(&v_ip);
  vvfree(&v_vertices);
  vvfree(&v_edges);
  vvfree(&v_ep);

  return ((closest < 0) ? 0 : 1);
}
    

int PolyLineInt(Point3 *pt1, Point3 *pt2, int n_verts, Point3 *verts, 
		float tol, vvec *ip, vvec *vertices, vvec *edges, vvec *ep) 
{
  int i;
  int n_hits, old_n_hits;
  Point3 *vertcopy;
  Transform T, Tinv;

  PolyInt_Align(pt1, pt2, T);
  TmInvert(T, Tinv);

  vertcopy = OOGLNewNE(Point3, n_verts, msg);
  for (i = 0; i < n_verts; i++) Pt3Transform(T, &verts[i], &vertcopy[i]);

  old_n_hits = VVCOUNT(*ip);
  n_hits = PolyZInt(n_verts, vertcopy, tol, ip, vertices, edges, ep);
  for (i = 0; i < n_hits; i++) {
    Pt3Transform(Tinv, VVINDEX(*ip, Point3, old_n_hits + i), 
		 VVINDEX(*ip, Point3, old_n_hits + i));
    Pt3Transform(Tinv, VVINDEX(*ep, Point3, old_n_hits + i),
		 VVINDEX(*ep, Point3, old_n_hits + i));
  }

  OOGLFree(vertcopy);

  return n_hits;

}


int PolyRayInt(Point3 *pt1, Point3 *pt2, int n_verts, Point3 *verts, 
	       float tol, vvec *ip, vvec *vertices, vvec *edges, vvec *ep)
{
  int i, j;
  int n_hits, old_n_hits;
  Point3 *vertcopy;
  Transform T, Tinv;

  PolyInt_Align(pt1, pt2, T);
  TmInvert(T, Tinv);

  vertcopy = OOGLNewNE(Point3, n_verts, msg);
  for (i = 0; i < n_verts; i++) Pt3Transform(T, &verts[i], &vertcopy[i]);

  old_n_hits = VVCOUNT(*ip);
  n_hits = PolyZInt(n_verts, vertcopy, tol, ip, vertices, edges, ep);
  for (i = j = 0; i < n_hits; i++)
    if (VVINDEX(*ip, Point3, old_n_hits + i)->z <= 0) {
      Pt3Transform(Tinv, VVINDEX(*ip, Point3, old_n_hits + i),
		   VVINDEX(*ip, Point3, old_n_hits + j));
      Pt3Transform(Tinv, VVINDEX(*ep, Point3, old_n_hits + i),
		   VVINDEX(*ep, Point3, old_n_hits + j));
      j++;
    }
 
  OOGLFree(vertcopy);

  return j;

}


int PolySegmentInt(Point3 *pt1, Point3 *pt2, int n_verts, Point3 *verts,
		   float tol, vvec *ip, vvec *vertices, vvec *edges, vvec *ep)
{
  int i, j;
  int n_hits, old_n_hits;
  Point3 *vertcopy;
  Transform T, Tinv;

  PolyInt_Align(pt1, pt2, T);
  TmInvert(T, Tinv);

  vertcopy = OOGLNewNE(Point3, n_verts, msg);
  for (i = 0; i < n_verts; i++) Pt3Transform(T, &verts[i], &vertcopy[i]);

  old_n_hits = VVCOUNT(*ip);
  n_hits = PolyZInt(n_verts, vertcopy, tol, ip, vertices, edges, ep);
  for (i = j = 0; i < n_hits; i++) 
    if ((VVINDEX(*ip, Point3, old_n_hits + i)->z <= 0) &&
	(VVINDEX(*ip, Point3, old_n_hits + i)->z >= -1.0)) {
      Pt3Transform(Tinv, VVINDEX(*ip, Point3, old_n_hits + i),
		   VVINDEX(*ip, Point3, old_n_hits + j));
      Pt3Transform(Tinv, VVINDEX(*ep, Point3, old_n_hits + i),
		   VVINDEX(*ep, Point3, old_n_hits + j));
      j++;
    }

  OOGLFree(vertcopy);

  VVCOUNT(*ip) = old_n_hits + j;
  vvtrim(ip);

  return j;

}


void PolyInt_Align(Point3 *pt1, Point3 *pt2, Transform T) {
  Point3 newpt2;
  Transform Ttmp;

  if (!bcmp(pt1, pt2, sizeof(Point3))) {
    OOGLError(1, "PolyInt_Align called with identical points.");
    TmIdentity(T);
    return;
  }

  TmTranslate(T, -pt1->x, -pt1->y, -pt1->z);
  Pt3Transform(T, pt2, &newpt2);

  TmRotateY(Ttmp, -atan2(newpt2.x, -newpt2.z));
  TmConcat(T, Ttmp, T);
  Pt3Transform(T, pt2, &newpt2);

  TmRotateX(Ttmp, -atan2(newpt2.y, -newpt2.z));
  TmConcat(T, Ttmp, T);
  Pt3Transform(T, pt2, &newpt2);

  if (newpt2.z == 0.0) {
    OOGLError(1, "Second point too close to first point in PolyInt_Align");
    TmIdentity(T);
    return;
  }
  TmScale(Ttmp, -1.0 / newpt2.z, -1.0 / newpt2.z, -1.0 / newpt2.z);
  TmConcat(T, Ttmp, T);

}

/*
 * PolyInt_InBBox
 * Returns non-zero if the origin is inside the polygon described by
 * verts or within tol of begin so; returns 0 otherwise.
 */
static int PolyInt_InBBox(int n_verts, Point3 *verts, float tol) {
  int i;
  int posx = 0, negx = 0, posy = 0, negy = 0;

  for (i = 0; i < n_verts; i++, verts++) {
    if (verts->x < tol) negx |= 1;
    if (verts->x > -tol) posx |= 1;
    if (verts->y < tol) negy |= 1;
    if (verts->y > -tol) posy |= 1;
  }

  return (posx & negx & posy & negy);

}


static float PolyInt_Angle(Point3 *pt1, Point3 *pt2) {
  float ans;

  if (Pt3Distance(pt1, pt2) < FUDGE) return 0;
  ans = PT3DOT_XY(*pt1, *pt2) / 
    sqrt((double)(PT3DOT_XY(*pt1, *pt1) * PT3DOT_XY(*pt2, *pt2)));
  ans = acos(ans);
  return(ans * ((pt1->x * pt2->y - pt1->y * pt2->x) >= 0 ? 1.0 : -1.0));

}


static int PolyInt_Origin(int n_verts, Point3 *verts, Point3 *origin) {
  int bi, ci;
  float pz, pw;

  /* Find the first vertex different from the 0th vertex */
  for (bi = 0; bi < n_verts && !bcmp(&verts[0], &verts[bi], sizeof(Point3));
       bi++);
  if (bi >= n_verts) {
    Pt3Copy(&verts[0], origin);
    return 0;
  }

  /* Find the first vertex not collinear with the other two vertices */
  for (ci = bi+1; ci < n_verts; ci++) {
    pz = (verts[0].x * (verts[bi].y - verts[ci].y) 
	  - verts[0].y * (verts[bi].x - verts[ci].x) 
	  + (verts[bi].x * verts[ci].y - verts[bi].y * verts[ci].x));
    if (fabs(pz) > FUDGE) break;
  }
  if (ci >= n_verts) {
    Pt3Copy(&verts[0], origin);
    return 0;
  }

  pw = (verts[0].x * (verts[bi].z*verts[ci].y - verts[bi].y*verts[ci].z)
	- verts[0].y * (verts[bi].z*verts[ci].x - verts[bi].x*verts[ci].z)
	+ verts[0].z * (verts[bi].y*verts[ci].x - verts[bi].x*verts[ci].y));

  origin->x = origin->y = 0.0;
  origin->z = -pw / pz;

  return 1;
}





These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.