6.170 Laboratory in Software Engineering
Spring 2000
Problem Set 6: GizmoBall
April 10, 2000: Preliminary Design due
April 24, 2000: Initial Release due
May 8, 2000: Implementation and Critique due
    No late projects accepted!
May 9-11, 2000: Demonstration

Introduction

Your final project is to design, build, and test a program that plays Gizmoball. GizmoBall is a game similar in flavor to pinball, a popular arcade game. Pinball is a fast-moving game in which the object is to keep a ball moving around in the game, without falling off the bottom of the playing area. The player is provided with controls for a set of flippers which can be used to bat at the ball as it falls.

The advantage of GizmoBall over a traditional pinball machine is that GizmoBall allows users to construct their own machine layout. In fact, GizmoBall is more than just a pinball game because the user can use it to build complicated "Rube Goldberg" contraptions, with lots of moving gizmos, which are intended to be watched rather than played. (If you don't know what a Rube Goldberg machine is, check out one of these sites: http://www.anl.gov/OPA/rube/index.html or http://www.rube-goldberg.com).

Grading

You will work in teams of three or four. All members of a team will get the same grade, except in unusual circumstances.

You may reuse code from your work earlier in this semester. With the exception of the standard Java libraries, do not incorporate any other code in your project. If in doubt, check with your TA. While borrowing code from others is usually good software engineering practice, in this project it will be considered cheating.
 
Stage Due date % of project grade Graded on
Preliminary design April 10
10%
Have you identified the issues?
Weekly meetings 
with TA
April 11-May 4
5%
Participation of all team members.
Initial Release April 24
25%
Is it a good design? Is the required functionality present?
Design Critique May 8
25%
Are the tradeoffs & alternatives thoroughly analyzed?
Implementation & 
test
May 8
35%
Does it work, have you demonstrated that it works?
Each stage of this assignment should be handed in as hardcopy to your TA or to the course secretary (Kincade Dunn, NE43-529) by 4:30 PM on its due date. Additionally, all of the source and compiled code should be put online on May 8, in your team project directory.

The final report is due by 4:30 pm on May 8, 2000. Because of end of term constraints, late assignments will not be accepted.

GizmoBall Overview

Because this problem set is a design exercise, the assignment specifies what the user should be able to do and leaves it up to you to figure out what modules and interfaces are appropriate. This section gives an overview of Gizmoball. A more detailed specification is given in Appendix 1. Also, to make automated testing feasible, your implementation must support a rigid file format (defined in Appendix 2), in addition to the loosely-specified graphical user interface.

GizmoBall has a graphical user interface that allows a user to:


A screenshot of one implementation of GizmoBall.
Your implementation may look different.

The picture above illustrates the most important features of GizmoBall. First a gizmo palette on the side provides the user with a variety of operations (square, circle, triangle, flipper) for placing gizmos in the playing area. Next a "modifications" toolbar on the bottom provides the user with a variety of operations (move, delete, rotate) for editing the gizmos in the playing area.

Note, in particular, that the modifications toolbar provides a connect button. After this button is pressed the user can connect gizmos together. For example, the user might click on one of the flippers, and then on one of the circular bumpers. This indicates that every time the bumper is hit, the flipper will move. Alternatively, the user might click on one of the flippers and then hit one of the keys on the keyboard. This indicates that every time the user presses that key the flipper will move. Several triggers might activate the same flipper.

In addition, in the example, there is a blue bar across the bottom of the playing area called the absorber gizmo. This gizmo "eats" the ball when the ball crosses it, and then holds the ball in the lower right hand corner of the playing area. The absorber gizmo also has an action associated with it, such that if the absorber is holding the ball and the absorber's action is triggered, the absorber will shoot the ball straight up in the direction of top of the playing area.

Finally, there is a menu at the top of the window. The buttons on the menu allow the user to save or load game configurations and also to run or stop the game. In the picture a game is in progress: the ball is the small red circle near the upper right hand corner. It has just hit one of the green circular bumpers and is heading up toward one of the magenta flippers.

Stages of the assignment

Preliminary Design (due April 10)

It should be possible for you to express your preliminary design in under 15 pages. Your document must contain the following.
  1. Revised Specification

  2. The description of GizmoBall in Appendices 1 and 2 of this assignment is incomplete. For example, you need to design a user interface for the program, decide on any extensions to the basic required functionality you wish to provide, and resolve any ambiguities you find in the specification. The resulting revised specification should be as precise as possible about what you think the completed program will do.
  3. Problem Object Model

  4. Construct an object model that summarizes the possible states of the application from the user's point of view. The objects here will be objects from the problem domain (such as flippers, bumpers, balls and gizmo actions), not objects from the code. Try and keep this as abstract as possible, avoiding any implementation details. To check that your model is complete, ask yourself whether the model contains sufficient information to produce the behaviors you have described in the revised specification. State informally any constraints that arise from the properties of the problem itself, but which cannot be shown graphically.
  5. Implementation Overview

  6. Describe the major modules of your implementation and explain why you chose this decomposition. Draw an MDD to illustrate the proposed relationship between these major modules. Also describe and explain any other major design decisions you have made. To explain the decomposition and other design decisions, argue that they contribute to simplicity, extensibility (ease of adding new features),  partitionability (different team members can work on different parts of the design without communicating constantly), or similar software engineering goals.
  7. Project plan

  8. List milestones for the team and allocation of tasks to each team member. A milestone is what you expect to be done by some date, such as a tested module or a working feature---an achievement that is objective, easy to evaluate, and significant. There must be at least two milestones in the project between the preliminary design and the final deadline. The division of labor should be equitable.
The preliminary design will be graded for clarity of expression and for whether you have thought rigorously about the problem, identified the major issues and challenges, and proposed a design that makes sense.

Weekly Meetings with TA (April 11 - May 4)

Each team will meet with its TA once a week for half an hour. The official time for these meetings is during the regular 10-11 AM class time. To receive full participation credit: Although the progress document must be clear, it is short and informal and can be handwritten if you desire. This document will form the basis of discussion in the meeting, and the TA will keep it on file as a record of progress made. The team should bring multiple copies to the meeting, one for each team member and one for the TA. This progress document should include the following information:

Initial Release (due April 24)

The initial release has two components: a final design writeup, and a demonstration of basic functionality.

Final Design

The final design document is an improved version of the preliminary design document. You should include all of the sections from the preliminary design, updated as appropriate. In the project plan section, explain any milestones that you missed and give reasons why you were unable to reach them.

It should be possible for you to express your final design in under 20 pages. In addition to the sections from the preliminary design, the final design contains the following.

  1. Validation Strategy

  2. Describe your strategy for finding and removing bugs from your program. Explain what classes of errors you expect to find (and not to find!) with this strategy.
    Discuss what aspects of the design make it hard or easy to validate.
The final design is due two weeks before the final project due date. At this point, you should have found all major mistakes in your design. Therefore the final design will be graded on whether it is a good design, in addition to clarity of expression.

Required Functionality

At the initial release deadline, we will ask you to demonstrate some specific functionalities. It is acceptable for you to run four separate java programs to demonstrate the following four features:
  1. Demonstrate key-press triggering of a flipper on the screen. The flipper should move and then return to its original position. You should be able to trigger it a second or third time by pressing the key again after it has returned to the original position. (You need not demonstrate connecting the key to the flipper in build mode.)
  2. Demonstrate a working absorber, ball motion, and gravity. In running mode, with no bumpers or flippers on the screen and the ball sitting still in the absorber, you should be able to press a key, observe the ball shoot up out of the absorber, slow down as it rises, fall back to the absorber, and return to its original position. Also demonstrate that you can shoot it out a second time.
  3. Handle ball collisions with bumpers and the walls. Proper handling of ball-flipper collision is not required at this stage. During running mode, a ball shot out of the absorber must behave properly when it collides with bumpers or with the outer walls.
  4. Demonstrate loading files in the standard format. Given a test file, your implementation should display the gizmos specified in that file at the specified locations on the screen. You should be able to load and display all the standard gizmos.
Your animation in run mode should be smooth and adequate to demonstrate the features which we require above.

Amendment

Shortly after submitting the initial release, you will receive an amendment to the program specification. In large programming projects, the requirements often change during development. Your design should be flexible enough to adapt easily to possible changes in the specification.

Implementation and Critique (due May 8)

It should be possible for you to describe your implementation and critique your design in under 20 pages (except for the Specifications in Item 2, which may take another 5-10 pages and should be placed in an appendix). Conciseness is important. The team which won the 1999 Gizmoball design competition submitted a 7 page final report. The final report includes the following.
  1. Representation Rationale

  2. For each data abstraction with a nontrivial rep, give reasons for choosing the rep and some of the design tradeoffs. Mention any particular problems you discovered.
  3. Specifications

  4. For each major module or interface, give a specification (as comments in the code). This should include overviews of abstract data types, and requires/modifies/effects specs of methods. Turn in printouts of the specifications.
  5. Code

  6. Put your code online in your team project directory. You do not need to turn in a printout of your code
  7. Design Changes

  8. Detail what changes you have made to the design that was submitted on April 24. Be precise.

    It is vitally important that your TA have versions of the descriptive parts of the Revised Specification, Implementation Overview, and Validation Strategy that accurately describe your submitted code and what you actually did for testing or validation. If the description is not accurate, the submission will not be graded accurately.

  9. Known Bugs and Limitations

  10. In what ways does your implementation fall short of the specification? Be precise. Although you will lose points for bugs and missing features, you will receive partial credit for accurately identifying those errors, and the source of the problem.
  11. Design Critique
  12. The design critique is one of the most important parts of your submission, and will weigh strongly on your overall project grade.
The final report is due by 4:30 pm on May 8. Because of end of term constraints, LATE ASSIGNMENTS WILL NOT BE ACCEPTED; hand in what you have.

Demonstrations (May 9-11)

Demonstrate your working program for your TA. Again, all team members must be present to give a brief presentation and answer questions pertaining to their responsibilities in the development process. The presentation and demonstration should not last for more than 15 minutes. Expect another half hour of questions and discussion.

Design Contest

When you submit your implementation and critique on May 8, indicate on the first page if you want it to be considered for one of the following three prizes.
  1. Best design: Awarded for the project with the best abstraction, modularity, extensibility, simplicity, etc. The quality of the final report is also considered.
  2. Best Gizmoball game: Awarded for the project with the best game-play. Part of your submission for this prize should be an input file that sets up the playing area; the prize is for the playable game itself, not for the construction kit.
  3. Most artistic: Awarded for the project that is the most beautiful or fascinating to watch. Part of your submission for this prize should be an input file that sets up the playing area. When run with this input file, the ball or balls should bounce around forever without the user needing to press any keys.
Whether your program implements only the basic required functionality or extra gizmos etc. will not be considered when making the design award. However extra functionality that improves game-play or "Rube Goldberg" artistry will be an asset in competing for the other two awards.

Judging will be done initially by the TAs and final judgements will be made by the lecturers. One project can win multiple prizes. Winners will be announced at the last class on May 11.

What We Give You

Animations in Java are quite challenging. You will use the java.awt and javax.swing packages to construct your graphical user interface (GUI). We have provided you with a demonstration program that shows how to animate the movement of a ball bouncing around the window. It also demonstrates how to get your program to listen to user events, such as clicking on a toolbar button, pressing a key or dragging the mouse.

We have also provided you with a library of physics routines for calculating the dynamics of elastic collisions. You are welcome to use this code as is, or modify it in any way that you like.

Setup:

You should make sure that all members of your group can compile and execute the demo GUI.

The animation example and the physics library will be available in /mit/6.170/src/ps6 sometime soon. We will provide specific instructions for downloading these resources.

The specifications for the physics library are also provided at the end of this document.

Hints

Revision Control

For many of you, GizmoBall is the first large software engineering project you have to do in a team. You will be surprised at how challenging it is to keep the separate changes made by team members integrated into a single version of the source code. An automated tool that helps with this problem is called a revision control package. We strongly recommend that you use one.

The most widely used (free) revision control package is the Concurrent Version Control (CVS). CVS is available on Athena. If you work at home on a Linux machine, it probably has CVS installed already. For Windows machines, download WinCVS from http://www.wincvs.org/. For Macs, download MacCVS from http://www.maccvs.org/.

Athena provides an extensive CVS manual. Use the command info cvs to access the manual. You will want to read the sections "Starting a new project" and "Overview/A sample session" to get started. Additionally, if one or more of your group members wants to work from home, you will want to read the section on "Repository/Remote repositories."

Additional information is available from http://www.mit.edu/iap/unixdev/www/cvs.html and http://www.gnu.org/manual/cvs-1.9/cvs.html.

System model

Another tool we strongly recommend you use is called make. This tool allows you to write a makefile which is a model of your system: which files contain code, what work needs to be done to compile the system, what work needs to be done to run your tests, what work needs to be done to clean up the directory, etc. Once you have written the makefile, you can do any of these tasks by typing a single command.

On athena, type info make.



Appendix 1: Detailed Requirements

  1. Your implementation must support two modes of execution, building and running. In building mode, the user can add gizmos to the playing area and can modify the existing ones. In running mode, a ball moves around the playing area and interacts with the gizmos.
  2. The playing area must have the following characteristics:
  3. In building mode the user can:
  4. Connect a trigger from one gizmo to the action of a gizmo.
  5. Connect a key-press trigger to the action of a gizmo.
  6. Remove a gizmo from the playing area
  7. Add a ball to the playing area
  8. Save to a file named by the user.
  9. Load from a file named by the user. You must be able to load a game saved in the standard format.

  10. Switch to running mode
  11. Quit the application.
  12. In running mode, the user can:
  13. In running mode, GizmoBall should
  14. There are six standard gizmos that must be supported.


Appendix 2: The GizmoBall File Format


INFORMAL DESCRIPTION

Gizmoball files are command scripts. The syntax is, perhaps, somewhat richer than one might design for a simple game, but we also wanted the file format to be usable for debugging and testing the program independently of the GUI. That is, the file format specifies a standard interface that everyone must implement, so that the TAs can test the functionality of your program. As such, the files specify commands for exercising all the functionality of your program, including deleting and modifying gizmos, not just a list of gizmos and their positions.

Each Gizmoball file is made up of multiple lines. Each line represents a different command. Each command is made up of an opcode and zero or more arguments. For example:

        Triangle 0 7
This is a single command. It's opcode is "Triangle" and its arguments are "0" and "7". This command specifies that the program should create a new triangular bumper at the location <0,7>. There are similar commands for creating square and circular bumpers and flippers.

The syntax of Gizmoball files is designed to be easy for your program to read. Each command lives on a seperate line so that you can use the BufferedReader.readLine() method to read in each command. The tokens of each command are seperated by spaces or tabs so you can use the StringTokenizer class to break up each line into its constituent parts.

Notice that the Triangle command doesn't specify a rotation for the triangle. Instead, the triangle command specifies that the triangle should be placed at a default rotation, and if you want the triangle at a different rotation you need to use the "Rotate" command:

        Triangle 3 5
        Rotate 3 5
        Rotate 3 5
This creates a triangle bumper at position <3,5> and then rotates it twice by 90 degrees in the clockwise direction, for a total rotation of 180 degrees. Along with the Rotate command, there are also commands for moving gizmos and deleting gizmos. For example:
        Square 10 2
        Square 15 15
        Delete 10 2
In this example we create a square bumper at <10,2>, another at <15,15> and then delete the square bumper at <10,2>, leaving one square bumper at <15,15>.

There are also commands for connecting triggers to actions. For example:

        Square 13 13
        LeftFlipper 5 18
        Connect 5 18 13 13
This creates a square bumper at <13,13> and a left handed flipper at <5,18>. Then the "Connect" command specifies that the flippers action should be triggered whenever the ball hits the square bumper.

You can also specify that the action of a gizmo is associated with a particular key being pressed or released with the KeyConnect command:

        RightFlipper 9 18
        KeyConnect 9 18 32
This specifies that the right handed flipper at <9,18> should be activated whenever the space bar key is pressed or released ("32" is the symbol number that the Java AWT gives to the space bar).

Because you might also want to allow the absorber and outer walls to trigger various actions, there are special position markers for those gizmos:

        KeyConnect Absorber 127
        Connect 9 18 OuterWalls
This set of commands would cause the absorbers actions to be triggered by key 127 (the "Delete" key) and would have the ball hitting any of the outer walls trigger the action of the gizmo at <9,18>.

The final command in the gizmoball file allows you to specify the position and velocity of the ball. Because the ball can be at intermediate points within a particular square, the coordinates are specified as floating point numbers:

        Ball 14.2 4.5 -3.4 -2.3
Here is the gizmoball file for the example shown above. It specifies a triangular bumper in the upper right hand corner, a bunch of circular and square bumpers and a few flippers. The action of the upper flippers is triggered by the "space" key, the action of the lower flippers are triggered by the "q" and "w" keys, and also by hitting some of the circular bumpers. The action of the absorber is triggered both by the "delete" key and also by the absorber itself! This allows the game to run continuously. Every time the ball hits the absorber, the absorber immediately shoots it back up towards the top again.
Triangle 19 0
Rotate 19 0

Square 0 2
Square 1 2
Square 2 2
Square 3 2
Square 4 2
Square 5 2
Square 6 2
Square 7 2
Square 8 2
Square 13 2
Square 14 2
Square 15 2
Square 16 2
Square 17 2
Square 18 2

Circle 4 3
Circle 5 4
Circle 6 5
Circle 7 6
Circle 9 9
Circle 10 9
Circle 11 10
Circle 12 9
Circle 13 9
Circle 15 6
Circle 16 5
Circle 17 4
Circle 18 3

LeftFlipper 9 2
KeyConnect 9 2 32

RightFlipper 11 2
Rotate 11 2
KeyConnect 11 2 32

LeftFlipper 8 7
KeyConnect 8 7 81
Connect 8 7 4 3
Connect 8 7 5 4
Connect 8 7 6 5
Connect 8 7 7 6
Connect 8 7 10 9
Connect 8 7 11 10
Connect 8 7 13 9

RightFlipper 14 7
Rotate 14 7
KeyConnect 14 7 87
Connect 14 7 9 9
Connect 14 7 11 10
Connect 14 7 12 9
Connect 14 7 15 6
Connect 14 7 16 5
Connect 14 7 17 4
Connect 14 7 18 3

KeyConnect Absorber 127
Connect Absorber Absorber

FORMAL SYNTAX

<file> ::= <request>*

<request> ::= <command>"\n"

<command> ::= <gizmoOp> COORDINATE COORDINATE        |
              Rotate <position>                      |
              Delete <position>                      |
              Move <position> COORDINATE COORDINATE  |
              Connect <position> <position>          |
              KeyConnect <position> KEYID            |
              Ball FLOAT FLOAT FLOAT FLOAT

<gizmoOp> ::= Square | Circle | Triangle | RightFlipper | LeftFlipper

<position> ::= COORDINATE COORDINATE |
               Absorber              |
               OuterWalls

COORDINATE represents any integer
FLOAT represents any floating point number

GizmoBall keywords are case insensitive.

Square XCoord YCoord
Circle XCoord YCoord
Triangle XCoord YCoord
RightFlipper XCoord YCoord
LeftFlipper XCoord YCoord
Creates the appropriate gizmo at the specified coordinates, in the default orientation. The default orientation for each gizmo is:
Square
none (All orientations are equivalent)
Circle
none (All orientations are equivalent)
Triangle
One corner in the north-east, one corner in the north-west, and the last corner in the south-west. The diagonal goes from the south-west corner to north-east corner.
LeftFlipper
pivot in north-west corner, other end in south-west corner
RightFlipper
pivot in north-west corner, other end in north-east corner

Rotate <position>
Performs a 90 degree clockwise rotation on the gizmo at the specified position. Note that some gizmos (like the Absorber and the OuterWalls) can not be rotated.

Delete <position>
Deletes the gizmo at the specified position. After this operation, this gizmo will no longer exist.

Move <position1> XCoord YCoord
Moves the gizmo from <position1> to <XCoord, YCoord>.

Connect <consumer-position> <producer-position>
Makes the gizmo at <consumer-position> a consumer of the triggers produced by the gizmo at <producer-position>. That is, every time the ball hits the producer gizmo, the consumer's action will happen. <consumer-position> and a <producer-position> can each be coordinate pairs, "Absorber", or "OuterWalls".

KeyConnect <position> KeyID
Makes the gizmo at <position> a consumer of the trigger produced when the key KeyID is pressed or released.


Appendix 3: The physics package

NOTES:

(1) The intended use of the Geometry library is as follows: Suppose that all the objects in the scene are called "Bouncers". First the timeUntilCollision() methods are called to calculate the time at which the ball will collide with each of the Bouncers. The minimum of all these times (call it "mintime") is the time of the next collision. Next the client must update the position of the ball and all the Bouncers to mintime. At this point the ball and the Bouncer it is about to hit are exactly adjacent to one another. Now the client calls the appropriate reflect() method to calculate the changes to the ball's velocity vector. Finally the client updates the ball's velocity vector, and then repeats the entire process.

(2) The methods here for dealing with line segments do NOT deal with the end-points of the line segment. To properly handle this in your Gizmoball game, you will need to make each of your shapes out of a combination of line segments with 0-radius circles at the end points. For example: if you have two line segments, one between the points <1,1>,<1,2> and the other from <1,1>,<2,1> and a ball is approaching from <0,0> in the <1,1> direction, it will hit the ends of both the line segments at a 45 degree angle and something REALLY WEIRD will happen. But if you put a little tiny (radius 0) circle at <1,1>, then the ball will bounce off the circle in the expected manner.

(3) The Geometry package is dependant on the java.awt.geom package. It uses objects Point2D.Double and Line2D.Double. These types are mutable, so be careful when using them in your code. 


public class Angle {
  // Overview: This immutable abstract data type represents the
  // mathematical notion of an angle.
  //
  // Abstraction Function: The angle <a> such that cos(a) = cosine and
  // sin(a) = sine
  //
  // Rep. Invariant: cosine^2 + sine^2 = 1
  //
  // Rep. Rationale: In most of the math we use here we start in
  // cartesian coordinates, so calculating sine and cosine is
  // relatively efficient (just a sqrt and a division).  Finding the
  // angle in terms of arcsin and arccos would be very slow.  On the
  // other hand, adding and subtracting angles that are represented
  // this way can be done relatively efficiently using standard
  // trig. identities.


  // useful constants
  static public Angle zero = new Angle(1.0, 0.0);
  static public Angle piOverFour = new Angle(Math.sqrt(0.5), Math.sqrt(0.5));
  static public Angle piOverTwo = new Angle(0.0, 1.0);
  static public Angle Pi = new Angle(-1.0, 0.0);
  static public Angle threePiOverTwo = new Angle(0.0, -1.0);
  static public Angle twoPi = zero;

  // CREATORS:

  public Angle() 
    // effects: initializes this to be the angle with 0 radians

  public Angle(double a) 
    // effects: initializes this to be the angle with <a> radians

  public Angle(Angle a) 
    // effects: initializes this to be a copy of Angle <a>

  public Angle(double c, double s) 
    // effects: initializes this to be the angle with cosine c and sine s

  public Angle(double x, double y, double z) 
    // effects: initializes this to be the angle at the corner of
    //    x and z for the right triangle (with the corner of
    //    sides x and y being 90 degree)


  // OBSERVERS:

  public String toString() 
  public int hashCode() 
  public boolean equals(Angle a) 
  public boolean equals(Object o) 

  public double cos() 
    // effects: returns the cosine of this

  public double sin() 
    // effects: returns the sine of this

  public double tan() 
    // effects: returns the tangent of this

  public boolean greater(Angle c) 
    // effects: returns true if and only if <this> is a bigger angle than <c>.

  public boolean between(Angle a, Angle b) 
    // effects: returns true when this angle is contained in the arc swept out
    // starting at angle a and ending before angle b.

  // PRODUCERS:

  public Angle plus(Angle a) 
    // effects: returns the angle <this> + <a>

  public Angle minus(Angle a) 
    // effects: returns the angle <this> - <a>
}

public class Circle {


    /* Overview: This mutable abstract data type represents a
     * mathmatical circle in two dimensions.  The circle has a center
     * and a radius.
     */

    // Constructors -----------------------------------

    public Circle(double cx, double cy, double r)
        /* effects: Creates a Circle at location (cx, cy)
         *     with radius <r>
         */

    public Circle(Point2D.Double center, double r)

        /* effects: Creates a Circle at location <center>
         *     with radius <r>
         */

    // Observers --------------------------------------

    public Point2D.Double getCenter() 

        /* effects: Return the center of this circle
         */

    public double getRadius() 

        /* effects: Return the radius of this circle
         */

    // Mutators --------------------------------------
     
    public void translate(double dx, double dy) 

        /* modifies: this
        /* effects: this_post is the Circle <this> + (dx, dy)
         */
}

public class Vect 
  // Overview: This immutable abstract data type represents the
  // mathematical notion of a vector in 2-space.
  //
  // Abstraction Function: The vector in the cartesian plane with
  // angle to the horizontal equal to theta and length equal to r.
  //
  // Rep. Invariant: r >= 0.0
  //
  // Rep. Rationale: Our Angle rep. can do addition relatively
  // inexpensively and sin() and cos() for free, so this rep. makes it
  // cheap to move back and forth between polar and cartesian
  // coordinates.

  // useful constants
  static public Vect zero = new Vect(Angle.zero, 0.0);
  static public Vect one = new Vect(Angle.zero, 1.0);

  // CREATORS:

  public Vect(Angle a) 
    // effects: initializes this to be a unit vector in the direction of <a>.

  public Vect(Angle a, double l) 
    // effects: initializes this to the vector with angle <a> and length <l>.

  public Vect(double x, double y)
    // effects: initializes this to the vector in Cartesian space with
    // coordinates <x,y>.

  public Vect(double x1, double y1, double x2, double y2) 
    // effects: initializes this to the vector in Cartesian space from
    // the point <x1,y1> to the point <x2,y2>

  // OBSERVERS:

  public Angle angle() 
    // effects: returns the angle of <this> in polar coordinates

  public double length() 
    // effects: returns the length of <this>

  public double x() 
    // effects: returns the horizontal coordinate of <this> in
    // Cartesian coordinates

  public double y() 
    // effects: returns the vertical coordinate of <this> in Cartesian
    // coordinates

  public boolean equals(Vect b) 
  public boolean equals(Object o)
  public String toString() 
  public int hashCode() 

  public double distanceSquared(Vect b) 
    // effects: returns the distance between <this> and <b> 

  // PRODUCERS:
  
  public Vect plus(Vect b) 
    // effects: returns the new vector that is the sum of <this> and &ltb>

  public Vect minus(Vect b) 
    // effects: returns the new vector that is the difference of
    // <this> and <b>

  public Vect rotateBy(Angle a) 
    // effects: returns the new vector that is <this> rotated around
    // the origin by angle <a>.

  public Vect neg() 
    // effects: returns the new vector that is this rotated by Pi radians

  public Vect times(double amt) 
    // effects: returns the new vector with length scaled by <amt>

  public Vect projectOn(Vect b) 
    // effects: returns a new vector, <c>, that represents the
    // projection of <this> onto <b>.  That is, the new vector has the
    // same angle as <b>, but its length will be such that <this> -
    // <c> is perpendicular to <c>.
}

