You are not required to use the following classes, they are provided because we don't want you focusing on the mathematical aspects of the program.

The following is assumed by the classes below.

An LRStruct filled with java.awt.geom.Point2d.Double's objects. This is the "prototype list" and defines the pattern which will be replicated onto every straight line when the Koch curve is told to "grow". These points are defined on a "unit" interval from (0.0,0.0) to (1.0,0.0) and are the "vertices" that join the line segments together. The endpoints are assumed and are not included in the list of points.

- All factories have two methods, one to create a Koch curve in its base case and the other to create a Koch curve in its inductive case. Both methods have the same inputs: the two end points of the Koch curve.
See the documentation for the following classes: (regular Point version)

If your Koch curve object uses Point2D.Doubles for the two endpoints, download this version: codeDouble.zip

If your Koch curve object uses Points for the two endpoints, download this version: codeInt.zip

## MakeManyAlgo

The MakeManyAlgo The Koch system uses an abstract factory ability to manufacture a base case curve to create the first instance of the Koch curve and also to reset the Koch curve. It's ability to create the non-base case Koch curve is used in the "growing" process -- the base case uses it to generate and install the inductive case. To generate the non-base case Koch curve, the a factory executes a MakeManyAlgo algorithm on the list (LRStruct) of prototype points to create a LRStruct filled with base-case Koch curves. The MakeManyAlgo recursively uses a mathematical process called an Affine transformation.

MakeManyAlgo assumes the Koch curve class is called "Koch". If your corresponding class has a different name, go into the code of MakeManyAlgo and change "Koch" to the name you are using.

## Affine transform

An Affine transformation is a technique that enables one to take points defined in terms of one coordinate system (here, the prototype points defined on the (0,0)-(1,0) interval) and rotate, scale and translate them so that they appear in a different coordinate system (here, the beginning and end points of the Koch curve being drawn at the moment). That is, (0,0) will become the first point of the Koch curve ("a") and (1,0) will become the last point ("b") and all the in-between points will be in-between a and b, and enlarged and angled to correspond to the size and angle determined by the distance between points a and b. The Affine transformation process needs two things: the previous point processed (since a Koch curve is defined over an interval between two points) and an AffineXForm object. The AffineXForm object is capable of executing all the mathematics needed for the transformation. The AffineXForm object needs to know the interval to which it is transforming the prototype data, hence it is handed those two Points at construction time.

## Affine data

