CPTR 124 Fundamentals of Programming


In this lab you will design and implement your own class given some minimal requirements. You will determine the needed instance variables and implement methods that conform to a specified interface that client code expects. In the process you will organize your classes into a Java package.
  1. Teams. You may work with a partner on this lab. You may partner with anyone in this class, but it is helpful if one of you remembers how to compute slopes, intercepts, etc. from high school algebra class. Please submit only one check sheet containing both partners' names.

    You may work alone if you insist.

  2. Housekeeping. Create a project named Lab7. In your Lab7 project, create a package named Geometry. The Point and Line classes described below are to be part of this Geometry package.

  3. Geometric Primitives. Several primitives are used in geometry:

    • Two-dimensional geometric points are real number pairs (x, y). We'll approximate real numbers with double precision floating point numbers.
    • A geometric line is an infinite set of colinear points. A line can be defined by two nonequal points on that line, and are often represented by an equation.

  4. Point Class. Create a Point class in your Geometry package that provides the functionality of two-dimensional geometric points. Specifically:

    1. You must be able to create a Point object using any of three constructors:
      • one that takes two doubles (the x and y coordinates),
      • one that takes a reference to a Point object (a new Point object is created with the same x and y coordinates), and
      • a default constuctor with no arguments that creates a point at the origin (0,0).

    2. Write a toString() method that takes no arguments and returns a String object. It shows the point in a nice human-readable form. Whenever Java needs to convert an object to a string (for example, when calling System.out.println()), the object's toString() method is called:
      		 Point pt = new Point(3, 4);
      		 //  Prints "pt = (3.0,4.0)"
      		 System.out.println("pt = " + pt);
      		 

      Use a DecimalFormat object with the format string "0.##" to limit the display of the double precision floating point numbers to two digits after the decimal point. This DecimalFormat can be shared by all Point objects (that is, make it a static field of the Point class).

      The difference between using a DecimalFormat object and our trusty System.out.printf() is that printf will always show the two decimal places, but our format object will show at most two decimal places.

    3. Include accessor methods to read a point's coordinates:
      • getX(): returns the x coordinate of the point
      • getY(): returns the y coordinate of the point
      See the sample client code for examples of their usage. Notice that clients can examine the x and y values of a point object, but clients cannot modify them. This means that Point objects are immutable.

      There should be no other way for code outside the Point class to determine a point's x or y coordinate values.

    4. Include a distance() method that computes the distance to a point passed into it:

      		 Point pt1 = new Point(3, 4);
      		 System.out.println("pt1 = " + pt1);
      		 Point pt2 = new Point(15, 10);
      		 //  Prints "Distance: 13.416407864998739"
      		 System.out.println("Distance: " + pt1.distance(pt2));
      		 

      As you can see, to find the distance between points pt1 and pt2, use

      		 pt1.distance(pt2)
      		 

      The call

      		 pt2.distance(pt1)
      		 

      would work just as well.

    (Don't forget that your Point class should be a part of your Geometry package.)

  5. Line Class. Create a Line class in your Geometry package that provides the functionality of two-dimensional geometric lines. Specifically:

    1. You must be able to create a Line object. Its only constructor accepts two nonequal Point objects that determine the line. The constructor's behavior is undefined if the two points passed to it are equal. (Being undefined means you don't have to worry what your program does if a client attempts to create such a line.)

      While clients create a Line object with two points, these actual points are not useful after the line is created. You should precompute the slope and y-intercept (or x-intercept, as appropriate) and let these two values define the state of a line object. (See below for more information about intercepts.)

      This information about the slope and intercept should not be directly accessible by clients. Clients can see these values via methods (see below) but cannot change them. This means that lines, like points, are immutable objects.

    2. Include a slope() method that returns the slope of the line. Recall that a vertical line has an undefined slope. We'll let our vertical lines have a slope of Double.POSITIVE_INFINITY, a static final value (obviously respresenting positive infinity) publically available in the java.lang.Double class.

    3. Include an intercept() method that returns:
      • for a non-vertical line, the y coordinate of the point of intersection with the y-axis, or
      • for a vertical line, the x coordinate of the point of intersection with the x-axis.
      Thus, intercept() returns the y-intercept when called on a non-vertical line, and it returns the x-intercept when called on vertical lines.

    4. Write a toString() method that takes no arguments and returns a String object. It shows the equation of the line in a nice human-readable form. The equation should be in slope-intercept form: y = mx + b. For example:

      		 Line myLine = new Line(new Point(3, 4), new Point(15, 10));
      		 //  Prints "Line: y = 0.5x + 2.5"
      		 System.out.println("Line: " + line);
      		 

      Careful! Recall that a horizontal line that crosses the y-axis at 3 has the equation

      y = 3

      and a vertical line that crosses the x-axis at 3 has the equation

      x = 3

      Your program should handle these situations correctly.

      As with the Point class (see above), use a shared DecimalFormat object to limit numeric values to two decimal places.

    5. Include a satisfies() method that accepts a single Point as a parameter and returns a boolean value indicating whether or not the point is on the line.

      How do you determine if a point satisfies a line? If you know the line's slope and intercept (i.e., the line's equation), you simply plug in the x and/or y values of the point and see if the equality holds:

      • for y = mx + b, plug in x and see if you get y
      • for y = c, where c is some number, y has to equal c (x does not matter)
      • for x = c, where c is some number, x has to equal c (y does not matter)

    6. Include an intersect() method that accepts a single Line as a parameter and returns a Point object indicating the point of intersection of the two lines. If the lines are parallel, the result is null.

      How do you compute the point of intersection of two lines?

      • If the two lines have the same slope, there is no single point of intersection. That's why we return a null reference.
      • If neither line is horizontal or vertical, we have a pair of equations that look like
        y = m1x + b1
        y = m
        2x + b2
        since both righthand expression equal y, set them equal to each other:
        m1x + b1 = m2x + b2
        and solve for x:

        m1x - m2x + b1

        =

        m2x - m2x + b2

        m1x - m2x + b1

        =

        b2

        m1x - m2x + b1 - b1

        =

        b2 - b1

        m1x - m2x

        =

        b2 - b1

        (m1 - m2)x

        =

        b2 - b1

        x

        =

        (b2 - b1)/ (m1 - m2)

        This final equation

        x = (b2 - b1)/ (m1 - m2)

        serves as a formula to compute x. Once you have x you can plug it into either of the original equations to compute y. The x and y values are then used to create the required point.

      • If one of the lines is horizontal or vertical, you know immediately one of the coordinates (x for vertical lines and y for horizontal lines). Plug the known value into the equation for the other line to compute the other coordinate.
      • If one of the lines is horizontal and the other is vertical, you know immediately one of the coordinates (the x coordinate comes from the vertical line and the y coordinate comes from the horizontal line).

    Like your Point class, your Line class should be a part of your Geometry package.

  6. Handling Floating Point Imprecision. Since floating point numbers are imprecise, the == operator may not work as expected when comparing floating point numbers. The == operator tests for exactly matching bit patterns between its two operands. Because of the imprecise representation, a computed value for 25 may be 24.99999 or 25.000001 instead of exactly 25. This means that a point that supposed to be on a line (when computing satisfies()) may not really appear to be on the line! A better approach, for our situation, is to test to see if the absolute value of the difference is less than a small value. For example, if a and b are floating point numbers, instead of using

    a == b

    write a class method named equals() that accepts two doubles and returns the result of the following comparison:

    |a - b| < 0.0001

    (The vertical bars mean absolute value.) This, in effect, says "The values of a and b are so close that we consider them equal."

    In your equals() method treat Double.POSITIVE_INFINITY specially. Use == to see if a and b are both Double.POSITIVE_INFINITY; if so, return true. If one is == to Double.POSITIVE_INFINITY but the other is not, then return false. If neither a nor b are Double.POSITIVE_INFINITY, then proceed with the absolute value of the difference code mentioned above. This procedure will suffice for this program.

  7. Class Organization. As mentioned above, place your Point and Line in a package named Geometry.

  8. Test Code. Your two classes should work with the code provided here (GeometryTest.java). This GeometryTest class should reside in the default package of your Lab7 project. The output of this test program can be found here, but, more importantly, your code should work with any Java program that needs to use these geometric objects and respects the interface described above. Your class may be tested against other Java code, not just the one provided above. Feel free to write other test programs to exercise your code.

  9. Coding Conventions. As always, use the standard Java coding style conventions.

  10. Submission. When you believe the program works correctly:
    1. recheck your style to make sure it is similar to the Sun guidelines,
    2. let me check and review your lab with you,
    3. get a Lab Report (one per team),
    4. close your project, exit Eclipse, and log out from your workstation.