public class Geometry {

  public static class NoRealSolution extends Exception { }

  public static double minQuadraticSolution(double a,
                                            double b,
                                            double c)
       throws NoRealSolution
  
    // effects: Solves the quadratic equation ax^2 + bx + c = 0 and of
    // the two solutions x1 and x2, returns the smaller.  Unless,
    // there is no solution to the equation => throws NoRealSolution.

  static public Vect distanceSquared(Point2D.Double p1, Point2D.Double p2, Point2D.Double point) 

      /* requires: Line from <p1> to <p2> defines a line segment of 
       *     non-zero length.
       *
       * effects: given the line segment <p1>-<p2> and the <point>,
       *     this method transforms the coordinates of <point> into
       *     a vector in the space given by 
       *     <fraction-of-the-way-along-the-line-from <p1> 
       *     to <p2> / perpendicular-distance-from-point-to-line-
       *     (squared)>.
       */


  static public Vect perpendicularPoint(Line2D.Double line,
                                        Vect transform) 

      /* requires: <line> defines a line segment of non-zero
       *     length.
       *
       * effects: given the line segment <line> and a <transform> 
       *     calculated by the distanceSquared() method, returns 
       *     the perpendicular point <x,y> along the line segment.
       */

  public static double timeUntilWallCollision(Line2D.Double line,
                                              Circle ball,
                                              Vect velocity)
    throws CollisionMissException 

