This is sortContour.c in view mode; [Download] [Up]
/* Sort contours in the order of outer most to inner most. This is necessary for filling the inside of contours with color. See below. William Grosso 92-05-27 */ #include <stdio.h> #include <stdlib.h> #include "contour.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* Function prototypes private to this module */ int contoursub1(float xp, float yp, ContourPath *currentj, int npts); int contoursub2(double x1, double y1, double x2, double y2, double xp, double yp); /* ===================================================================================== A brief explanation: Our goal for this subroutine is this: to determine which contours lie inside other contours (we assume no contours intersect). The algorithm is essentially an adaptation of the pl proof of the Jordan Curve Theorem: we draw lines out to infinity and check parity. E.g. Given closed curves C1 and C2 we cant to check whether C1 contains C2 (C2 is "inside of" C1). To do so, we choose a point [xp,yp] on C2, draw a line from [xp,yp] to infinity, and count the number of intersections this line has C1. If even, C2 is not inside C1. If odd, C2 is inside C1. If the above was incomprehensible, may I suggest "The Topology of Plane Sets" by Newman. If you're curious about how this works in higher dimensions, Moise's "Geometric Topology in Dimensions 2 & 3" is a fine book (as is Zeeman's "Seminar on Combinatorial topology"). functions: contoursub1 draws the line and checks whether the line intersects each of the line segments that make up C1 (by using contoursub2--which solves the linear equation). optimization notes: the code is fairly incomprehensible at points because I wanted to optimize. The matrix "matrixie" is superfluous to the algorithm, but helps cut down the number of "containment" checks (if C1 is contained in C2, then C2 cannot be contained in C1). A 1 in the i,j place indicates Ci is contained in Cj. the (i,nContours+1) place holds the total number of contours that contain Ci syntax notes: C1 above is the "i" contour below. C2 above is the "j" contour below. ===================================================================================== Usage Notes: The last argument must have the storage for pointers allocated as below before calling the function: SCntrPtr = (ContourPath **)(malloc((size_t)(sizeof(ContourPath *)*nContours))); cntrList: linked list of contours containing all the information about contours. nContours: number of contours in cntrList. SCntrPtr: array of pointers to contours in cntrList, sorted in outer to inner order. ===================================================================================== */ void sort_contourList(ContourPath *cntrList, float xxmin, float xxmax, float yymin, float yymax, int nContours, ContourPath **SCntrPtr) { int i, j, k, jj=0; float xp=0.0, yp= 0.0; int *matrixie; /* where we'll store the containment data (temporarily). matrixie (i,j) indicates whether contour i is inside countour j Why is this here at all ? Well, we first check to see if contour i is contained in contour j (for i<j). Then, we use the fact that two contours can not each contain the other to cut down on our checking time. */ ContourPath *currenti, *currentj; /* pointers to the current paths for the i and j loops */ /* We've declared our variables...Now we must initialize them. Only the matrixie must be initialized (the others are done in loops). */ matrixie = (int *)calloc((size_t)((nContours+1)*nContours), (size_t)sizeof(int) ); currenti=cntrList; for (i=0;i<nContours;i++) { *(matrixie+(i+1)*(nContours+1)-1)=0; /* how many guys contain contour i */ currentj=cntrList; /* pick the point on contour-i which is inside the domain as the point from which we draw the line out to infinity. Start with the x[3], y[3] in an attempt to get a point that is not on the edge. */ for(j=3; j<(currenti->num_pts+3); j++) { jj = j % currenti->num_pts; xp=currenti->x[jj]; yp=currenti->y[jj]; if(xp >= xxmin && xp <= xxmax && yp >= yymin && yp <= yymax) break; /* we got it */ } for (j=0;j<nContours;j++) { if ((j==i) || ((j<i) && (*(matrixie+j*(nContours+1)+i)==1))) { currentj=currentj->next; continue; } *(matrixie+i*(nContours+1)+j)=contoursub1(xp,yp,currentj,currentj->num_pts); *(matrixie +(i+1)*(nContours+1)-1)+=*(matrixie +i*(nContours+1)+j); currentj=currentj->next; } currenti=currenti->next; } /* If all went well, we now have a nContours by nContours matrix holding all the "containment" information. Now, we create a list telling the drawing routine which ones to draw first (array of pointers to cntrList: SCntrPtr). The methodology is this: We create a list that has the contours which aren't contained in any other contour at the head of the list. Then come the contours that are contained in only one other contour. Then come the contours that are contained in only two other contours... */ k=0; for (i=0;i<nContours;i++) { currentj=cntrList; for(j=0;j<nContours;j++) { if (*(matrixie+(j+1)*(nContours +1)-1) == i) { SCntrPtr[k]=currentj; k++; } currentj=currentj->next; } if (k==nContours) break; } free(matrixie); } /* See commments above: this checks to see whether the line from [xp,yp] hits contourj an odd or an even number of times. Return val = 0 (xp,yp) is outside ContourPath *currentj Return val = 1 (xp,yp) is inside ContourPath *currentj */ int contoursub1 (float xp , float yp, ContourPath *currentj , int npts) { int k, counter=0; for (k=1;k<=npts;k++) { counter += contoursub2( currentj->x[k],currentj->y[k], currentj->x[k-1],currentj->y[k-1],xp,yp); } counter += contoursub2( currentj->x[npts],currentj->y[npts], currentj->x[0],currentj->y[0],xp,yp); return(counter%2); } /* solves the linear equation for contoursub1 * return val = 1: lines intersect * return val = 0: don't intersect. * Tests whether a line from (xp,yp) intersects line (x1,y1) --- (x2,y2). */ int contoursub2 (double x1, double y1, double x2, double y2, double xp, double yp) { double dummy; if(y1 == y2) return (0); dummy=(yp-y1)/(y2-y1); if ((0.0<=dummy) && (dummy<1.0) && ((dummy*(x2-x1)+x1-xp) >= 0.0)) return(1); else return(0); } /* -------------------------------------------------------------------------------- Routine returns (2) if (xp,yp) is inside the polygon x[n_path], y[n_path], (0) if outside, and (1) if exactly on the path edge. Uses non-zero winding rule in Adobe PostScript Language reference manual, section 4.6 on Painting. Path should have been closed first, so that x[n_path-1] = x[0], and y[n_path-1] = y[0]. This is version 2, trying to kill a bug in above routine: If point is on X edge, fails to discover that it is on edge. We are imagining a ray extending "up" from the point (xp,yp); the ray has equation x = xp, y >= yp. Starting with crossing_count = 0, we add 1 each time the path crosses the ray in the +x direction, and subtract 1 each time the ray crosses in the -x direction. After traversing the entire polygon, if (crossing_count) then point is inside. We also watch for edge conditions. If two or more points on the path have x[i] == xp, then we have an ambiguous case, and we have to find the points in the path before and after this section, and check them. --------------------------------------------------------------------------------- */ int non_zero_winding(float xp, float yp, float *x, float *y, int n_path) { int i, j, k, jend, crossing_count, above; double y_sect; above = FALSE; crossing_count = 0; /* First make sure first point in path is not a special case: */ j = jend = n_path - 1; if (x[j] == xp) { /* Trouble already. We might get lucky: */ if (y[j] == yp) return(1); /* Go backward down the polygon until x[i] != xp: */ if (y[j] > yp) above = TRUE; i = j - 1; while (x[i] == xp && i > 0) { if (y[i] == yp) return (1); if (!(above) && y[i] > yp) above = TRUE; i--; } /* Now if i == 0 polygon is degenerate line x=xp; since we know xp,yp is inside bounding box, it must be on edge: */ if (i == 0) return(1); /* Now we want to mark this as the end, for later: */ jend = i; /* Now if (j-i)>1 there are some segments the point could be exactly on: */ for (k = i+1; k < j; k++) { if((y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp)) return (1); } /* Now we have arrived where i is > 0 and < n_path-1, and x[i] != xp. We have been using j = n_path-1. Now we need to move j forward from the origin: */ j = 1; while (x[j] == xp) { if (y[j] == yp) return (1); if (!(above) && y[j] > yp) above = TRUE; j++; } /* Now at the worst, j == jstop, and we have a polygon with only 1 vertex not at x = xp. But now it doesn't matter, that would end us at the main while below. Again, if j>=2 there are some segments to check: */ for (k = 0; k < j-1; k++) { if((y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp)) return (1); } /* Finally, we have found an i and j with points != xp. * If (above) we may have crossed the ray: */ if(above && x[i] < xp && x[j] > xp) crossing_count++; else if(above && x[i] > xp && x[j] < xp) crossing_count--; /* End nightmare scenario for x[0] == xp. */ } else { /* Get here when x[0] != xp: */ i = 0; j = 1; while (x[j] == xp && j < jend) { if (y[j] == yp) return (1); if (!(above) && y[j] > yp) above = TRUE; j++; } /* Again, if j==jend, (i.e., 0) then we have a polygon with only 1 vertex not on xp and we will branch out below. */ /* if ((j-i)>2) the point could be on intermediate segments: */ for (k = i+1; k < j-1; k++) { if ( (y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp) ) return (1); } /* Now we have x[i] != xp and x[j] != xp. If (above) and x[i] and x[j] on opposite sides, we are certain to have crossed the ray. If not (above) and (j-i)>1, then we have not crossed it. If not (above) and j-i == 1, then we have to check the intersection point. */ if (x[i] < xp && x[j] > xp) { if(above) crossing_count++; else if( (j-i) == 1) { y_sect = y[i] + (y[j] - y[i]) * ( (xp - x[i]) / (x[j] - x[i]) ); if (y_sect == yp) return (1); if (y_sect > yp) crossing_count++; } } if (x[i] > xp && x[j] < xp) { if (above) crossing_count--; else if ( (j-i) == 1) { y_sect = y[i] + (y[j] - y[i]) * ( (xp - x[i]) / (x[j] - x[i]) ); if (y_sect == yp) return (1); if (y_sect > yp) crossing_count--; } } /* End easier case for x[0] != xp */ } /* Now MAIN WHILE LOOP begins: Set i = j, and search for a new j, and do as before. */ i = j; while (i < jend) { above = FALSE; j = i+1; while (x[j] == xp) { if (y[j] == yp) return (1); if (!(above) && y[j] > yp) above = TRUE; j++; } /* if ((j-i)>2) the point could be on intermediate segments: */ for (k = i+1; k < j-1; k++) { if ( (y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp) ) return (1); } /* Now we have x[i] != xp and x[j] != xp. If (above) and x[i] and x[j] on opposite sides, we are certain to have crossed the ray. If not (above) and (j-i)>1, then we have not crossed it. If not (above) and j-i == 1, then we have to check the intersection point. */ if (x[i] < xp && x[j] > xp) { if (above) crossing_count++; else if ( (j-i) == 1) { y_sect = y[i] + (y[j] - y[i]) * ( (xp - x[i]) / (x[j] - x[i]) ); if (y_sect == yp) return (1); if (y_sect > yp) crossing_count++; } } if (x[i] > xp && x[j] < xp) { if (above) crossing_count--; else if ( (j-i) == 1) { y_sect = y[i] + (y[j] - y[i]) * ( (xp - x[i]) / (x[j] - x[i]) ); if (y_sect == yp) return (1); if (y_sect > yp) crossing_count--; } } /* That's it for this piece. Advance i: */ i = j; } /* End of MAIN WHILE. Get here when we have gone all around without landing on edge. */ if (crossing_count) return(2); else return(0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.