The AffineData class is what is called a "wrapper" class (another Adapter pattern technically) and "wraps" the two necessary things (the AffineXForm object and the previous Point into a single object that can be passed as the input to the algorithm. The inductive case method makes an AffineData object using the endpoints it is given. The AffineData's constructor uses those endpoints to first make the AffineXForm object. Its "previous point" is initialized to (0,0). The MakeManyAlgo transforms the previous point and the current prototype point it is working on. Using the transformed points, it then instantiates a base case Koch object. The MakeManyAlgo then recurs on to the next prototype point. When the end of the prototype list is encountered, the point (1, 0) is used as the current point and the algorithm is done. The key issue here is that the Affine transform plus the MakeManyAlgo allows us to define a prototype growth pattern independent from sizes, positions and angles of its actual applications during the growing process.

## Calculating the height of an equilateral triangle

Given a base dimension of base, the height is base*Math.sqrt(3.0)/2.0.

## Calculating the 3rd point in a triangle

Given two arbitratry points, A and B, what is the point C that forms an equilateral triangle with A and B?

Remember that given V = (Vx, Vy) then (-Vy, Vx) is perpendicular to it. Likewise, (Vy, -Vx) is perpendicular to it.

Recalling your Euclidian geometry, Point C is thus halfway from A to B, and sqrt(3)/2 times that distance in the perpendicular to the direction from A to B.

To calculate this, first define the vector from A to B :

V = (Bx-Ax, By-Ay).Then C is simply:

(Cx, Cy) = (Ax, Ay) + (1/2) (Vx, Vy) + (sqrt(3)/2)(-Vy, Vx)(Using the other form for the perpendicular direction will put C on the other side of the A-B line.)

In Java:

int vx = b.x - a.x;

int vy = b.y - a.y;

Point c = new Point( a.x + vx/2 - (int)((Math.sqrt(3.0)/2.0)*vy),

a.y + vy/2 + (int)((Math.sqrt(3.0)/2.0)*vx));Note the casting of doubles to ints because Math.sqrt() returns a double. Note the breaking of the code into two lines to make it easier to read.

Tech note: sqrt() is a "static" method of the Math class, so that's why we can call the method directly on the class without having a Math object first.

## Calculating the other 2 points of a rectangle:

Given two arbitrary points, A and B, what are the remaining 2 points that form a rectangle?

Rectangles are easy. All you need to do is to go in perpendicular direction from each point the same distance, which is determined by teh ratio of the length and width.

That is, let V be the perpendicular line segment (vector) to A-B, scaled by the length to width ratio:

V = (-(By-Ay), (Bx-Ax)) * ratioThen the remaining points, C and D (going clockwise: A->B->C->D->A) are simply the vector V added to the points B and A respectively:

C = B + V and D = A + V

In Java:

double ratio = [whatever you want];

int vx = -(int)((b.y - a.y) * ratio);

int vy = (int)((b.x - a.x)*ratio);

Point c = new Point( b.x +vx, b.y+vy);

Point d = new Point( a.x + vx, a.y+vy);Likewise, if the opposite signs for vx and vy are used, the rectangle will be on the opposite side of the A-B line. Note also the casting done because ratio may be less than 1 and thus needs to be a double not an int.

Since the view cannot directly access any class in the model, it must send a "message" to the model asking for a class to be loaded when the user wants to change what Koch factory is being used. For simplicity's sake, let assume that a drop-list exists in the view that holds the fully qualified class name of the desired factory (i.e. includes the package). When the user selects a factory from the drop-list, the string which holds the classname is sent to the model.

In the model, the appropriate class corresponding to the string must be loaded and the appropriate constructor called. This is accomplished using Java's dynamic class loading capability plus its "reflection" capability. "Reflection" is the ability to look a the structure of a class at run time, in this case, to find out how many input parameters are required for the constructor.

Below is a method that changes the current factory being used, given a String containing the name of the requested file:

// Assume the existence of "aFactory", the current factory in use. public void changeFactoryTo(String s) { try { // get the first constructor of class "s": java.lang.reflect.Constructor c = Class.forName(s).getConstructors()[0]; // get an array of arrays of input parameters. Here, arrays for zero and one input parameter: Object [][] args = new Object[][]{new Object[]{}, new Object[]{aFactory}}; // create a new instance given the number of input parameters for constructor: aFactory = (AFactory) c.newInstance(args[c.getParameterTypes().length]); } catch(Exception ex) { System.out.println(ex); } }

- It may be tempting to model the Koch curve as an LRStruct of pairs of points. What will you need to do if you are asked later to enhance your Koch curve with the capability to "ungrow"? That is to revert back to its previous state?
- Your Koch curve object may need to have two constructors: one that takes a pair of Points and the other that takes in a list of Koch curves. Why might this be necessary?
- Build and test in SMALL pieces! This program may generate close to 600,000 objects when it runs--you don't want to be dealing with that kind of complexity if it is broken. KEEP IT SIMPLE!!
- Be sure that your factories and policies don't violate encapsulations. Why might these factories be best off in their own package?
- Don't use a design pattern or piece of code because you feel you "are have to" or worse, because "you are supposed to". If your abstraction matches a design pattern, fine, then use it. Just don't drive your code design by the desire to use something. Look for solutions that best express what you see.
- The factories can only return Koch curve objects and not their states for encapsulation reasons (do you believe this?). However, you may find that you need some package private information from the factory's return value. Remember that some of the components of the Koch system have the access capabilities you seek. Let the objects with the appropriate access privileges do the work you need.
- Visitors aren't always the answer. Don't use them if the implied separation of variant and invariant isn't warranted. Remember that you can encapsulate the use of a visitor so that the outside world doesn't realize that they are being used.
- Don't replicate code! Use subclassing and composition carefully, wisely and effectively.
- Post questions to the newsgroup so that additional hints and thoughts can be posted!
- DO NOT PROCRASTINATE!!! START RIGHT AWAY. STARTING LATE WILL RESULT IN DISMAL FAILURE.