Computer Graphics (CMSC 427) - Spring 2000

                                                Prof. Ben Bederson

                                                Homework Three: Clipping

                                                Due: Tuesday, Feb 22nd, 2000

 

The purpose of this homework is to learn about clipping.

Your assignment is to create a program that implements the Weiler-Atherton algorithm for clipping any polygon against any other polygon, including concave polygons.  However, you are not required to make your solution work on polygons or clip regions with holes.  Specifically, your program must read data from a file named "hw3-data.txt" that contains a list of subject and clip polygons, and one at a time draw them to the screen.  Your program should include a "next" button that can be pressed to process and display the output from the next entry from the input data file.  You can use the standard Graphics.drawPolygon and fillPolygon functions for rendering.  Note that you will need to be able to determine whether a point is inside a polygon for this algorithm.  Code that implements this function is at end of this file.

You should render the polygons as follows:

The format of the "in.data" file is as follows.  All numbers are integers.  The first line specifies the number of tests.  There are two lines per test with input and clip polygons alternating lines.  Each polygon is specified by the number of vertices followed by a comma followed by comma-delimited list of vertices with x and y coordinates alternating.  You should use this hw3-data.txt file to test your algorithm. We may also add test cases to the data file when grading your homework.  A simple data file with one test case would look like this:

1
3, 0, 0, 100, 0, 0, 100
4, 0, 0, 50, 0, 50, 50, 0, 50

To read and write data from files with Java, you are welcome to start with the sample code below which reads in the data file format for this homework and does nothing with it.

Please submit this assignment using the WAM submit program after making a zip (or tar) file containing all of your source code and Visual J++ project files.  Put them in a directory with your last name, and include a README file if you want to tell us anything about your code, the algorithm you used, how to use it, etc.


/**
 * Test program to read in a datafile for hw3.
 */
import java.io.*;
import java.util.*;
import java.awt.*;

public class testio
{
    public testio(String fileName) {
        try {
	    String line; 		// General purpose line buffer
	    String token;		// Token while parsing the string
	    int tests;			// Number of tests
	    int numVertices;		// Number of vertices for current polygon
	    Point vertices[] = null; 	// Actual vertices for current polygon
	    int x, y; 			// Temporary coordinate vras
	    int vertexNum; 		// Current vertex number within current polygon
	    StringTokenizer st; 	// Tokenizer to help parse line
	    boolean readingClipPolygon; // False when reading source polygon, true for clip polygon

			// A LineNumberReader in Java can read a line of text in one shot
	    LineNumberReader reader = new LineNumberReader(new FileReader(fileName));
	    line = reader.readLine();
	    tests = Integer.parseInt(line);
	    for (int test=0; test<tests; test++) {
			// First read polygon, then clip
	        readingClipPolygon = false;
	        for (int i=0; i<2; i++) {
			// Start by parsing line and extracting number of vertices
		    line = reader.readLine();
		    st = new StringTokenizer(line, "\t\n\r, ");
		    token = st.nextToken();
		    numVertices = Integer.parseInt(token);
		    vertices = new Point[numVertices];
		    System.out.println("clipPolygon = " + readingClipPolygon + ", numVertices = " + numVertices);
			// Then, for each vertex, read x and y coordinate
		    for (vertexNum=0; vertexNum<numVertices; vertexNum++) {
		        token = st.nextToken();
		        x = Integer.parseInt(token);
		        token = st.nextToken();
		        y = Integer.parseInt(token);
		        System.out.println("x=" + x + ", y = " + y);
		        vertices[vertexNum] = new Point(x, y);
		    }
			// The vertices are filled up now, and the application
			// should do something with them.
		    readingClipPolygon = true;
	        }
	    }
        }
        catch (IOException e) {
	    System.out.println("Trouble reading file");
        }
    }

    public static void main(String args[]) {
	new testio("hw3-in.data");
    }
}

/**
 * This is code that implements a method to determine if a point
 * is inside of a polygon.
 */
import java.awt.*;

public class Util {
    /**
     * A bit of test code to show use of the pointInPolygon method.
     */
    static public void main (String[] args) {
	Point vertices[] = new Point[4];
	Point p = new Point();

	vertices[0] = new Point(0, 0);
	vertices[1] = new Point(0, 100);
	vertices[2] = new Point(10, 10);
	vertices[3] = new Point(100, 0);
	p.setLocation(5, 5);
	System.out.println("point in polygon: " + pointInPolygon(p, vertices));
    }

    /**
     * Returns true if point, is inside polygon.
     */
    public static boolean pointInPolygon(Point p, Point vertices[]) {
	int i;
	float angle = 0.0f;
	boolean inside;

				// Implemented with the winding angle algorithm.
				// Just add up the angles between points
	for (i=0; i<vertices.length-1; i++) {
	    angle += angleBetweenPoints(vertices[i], p, vertices[i+1]);
	}
	angle += angleBetweenPoints(vertices[vertices.length-1], p, vertices[0]);

				// Allow for a bit of rounding
				// Ideally, angle should be 2*pi.
	if (Math.abs(angle) > 6.2) {
	    inside = true;
	} else {
	    inside = false;
	}

	return(inside);
    }

    /**
     * Returns the angle at p from p1 to p to p2.
     * (Positive is counter-clockwise).
     */
    public static float angleBetweenPoints(Point p1, Point p, Point p2) {
	Point v1 = new Point();
	Point v2 = new Point();
	float z, s;
	float theta;
	float t1, t2;

	v1.setLocation(p1.x - p.x, p1.y - p.y);
	v2.setLocation(p2.x - p.x, p2.y - p.y);

	z = (v1.x * v2.y) - (v1.y * v2.x);
	s = (z >= 0.0f) ? 1.0f : -1.0f;
	t1 = length(v1) * length(v2);
	if (t1 == 0.0f) {
	    theta = 0.0f;
	} else {
	    t2 = ((v1.x * v2.x) + (v1.y * v2.y)) / t1;
	    if ((t2 < -1) || (t2 > 1)) {
		theta = 0.0f;
	    } else {
		theta = (float)(s * Math.acos(t2));
	    }
	}
    
	return theta;
    }

    public static float length(Point v) {
	return (float)Math.sqrt(v.x*v.x + v.y*v.y);
    }
}