      /* requires: <line> defines a line segment of non-zero
       *    length.
       *
       * effects: given a line segment <line> and a moving
       *    <ball> at <velocity>, we return the time (with now being 
       *    time 0) at which the ball and the line segment first collide.
       *  UNLESS <ball> and <line> never collide 
       *    => throws CollisionMissException
       *
       * NOTE: This method calculates only the point at which the ball
       *    will collide with the LINE through the given points.  It does
       *    NOT deal correctly with end-point effects.  To deal with that
       *    problem, you should also calculate timeUntilCollision for two
       *    zero-radius circles at the end-points
       */

  public static Vect reflectWall(Line2D.Double line,
                                 Vect velocity) 

      /* requires: <line> defines a line segment of non-zero length.
       *
       * effects: given a wall at <line> and a ball directly adjacent 
       *    to the wall between the endpoints. Given that the ball 
       *    hits the wall with relative velocity <velocity>, returns 
       *    the new relative velocity of the ball after it bounces 
       * off the wall.
       */

  static public double distanceSquared(Point2D.Double p1, Point2D.Double p2) 

      /* effects: returns the distance between the points <x,y> and <a,b>
       */

  static public double timeUntilCircleCollision(Circle circle,
                                                Circle ball,
                                                Vect velocity)
    throws CollisionMissException

