CS 535
WEILER-ATHERTON

PROBLEM

Implement Weiler-Atherton. You should draw each polygon in a different color and fill the clip areas generated with a third color.

NOTES:

The Weiler-Atherton algorithm is the most general of the clipping algorithms. We have two 2D polygons (may be convex or concave and they both may have holes). The polygon to be clipped is called the SUBJECT polygon and the polygon defining the clipping area is called the CLIP polygon. To make the algorithm work easier (in the data structures, etc.) we usually assume that the exterior vertices are given clockwise and the hole vertices are given counterclockwise. In clipping we usually want to find the parts of the subject polygon that are inside the clip polygon. However, this algorithm can be used in modeling to find the "union", "intersection", and "difference" of the polygons.

The data structures are several circular linked lists of vertices which are also linked together and the clipping is done by traversing these. The lists could be doubly linked. This enables the traversal in either direction at any node in the list. Starting at a particular node and traversing in one direction will produce the interior polygon(s) while starting at a different node and traversing can produce the outside. Note that producing the exterior does need the doubly linking and care must be taken in performing the traversal.

STEP 1:

The first phase of the building of the data structures occurs when we input the edges and put them in two linked lists - the SUBJ list and the CLIP list. The vertices are place in order from input (thus clockwise). There are separate lists for the exterior and for each hole. Thus, the initial build is a standard queue insert (input a vertex - insert it at end of list). Note that this creates a list in which a standard list traversal is equivalent to "walking around" the polygon edge visiting the vertices in order.

STEP 2:

The second phase of the list building is computing and inserting the "real" INTERSECTION points (a point on both line segments). If we have a SUBJECT polygon edge (SVi to SVi+1) that intersects a CLIP polygon edge (CVj to CVj+1) at a point INTER. Note that the edges form straight lines that may intersect, we are assuming that the line segments SVi to SVi+1 intersects the line segment CVj to CVj+1. The intersection point INTER is then inserted on BOTH of the linked lists - SUBJ and CLIP. It is inserted BETWEEN SVi and SVi+1 on the SUBJ list and CVj and CVj+1 on the CLIP list. The idea is still that traversing the list using a standard list traversal we would encounter the points in their geometric order. Note that there may be several intersection points between any given pair of vertices and they must be inserted in the correct order in the lists. This is done by using the t and u values computed in the vector line intersection subprogram. Each intersection point thus has TWO nodes - one on each list (SUBJ and CLIP). We link these two entries together which provides a way of getting from one list to the other.

STEP 2.5:

Any straight line divides the plane into two half-planes. Thus each polygon edge (extended to a line) will divide the plane into two half-planes. Because we listed the vertices clockwise, we consider the half-plane to the right as containing the interior of the polygon. Thus the right half-plane is called the interior half-plane. If we consider ourselves as "walking along" a polygon edge, then we can categorize each of the INTERSECTION points as "entering" or "exiting". This is usually done from the SUBJ polygon's point of view. Thus, as we walk along the SUBJ polygon edge SVi to SVi+1 and we encounter intersection point INTER, we can ask the question - am I "entering" the CLIP polygon or am I "exiting" the CLIP polygon? The second part of computing the intersection point is to classify them as "entering" or "exiting". We create one or two lists - one for entering intersections and one for exiting intersections.

STEP3:

Once the lists are built the basic idea to do the clipping is as follows Remark: For the exterior, try starting with an EXITING point. Start the traversal on the SUBJ list (same direction as the Interior). At what point do you need to use the double link and to traverse in the opposite direction? (Hint: look at the CLIP polygon list).

IMPLEMENTATION:

In the below data structures we place all of the vertices and intersection points in a 1D array and use the subscript instead of the actual coordinates.
const int MAXVERT = 500;
const int MAXPOLYV = 50;
const int MAXH = 10;

struct Point2D
{float x,y;
};
typedef Point2D Vertices[MAXVERT];

enum VerType = {Polygon, Intersection};

typedef struct ClipListRec * ClipPtr;
struct ClipListRec
{ int Vindex;
  ClipPtr next;
  VerType Kind;
  float t;
  ClipPtr otherList;
}

struct Polygon
{ int nVertex;
  int vert[MAXPOLYV];
  ClipPtr list;
}
struct GenPolygon
{ Polygon exterior;
  int numHoles;
  Polygon Holes[MAXH];
}

GenPolygon Sub,Clip;
int entering[MAXVERT],exiting[MAXVERT];
Vertices V;
int numVertex = 0;    // size of the array of verticies
int clipPoly[MAXVERT];  // a clip polygon

int readPoint();
{ cin >> inX;  cin >> inY;
  if (numVertex < MAXVERT)
  {  V[numVertex].x = inX;
     V[numVertex].y = inY;
      idNo = numVertex;
     numVertex++;
  }
  else
     idNo = -1;
  return idNo;
}

void readPolygon (GenPolygon p)
{ cin >> p.exterior.nVertex;
  for (i = 0; i < p.exterior.nVertex; i++)
  { newId = readPoint();
    if (newId < 0)
      error
    else
    { insertAtRear (p.exterior.list,newId);
      p.exterior.vert[i] = newId;
    }
  }
  // now do the holes basically the same way
. . . 
}
// the "main" program loop would then be (pseudocode)
while (!EMPTY(entering))
{ nextInter = delete (entering);
  SEARCH (SubjectPolygon,nextInter,ptr1);
  AddToOutputList (ptr1->. . .)
  StartPoint = ptr1->. . .
  ptr1 = prt1->next;
  while (ptr1->. . . != StartPoint)
  { AddToOutputList (ptr1->. . .);
    if (ptr1-> . .   ==  INTERSECTION)
      ptr1 = prt1->otherList->next;
    else
       ptr1 = ptr1->next;
  }
  FixListForOutput();
  DrawPolygon();
  EmptyOutputList();
}