SPRING 2002 CS 1321
EXTRA CREDIT ASSIGNMENT
FRACTALS

Due Friday, April 26th, 6:00pm

EXTRA CREDIT AMOUNT: At most, 200 Homework pts.

Instructions:

Note:

 

GENERAL NOTES:

  0.  This extra is all about fractals!  Most people find the fractals
      assignment to be both very challenging and rewarding - because,
      after all the work, you get to see the drawings being done before
      your very eyes.

      To preview the fractals you will be drawing, check out the
      collection of images at:
      
www.prism.gatech.edu/~gtg121a/images/CompSci/Spirals

www.prism.gatech.edu/~gtg121a/images/CompSci/Dragons

      Each fractal requires a large set of helper functions, so you must
      get started early and split the work up over several sessions.

  1.  This assignment is to be done on an *entirely* individual basis.
      No collaboration whatsoever is permitted, except for assistance
      from TAs on Phase 1 ONLY.  The work you turn in from then on must 
      reflect entirely your individual effort on the fractals.


  2.  This assignment is worth 200 points towards your homework grade.


  3.  PHASE 1 MUST BE COMPLETED TO RECEIVE ANY CREDIT.  Drawing the
      rest of the fractals, however, is entirely up to you.  You will
      receive credit for each portion that you complete fully.

  4.  For the fractals that you choose to turn in: it's all or nothing!
      Either everything is there and it functions and draws correctly
      or - if it doesn't work or the documentation is missing, you get
      no credit for that phase.


ASSIGNMENT NOTES:

  0.  Be sure that you are in Advanced Student mode and turn in a .scm
      file when you are finished.

  1.  All functions (whether local or global) require a Contract and
      Purpose ONLY.  You will also be expected to have test cases 
      that will display each fractal in a separate canvas window 
      (details below).

  2.  DO NOT define the posn structure - it is predefined and the 
      built-in drawing functions do not work with "redefined" posns.

  3.  DO NOT define pi.  The accuracy of the fractal depends highly on
      the significant digits of the built-in pi definition.

  4.  Most of the fractal drawings require some basic knowledge of trig.
      You may find it helpful to review your sines, cosines and radians.

  5.  Keep in mind each fractal may require several functions besides
      ones mentioned directly.  Making good design decisions and writing
      abstractions where necessary will save you some large headaches.

 ======================================================================



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRELIMINARY PHASE - Screen info & drawing exercises ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


Before we dive headfirst into drawing fractals, it is important to get
familiar with how on-screen graphics work.  It's quick and painless,
so pay attention:

                              ----------

(a)  On-screen graphics use a slightly different coordinate system than
     what you are used to.  Computers start counting pixels from the
     UPPER left-hand corner, with the x axis horizontal as usual, but
     the y-axis directed DOWNWARD instead of upward.  This makes things
     slightly confusing - everything is "upside down" vertically, but
     the horizontal directions are the same as always.

(b)  The origin is therefore at the top left of the screen, and a point
     coordinate such as (make-posn 200 100) is actually found by taking
     200 pixels across, and 100 pixels *downward* from that top-left 
     corner:

                         SCREEN:

     origin
     o--------------------------------------->  x-axis
     |(0,0)............(200, 0)
     |                  .
     |                  .
     |                  .
     |             P = (200, 100) - that's 200 across, 100 downwards
     |
     |
     |
     |
     V

     y-axis

    whereas in Cartesian coordinates, you would count 200 across and
    then 100 upwards.  You'll get used to it - don't worry.

(c) Rotations around the origin therefore become *clockwise* for 
    positive angles instead of counterclockwise.  This will become
    apparent later, when you see it drawn on screen.  Just remember,
    if you want to rotate something the regular way, you have to plug
    in a *negative* angle.

                             ----------


Now that you know a little bit about on-screen coordinates, we can start
the fun stuff - drawing pictures!  Finally you'll be able to show your 
mom something cooler than lists and vectors:

(a)  From the Language menu in Dr. Scheme, Add Teachpak:  draw.ss

(b)  Hit Execute!  You should see a teachpak message in the interactions
     window.

(c)  From the Help Menu, pull up the Help Desk and click on the
     "Teachpaks" category.  Read the (very brief) documentation for
     draw.ss to get familiar with all the drawing functions.

     Particularly useful for this project:

     (start x y)
     (draw-solid-line p1 p2 'color)
     (draw-circle ctr rad 'color)

     Note which symbols are legal color symbols, and try drawing a few 
     circles and lines of any colors:

     (begin
       (start 500 500)  ;; <-- a large canvas
       (draw-solid-line (make-posn 0 0) (make-posn 100 300) 'blue)
       (draw-circle (make-posn 200 200) 150 'red))

     What happens when you leave the optional color argument off?

     What gets returned *each time* you draw to the screen?
     (for contracts, later...)

     Experiment a little bit and you'll quickly get the hang of passing
     in coordinates and drawing them where expected.

                               ----------

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PHASE 1 - CONSTANTS AND GEOMETRY - 50 pts ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


In order to set the stage for drawing complex patterns like fractals, 
you must get certain mundane details out of the way:

1.  CONSTANTS:

   (a)  Define two constants - one for the width of the canvas, one for
	the height.  Set them to something fairly large, between 300 and
	500 or so (500x500 is good).  Whenever you use the (start ...)
	command, insert these two constants from now on, instead of
	actual numbers for the dimensions.

   (b)  Define the center of the canvas, using the previous constants.
	The center should be a posn, and it should depend on the 
	dimensions defined above - if the screen constants are changed
	to other values, the center should still be valid, without
	the need for changes to the code.

   (c)  Define a constant for the radius of a large circle, using the 
	two constants from part (a).  The radius will be exactly TWO
	FIFTHS the size of the smallest of the two dimensions: that is,
	either 2/5 of the height or the width, whichever is smallest.

	You may use the function (min ... ) in your definition.

	If the screen constants are changed, this radius should change
	accordingly, without needing any updates - much like the center.

   (d)  Define the top of the circle (a posn that is directly above the
	center of the circle, lying on the circle's radius).  It should
	also completely depend on the previous definitions.


	FOR THIS ASSIGNMENT, YOU MUST INCLUDE ALL TEST CASES SHOWN.
	
	Include the following, replacing the items in BOLD by your
	defined constants, and fill in any valid color symbol:

	;; Test:
	(start WIDTH HEIGHT)
	(draw-solid-disk CENTER RADIUS 'any-color)
	(draw-solid-line CENTER TOP 'any-color)
	

	Make sure these tests work for any arbitrarily sized canvas!
	The circle should not get "cut off" by the edge of the screen.

	                    ----------


In order to run the fractal algorithms, we will need to rotate points 
around other points on the screen:

2. ROTATIONS:

   (a)  Define a function called rotate.  It should take a posn and
	an angle measured in *radians*, and rotate that posn about the
	origin, by that angle.  We will use the old rotation matrix from
	your MatLab assignment - to refresh your memory, here it is:

	Given a posn with coordinates (x,y), the coordinates for the new
	posn (x',y'), rotated through angle theta, are given by:

	x' = x*cos(theta) - y*sin(theta)
	y' = x*sin(theta) + y*cos(theta)

	You will have to make use of Scheme's (sin ...) and (cos ...)
	functions to work this formula.

	Remember to test with angles in radians, such as (* 2 pi) and
	(* 1/2 pi), Scheme's sine and cosine do not take regular 
	degrees!


	Include the following:

	;; Test:
	(rotate (make-posn 1 0) (* pi 2))
	(make-posn 1 0)


	Remember to include all shown test cases.  If you get imprecise
	numbers, prefixed with #i..., that's fine - just estimate where
	exactly they lie.  In most cases 0 will appear as #i...e-017 or
	e-16.


It is not always the case that we want to rotate about the *origin* 
per se.  For most of our work, we will need to have a different point
about which to rotate.

   (b)  Define a function called rotate-about.  It should be called
	in the following manner:

	(rotate-about <center> <point> <angle>)

	It should rotate the given point about the given "center" point,
	instead of about the origin.

	Using a local, you may wish to save the vertical and horizontal
	displacements between the two points as two local constants:

	     dx = Px - Cx
	     dy = Py - Cy

	Rotate those about the origin, and once they are rotated, return
	the posn resulting from those displacements *off the center 
	point!*  Thus the new coordinates x' and y' are given by:

	     x' = Cx + Rx
	     y' = Cy + Ry

	Where R stands for the rotated displacements.


	Include the following:

	;; Test:
	(rotate-about (make-posn 5 5) (make-posn 6 5) pi)
	(make-posn 4 5)


	Think about how this works: (6,5) rotating around (5,5) is just
	like (1,0) rotating about (0,0).  After pi radians, the new posn
	lands on (-1, 0) - exactly 180 degrees away - and then we use
	that to gather the relative distance from (5,5) in the original
	problem.  The rotated point is therefore (5-1, 5+0) or (4,5).
	Sketching this on paper geatly helps to visualize everything.

	Again you may get imprecise numbers - but they should be awfully
	close to 4 and 5, in that test case.

				-----------


3.  MISCELLANEOUS GEOMETRY:

   (a)  The distance formula always comes in handy - so define it!
	Note however: this is not the distance from a point to the 
	origin, this is the distance between *two points*.

	Test carefully - otherwise you'll never be looking for a 
	mistake here later...
	
	Recall: dist[P1P2] = sqrt[ (dx*dx) + (dy*dy)]

	dx and dy are the horizontal and vertical distances, given by:

		dx = p2x - p1x
		dy = p2y - p1y


	Include the following:

	;; Test:
	(dist (make-posn 0 3) (make-posn 4 0))
	5


   (b)  Finally, for each of these fractals we will need to move points
	towards other points, shifting them in a certain direction by
	a certain factor of distance.

	Write a function called shift which will take a posn, and move
	it towards another posn, by some given percent of the distance 
	between them.  In order to find a good formula for this, make 
	up a few simple examples and dissect your method of finding the 
	solution.  The function should operate as follows, shifting p1
	towards p2:

		(shift <p1> <p2> <factor>)

	For instance, if I were to move (10,10) towards (20, 20) by 70%,
	of the distance between them, where would it land?  How did you 
	get that answer?  Write the formula down and then translate it
	into Scheme code.  You may wish to use a local to save the
	horizontal and vertical displacements.  Don't forget to add the
	scaled displacement back to the original coordinates of p1!

	Assume that the percent factor will be given as a decimal.


	Include the following:

	;; Test:
	(shift (make-posn 50 100) 
	       (make-posn 150 200) .40)
	(make-posn 90 140)



	In this case 50 moves towards 150 by 40% of that distance,
	and 100 moves towards 200 by 40% of that distance.

				-----------

4.  TESTING:

In order to get credit for this phase, the following test MUST be
visible on a fresh canvas, upon hitting Execute in your file!

Simply insert your predefined constants in place of the BOLD
variables in the code to run this:


;; Test:
"TESTING PHASE 1:"

(begin
  (start WIDTH HEIGHT)

  ;; green background:
  (draw-solid-disk CENTER RADIUS 'green)

  ;; rotations:
  (do [(i 0 (+ i 1)) ]
     [(> i 32) true]
    (draw-solid-line 
      (shift CENTER (make-posn WIDTH HEIGHT) .90)
      (rotate-about CENTER TOP (* i pi -1/16) )) ))


This should produce something along the lines of:






If that worked, then that's it!  Congrats - you're done with Phase 1,
that's 50 points back on a homework!  Now on to the real fun...






               ---------  FRACTALS  ----------


EACH FRACTAL IS COMPLETELY OPTIONAL - YOU WILL GET CREDIT FOR ANY THAT
ARE COMPLETE, SO LONG AS PHASE 1 IS COMPLETE AS WELL.


NOTE:

    As a word of info, phase 2 is rather lengthy, while phases 3
    and 4 are very closely related to each other - doing one will
    help with completing the other.



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PHASE 2 - POLYGONAL SPIRAL - 50 pts ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


This phase will deal with spiraling any N-sided polygon and forming an
image such as this:






More examples are shown on this page:

www.prism.gatech.edu/~gtg121a/images/CompSci/Spirals


This function will require a lot of patience and a very good grasp 
of geometry.  Ready?

We're all familiar with regular polygons:

  3 sides - triangle
  4 sides - square
  5 sides - pentagon
  6 sides - hexagon
  etc. etc. etc..

Each polygon consists of N sides and N points - edges and vertices.

In Scheme we will represent only the vertices, and we will store them
in a LIST.  Why a list?  So that there is no limit on how many-sided 
the polygon may be.  Remember, each point on the polygon is a posn -
therefore our polygon is really going to be: a list of posns.  

Keep this in mind, and write your contracts accordingly.

Remember those constants we defined in phase 1?  Here is where they
start to come in handy.  *All* of your regular polygons will always fit
precisely into the large circle we defined.  Their points will all lie
on the circle's edge, so it's just a matter of rotating one point around
that edge to define them all.  We will use this to construct a list of 
points that will make up the polygon.

                          ------


1.  MAKING A POLYGON:

In order to make a polygon of any number of sides, you should begin
with a the circle we defined constants for in phase 1.  

All we have to do now is to place N different points on this circle, 
spacing them out evenly - and voila! - there is our polygon.  If you're
not convinced, try drawing a picture.  You can inscribe *any* regular
polygon into a circle (see screenshot below).

In order to space the points out evenly along the circle's edge, we 
will use rotation.  Starting at the topmost point (also a defined 
constant) we will rotate in evenly spaced increments and mark off each 
point until we come around full circle.  We will also include the
first point *again* at the end of the list, in order to close the 
polygon fully - back to front.


(a)  Write a function called make-polygon.  It will take a number, N,
     and construct a list of N+1 posns.  There are really only N 
     distinct points - the reason we will have N+1 is because we will
     attach the initial point both at the beginning AND at the end of
     the list of posns, in order to close the polygon when drawing.

     Begin with the "top" point, defined in phase 1.  Using either
     recursion or iteration, build a list of points given by rotating
     that top point in even increments.  You could make use of "local"
     and define the correct increment as a local constant.  Stop when
     the rotation angle becomes greater than 2*pi, but make sure you
     repeat the initial point once at the end of your list!  

     Remember to use radian measure for angles!  You will have to divide
     the full circle of 2*pi radians into N evenly spaced increments.

     The last point should simply be the "top" point repeated again.
     You will see in part (3) why this is useful.

     Remember each point should be rotated about the CENTER point
     defined as a constant in phase 1.  Do not just rotate about the 
     origin.

     If the user asks for a 0 sided polygon or less, you must return 
     an empty list.

     ;; Test carefully!
     (make-polygon 5)    ;; <-- a pentagon
     (list
       (make-posn #i249.99999999999994 #i50.0)  ;; <-- approx (250, 50)
       (make-posn #i59.78869674096927 #i188.19660112501055)
       (make-posn #i132.4429495415054 #i411.8033988749895)
       (make-posn #i367.55705045849464 #i411.80339887498945)
       (make-posn #i440.21130325903073 #i188.19660112501052)
       (make-posn 250 50))      ;; <-- notice this one is duplicated!


     This test case is for a 500x500 canvas - yours may differ slightly
     if you choose different dimensions for your screen.

     Note:  the point (250, 50) is the "top" point and appears twice,
	    both at the beginning and at the very end of the list.

     Note that those posns MUST form a regular pentagon.  The easiest
     way to check that is to write the function in part (2) and draw
     your result to the canvas.

				-------


2.  DRAWING A POLYGON:

Reading a list of points is not that exciting - the real way to test 
your polygons is to actually draw them and see the shapes for
yourself!  Here is where you'll need the drawing function:

(a)  Given a polygon (which is a list of posns), write a function that
     connects each pair of posns with a line.  Call it draw-polygon.

     That duplicate point at the end of the list is handy - we have to 
     connect *all* lines, including the line that connects the last
     point right back to the first one - closing the shape.

     Since your polygon will have the duplicate point at the end of it,
     all you have to do is write a function that connects points until
     they run out.  That final repeated point will automatically close
     the polygon.

     Your function should *not* start a canvas, it should only draw.
     We will be using this over and over again, and we want to always
     draw on the same canvas, not open new ones each time.

     Since you need at least a *pair* of posns in the list in order to
     draw lines, you will need to terminate your recursion or iteration
     when the list no longer contains at least TWO posns.  NOT just when
     the list is empty!

     When you are drawing lines, you may find it helpful to extract both
     the (first ...) of the polygon list, and the (second ...).  You may
     also find it helpful to use a begin statement, to perform more than
     one procedure in a row.

     Given an empty list, or a list with only one posn - you must return
     true, without drawing anything.

     Given a one or two sided polygon, your function will draw either a
     point or a line, if you coded it correctly.  (Even though there is
     technically no such thing as a one-sided or two-sided polygon...)

     Include the following, so that it comes up on a fresh canvas:

     ;; Test:
     (begin
	(start WIDTH HEIGHT)
	(draw-solid-disk CENTER RADIUS 'green)
	(draw-polygon (make-polygon 5) ))


     This should result in:




                              -----------


3.  SHIFTING A POLYGON:

In order to get the spiraling effect, we will need to apply the "shift"
function to each point in the polygon, moving them towards each other
along the sides, by a certain distance.  (see the screnshot below)

(a)  Write a function called shift-polygon which will move each posn
     in the given list (the given polygon) towards the next point, by
     a certain factor:

     (shift-polygon <polygon> <factor>)

     The "factor" is to be used in the "shift" function, to slide each
     point towards the next point by that percentage of the distance.

     Assume that you will be given a decimal percent, .75 for instance,
     for the shifting factor.  Simply pass that given factor into your 
     shift function.

     If the given list has at least two points in it, you'll need to use
     recursion or iteration to go through the list and shift each point
     towards the next point.   You may find it helpful to extract both
     the (first ...) point and the (second ...) point in the list each
     time.

     If the given list is empty, or if the given list only contains ONE
     point, you must return the given list, without any changes.

     BIG HINT:

     Shift the first point towards the second, and save that as a local
     constant before beginning your recursion or iteration.

     At the end of the process, attach that initial point to the very 
     end of the list in order to close the polygon.  Remember our 
     polygon lists will need to contain the first point duplicated at
     the end, in order to be drawn properly.  If you simply run this 
     function without attaching the first shifted point at the end, 
     then you will find that your polygon will not be closed - it will
     be missing a side.

     
(b)  Include the following, to be displayed on a new canvas:

     ;; Test:
     (begin
       (start WIDTH HEIGHT)
       (draw-solid-disk CENTER RADIUS 'green)
  
	;; Normal pentagon:
	(draw-polygon (make-polygon 5))
  
	;; Shifting the pentagon:
	(draw-polygon (shift-polygon (make-polygon 5) .20))
  
	;; Shifting twice:
	(draw-polygon 
	  (shift-polygon (shift-polygon (make-polygon 5) .20) .20) ))

;; should result in:








				------------

4. SPIRALING A POLYGON:

Now you are ready to define the main spiraling function.  You are going
to repeatedly shift the polygon by a certain factor, until it spirals
in on itself (nifty animation there).  When the points on the polygon
become too close - within 3 to 5 pixels - you may terminate the process.

(a)  Write a function called spiral-polygon which will take a number N,
     for the number of sides, and a factor F, for the spiraling shift.


     Given N for the sides, and a factor F, it should do the following:

     1.  Start a canvas of the predefined dimensions (from phase 1)
     2.  Draw a circle (not a disk) of the predefined radius, and of
	 any color you like.
     3.  Make and draw a polygon of N sides ONLY IF N<3.
     4.  Otherwise make a polygon of N sides and pass this polygon 
	 and the spiraling factor to a helper.


(b)  Your helper function should do the following:

     1.  Terminate and return true if the points in the polygon are
	 within a very small distance of each other (2 to 5 is good).
     2.  Otherwise draw the current polygon, and then use recursion
	 on the polygon after it is *shifted by the given factor*.

     You will see that running a very small factor (less than 10%) 
     produces the best results for your test cases.


(c)  Include the following screenshots:

     ;; Tests:
     (spiral-polygon 3 .05)    ;; triangle
     (spiral-polygon 4 .05)    ;; square
     (spiral-polygon 5 .05)    ;; pentagon
     (spiral-polygon 6 .05)    ;; hexagon
     (spiral-polygon 10 .05)   ;; decagon




     This code must work for any N, not just the above four. It should 
     not cause errors when the given N is less than 3.

     Again, images of each of these examples can be found at:

www.prism.gatech.edu/~gtg121a/images/CompSci/Spirals


     You may wish to comment these out when working on Phases 3-4, but
     don't forget to un-comment them when turning your file in!

				-------------



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PHASE 3 - CURLY FRACTAL - 50 pts. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Using a good combination of local and begin you should be able to
outline this fairly complex process.  Here is how to generate a curly
fractal (see screenshots below) using recursion:


1.  Begin with two points, A and B and draw a line between them:


    


2.  Find a point C, that is exactly 1/2 * sqrt(2) times the distance
    AB from point A, along the segment AB.  (You have a helper coded!)

3.  Rotate that point C about point A by an angle of -1/4 * pi radians.
    (That is -45 degrees).  This is going to be the "new" point B.
    Note that this is a counterclockwise rotation, on-screen.  Reread
    the preliminary discussion for more info.

4.  Use recursion on point A and the "new" point B.  Make sure to note
    very carefully the order of those two parameters!




    




5.  Now back from the original segment AB, find a point D that is 
    exactly 1/2 * sqrt(2) times the distance AB from POINT B, along 
    the segment AB.  (Again, using your helper makes this trivial.)

6.  Now rotate point D about point B by an angle of +1/4 * pi radians.
    (That is +45 degrees).  This is going to be the "new" point A.
    Note that this is a clockwise rotation - exactly opposite that of
    point C.

7.  Use recursion on the "new" point A and the original point B.  Note
    again, carefully, the order of those two parameters!


    



The next few sequences are shown, supposing you will simply use
recursion on each segment that is generated:



    


    

    
    



Your task:  Write a function called curl, which will take two posns
	    A and B, and a number signifying the amount of iterations.

	    The "amount of iterations" signifies how many times the 
	    algorithm should be repeated.  The triangles get smaller
	    and smaller as the process goes on, so after about 15 or so
	    the details of the fractal are fully rendered.

	    Your function should start a canvas using the predefined
	    dimensions, and then perform the above algorithm, generating
	    the fractal.  Once the process has repeated the specified
	    number of times, the recursion should stop.

	    In order to keep track of the number of recursions, you may
	    wish to have a helper function that keeps track of a
	    "counter" parameter and terminates when that counter reaches
	    the originally given limit.  At that point, simply return
	    true.

    The function should be called as follows:

    (curl <p1> <p2> <number of iterations>)

    In this call p1 and p2 are posns that will represent the original
    starting points A and B for the fractal.    

    Include a test case that will draw a good picture of the fractal
    when Execute is hit.  Something like 14 iterations should produce:



    


    This is given by (curl (make-posn 100 200) (make-posn 300 400) 14)
    on a 500x500 screen.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PHASE 4 - DRAGON FRACTAL - 25 pts. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


Ever read the old paperback "Jurassic Park"?  If you did, you might even
remember the "dragon" fractal shown in various stages of growth between 
each chapter heading:




       





In this phase you will be generating it!  The algorithm is very closely
related to that of the curly fractal, except for some tiny, but crucial
changes.  Read through the instructions for phase 3 to get familiar with
this.  For the dragon fractal, the process is as follows:

1.  Begin with two points A and B.  Draw a line between them.

    
    

2.  Locate point C on line AB, which is 1/2 * sqrt(2) times the 
    distance AB away from point A, along the segment AB.

3.  Rotate point C about point A by an angle of 1/4 * pi radians.  This
    will become the "new" point B.  Note that this is a clockwise 
    rotation, on-screen.  Reread the preliminary discussion for more 
    info.

4.  Use recursion on point A and the "new" point B.  Make sure to note
    very carefully again, the order of those two parameters!




    




5.  Now back from the original segment AB, find a point D that is 
    exactly 1/2 * sqrt(2) times the distance AB from POINT B, along 
    the segment AB.

6.  Rotate point D about point B by an angle of -1/4 * pi radians.
    Note that this is a counterlockwise rotation - exactly opposite 
    that of point C.  Believe it or not this will also become the 
    "new point B" for your recursive call!  Subtle, but very different 
    from the curly fractal.

7.  Use recursion on the original point B and the "new" point B.  Note
    again, carefully, the order of those two parameters!


    



The next two sequences are shown, supposing you will simply use
recursion on each segment that is generated:


    

    


The remaining iterations (all labeled Itr_X.jpg) can be seen at:


www.prism.gatech.edu/~gtg121a/images/CompSci/Dragons

Your task: Write a function called dragon, which will take two posns A and B, and a number signifying the amount of iterations. The "amount of iterations" signifies how many times the algorithm should be repeated. The triangles get smaller and smaller as the process goes on, so after about 15 or so the details of the fractal are fully rendered. Your function should start a canvas using the predefined dimensions, and then perform the above algorithm, generating the fractal. Once the process has repeated the specified number of times, the recursion should stop. In order to keep track of the number of recursions, you may wish to have a helper function that keeps track of a "counter" parameter and terminates when that counter reaches the originally given limit. However, if you simply leave it at that, you will notice your rendering of this fractal contains many unslightly "construction" lines. It is not a clean curve as in the screenshot below. You will have to deviate slightly from the given algorithm... Your task: Devise a way to eliminate the drawing of those intermediate lines, and to only draw the final curve. Where should you give the (draw-solid-line ...) command? Your function should be called as follows: (dragon <p1> <p2> <number of iterations>) Here p1 and p2 are posns representing the original A and B used as the starting points for your fractal. Include a test case that will draw a good picture of the fractal when Execute is hit. Something like 12 iterations should generate the example shown at the top of this section. Fewer iterations will generate a less detailed rendering: --------- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; MULTI-COLOR BONUS - 25 pts. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Adding at least two colors to each of your fractals will earn you an extra 25 pts. (No, black and white do not count as extra colors!) Colors and variations can make the fractals look quite intriguing: -------- END NOTE: Before you turn the assignment in, check carefully to make sure that you have included Contract & Purpose for EVERY function that you have written. NO CREDIT is given for functions without documentation! Make sure that when you hit Execute, the required screenshots on this page all come up, and the other required test cases are also printed. Do not leave any of them commented out! Don't forget to show your mom! --------