      /* requires: the radii of <circle> and <ball> >= 0.0
       *
       * effects: given a circle <x,y,r1> and a moving ball <a,b,r2>,
       *    with relative velocity given by <va,vb>, we return the time
       *    (with now being time 0) at which the ball and the circle 
       *    first collide.
       *  UNLESS,
       *    <ball> never collides with <circle>,
       *    => throws a CollisionMissException
       */

  public static Vect reflectCircle(Point2D.Double circlePoint,
                                   Point2D.Double ballPoint,
                                   Vect velocity) 

      /* requires: the distance from <x,y> to <a,b> is approximately
       *    equal to the sum of the radii of the circle and ball.
       *
       * effects: given a circle at <x,y> and a ball which is at point
       *    <a,b> and moving with relative velocity <velocity>, returns the
       *    new relative velocity of the ball after it bounces off
       *    the circle (which stays fixed).
       */

  public static Vect rotateAround(Point2D.Double p1, Point2D.Double p2, Angle a) 

      /* effects: returns a new point with <p1> rotated around <p2>
       *    by angle a
       */

  public static double timeUntilRotatingWallCollision(Point2D.Double p1, Point2D.Double p2,
                                                      Point2D.Double center,
                                                      double angularVelocity,
                                                      double startTime,
                                                      Circle ball,
                                                      Vect velocity) 
      throws CollisionMissException, NoCollisionYetException 

        /* requires: <p1>-<p2> defines a line segment of non-zero
         *    length.  In addition, to avoid numerical errors
         *    (angularVelocity * startTime) should be in the 
         *    range 0.0->2 PI.
         *
         * effects: given a line through the line <p1>-<p2>, where
         *    <p2> is rotating around the point <center>.  Assume that the
         *    line has been rotating with angular velocity <angularVelocity>
         *    (given in radians per time unit) for an amount of time given by
         *    <startTime>.  Now, given a moving <ball> with velocity relative
         *    to <center> given by the vector <velocity>.  We return the time
         *    (with now being time 0) at which the ball and the line segment 
         *    first collide.
         *  UNLESS,
         *    Ball doesn't collide with wall ever if they stay with their
         *       current trajectories
         *    => throws CollisionMissException
         *    Ball doesn't collide within the next time unit
         *    => throws NoCollisionYetException.
         *
         * notes: This method calculates only the point at which the <ball>
         *    will collide with the LINE through the given points.  It does
         *    NOT deal correctly with end-point effects.  To deal with that
         *    problem, you should also calculate timeUntilCollision for two
         *    zero-radius circles at the end-points.
         */

  public static Vect reflectRotatingWall(Point2D.Double p1, Point2D.Double p2,
                                         Point2D.Double center,
                                         double angularVelocity,
                                         double rotationTime,
                                         Circle ball, 
                                         Vect velocity) 

        /* requires: <p1>-<p2> defines a line segment of non-zero
         *    length.  In addition, to avoid numerical errors
         *    (angularVelocity * startTime) should be in the 
         *    range 0.0->2 PI.
         *
         * effects: given a line through the line <p1>-<p2>, where
         *    <p2> is rotating around the point <center>.  Assume that the
         *    line has been rotating with angular velocity <angularVelocity>
         *    (given in radians per time unit) for an amount of time given by
         *    <rotationTime>.  Now, given a ball directly adjacent to the wall
         *    between the end-points, travelling with velocity <velocity>
         *    relative to <center>, returns the new relative velocity of the
         *    ball after it bounces off the wall.  Because the wall is also
         *    moving, the ball may have a larger speed after hitting the wall
         *    than it did before.  If the ball does not hit the wall between
         *    the endpoints then this routine does not change the ball
         *    velocity.
         */


  public static double timeUntilRotatingCircleCollision(Circle circle,
                                                        Point2D.Double center,
                                                        double angularVelocity,
                                                        double startTime,
                                                        Circle ball,
                                                        Vect velocity) 
      throws CollisionMissException, NoCollisionYetException              
    
        
        /* requires: the radii >= 0.0.  In addition, to avoid numerical
         *    errors (angularVelocity * startTime) should be in the range
         *    0.0->2 PI.
         *
         * effects: given a <circle>, which is rotating around the
         *    <center>.  Assume that the circle has been rotating with
         *    <angularVelocity> (given in radians per time unit) for an 
         *    amount of time given by <startTime>.  Now, given a moving 
         *    <ball> with <velocity> relative to <center>, we return 
         *    the time (with now being time) at which the <ball> and 
         *    <circle> first collide.  
         *  UNLESS,
         *    <ball> doesn't collide with <circle> ever if they stay with 
         *       their current trajectories
         *    => throws CollisionMissException
         *    <ball> doesn't collide within the next time unit
         *    => throws NoCollisionYetException.
         */

  public static Vect reflectRotatingCircle(Circle circle,
                                           Point2D.Double center,
                                           double angularVelocity,
                                           double rotationTime,
                                           Circle ball,
                                           Vect velocity) 

        /* requires: the distance from center of <circle> to center of
         *    <ball> is approximately equal to the sum of the radii of
         *    the circle and ball.  In addition, to avoid numerical 
         *    errors (angularVelocity * rotationTime) should be in the
         *    range 0.0->2 PI.
         *
         * effects: given a <circle> which is rotating around the point 
         *    <center>.  Assume that the <circle> has been rotating with
         *    angular velocity <angularVelocity> (given in radians per time
         *    unit) for an amount of time given by <rotationTime>.  Now, 
         *    given a <ball> directly adjacent to the <circle> with 
         *    velocity <velocity> relative to <center>, returns the new 
         *    relative velocity of the <ball> after it bounces off the
         *    <circle>.  Because the <circle> is also moving, the <ball> 
         *    may have a larger speed after hitting the <circle> than it
         *    did before.
         */

  public static Vect reflectEqualMass(Point2D.Double center1,
                                      Vect initialVel,
                                      Point2D.Double center2) 

      /* requires: the distance from point <x,y> to point <a,b> is
       *    approximately equal to radius of circle centered at <center1>
       *    + radius of circle centered at <center2>
       * 
       * effects: given two balls of equal mass at <center1> and <center2> 
       *    with relative velocities <initialVel> and <0,0> returns the 
       *    new relative velocities.
       */

}
public  class CollisionMissException extends Exception  {
    public CollisionMissException() 
    public CollisionMissException(String s) 
}

public  class NoCollisionYetException extends Exception { 
    public NoCollisionYetException()
    public NoCollisionYetException(String s) 
}


public  class OverlapException extends Exception { 
    public OverlapException() 
    public OverlapException(String s) 
}
Last modified: 2000-Mar-13 15:06:00 EST by rwpinder