Fractals

``[G]eometry can be extended to account for objects with a fractional dimension. Such objects, known as <#34481#>fractals<#34481#>, come very close to capturing the richness and variety of forms found in nature. Fractals possess structural self-similarity on multiple ...\ scales, meaning that a piece of a fractal will often look like the whole'' (Flake, <#34482#>The Computational Beauty of Nature<#34482#>, The MIT Press 1998.) Here is an example of a fractal, known as the Sierpinski triangle: rawhtml46 The basic shape is an (equilateral) triangle, as shown in the leftmost picture. In the rightmost example we see that the triangle is repated many times and in many different sizes inside of the outermost triangle. The picture in the middle is a snapshot from the middle of the drawing process. The middle picture also suggests what the generative step might look like. Given the three endpoints of a triangle, we draw the triangle and then compute the midpoint of each side. If we were to connect these midpoints to each other, we would divide the given triangle into four triangles. The middle picture illustrates this idea. The Sierpinski triangle is the result of repeating the process for the three outer triangles and leaving the inner one alone. A function that draws this nest of triangles must mirror this process. Its input data must represent the triangle that we start with. The process stops when the input data specifies a triangle that is too small to be drawn. Since all of our drawing functions produce <#66413#><#34484#>true<#34484#><#66413#> when they are done, we agree that our Sierpinski function should also produce <#66414#><#34485#>true<#34485#><#66414#>. If the given triangle is still large enough, the function must draw the triangle and possibly some nested ones. The trick is to translate the partitioning of the triangle into Scheme. Let us summarize our discussion with a skeletal Scheme definition:
<#71481#>;; <#66415#><#34490#>sierpinski<#34490#> <#34491#>:<#34491#> <#34492#>posn<#34492#> <#34493#>posn<#34493#> <#34494#>posn<#34494#> <#34495#><#34495#><#34496#>-;SPMgt;<#34496#><#34497#><#34497#> <#34498#>true<#34498#><#66415#><#71481#>
<#71482#>;; to draw a Sierpinski triangle down at <#66416#><#34499#>a<#34499#><#66416#>, <#66417#><#34500#>b<#34500#><#66417#>, and <#66418#><#34501#>c<#34501#><#66418#>, <#71482#> 
<#34502#>;; assuming it is large enough<#34502#> 
<#34503#>(d<#34503#><#34504#>efine<#34504#> <#34505#>(sierpinski<#34505#> <#34506#>a<#34506#> <#34507#>b<#34507#> <#34508#>c)<#34508#> 
  <#34509#>(c<#34509#><#34510#>ond<#34510#> 
    <#34511#>[<#34511#><#34512#>(too-small?<#34512#> <#34513#>a<#34513#> <#34514#>b<#34514#> <#34515#>c)<#34515#> <#34516#>true<#34516#><#34517#>]<#34517#> 
    <#34518#>[<#34518#><#34519#>else<#34519#> <#34520#>...<#34520#> <#34521#>(draw-triangle<#34521#> <#34522#>a<#34522#> <#34523#>b<#34523#> <#34524#>c)<#34524#> <#34525#>...<#34525#> <#34526#>]<#34526#><#34527#>))<#34527#> 
The function consumes three <#66419#><#34531#>posn<#34531#><#66419#> structures and returns <#66420#><#34532#>true<#34532#><#66420#> when it is done. The <#66421#><#34533#>cond<#34533#>-expression<#66421#> reflects the general outline of an algorithm. It is our task to define <#66422#><#34534#>too-small?<#34534#><#66422#>, the function that determines whether the problem is trivially solvable, and <#66423#><#34535#>draw-triangle<#34535#><#66423#>. In addition, we must still add a Scheme expression that formulates the partitioning of the triangle. The partitioning step requires the function to determine the three mid-points between the three end-points. Let us call these new mid-points <#66424#><#34536#>a-b<#34536#><#66424#>, <#66425#><#34537#>b-c<#34537#><#66425#>, and <#66426#><#34538#>c-a<#34538#><#66426#>. Together with the given endpoints, <#66427#><#34539#>a<#34539#><#66427#>, <#66428#><#34540#>b<#34540#><#66428#>, and <#66429#><#34541#>c<#34541#><#66429#>, they determine four triangles: <#66430#><#34542#>a,<#34542#>\ <#34543#>a-b,<#34543#>\ <#34544#>c-a<#34544#><#66430#>; <#66431#><#34545#>b,<#34545#>\ <#34546#>a-b,<#34546#>\ <#34547#>b-c<#34547#><#66431#>; <#66432#><#34548#>c,<#34548#>\ <#34549#>c-a,<#34549#>\ <#34550#>b-c<#34550#><#66432#>; <#66433#><#34551#>a-b,<#34551#>\ <#34552#>b-c,<#34552#>\ <#34553#>c-a<#34553#><#66433#>. Thus, if we wanted to create the Sierpinski triangle for, say, the first listed triangle, we would use <#66434#><#34554#>(sierpinski<#34554#>\ <#34555#>a<#34555#>\ <#34556#>a-b<#34556#>\ <#34557#>c-a)<#34557#><#66434#>. Since each midpoint is used twice, we use a <#66435#><#34558#>local<#34558#>-expression<#66435#> to translate the generative step into Scheme. The <#66436#><#34559#>local<#34559#>-expression<#66436#> introduces the three new midpoints. Its body contains three recursive applications of <#66437#><#34560#>sierpinski<#34560#><#66437#> and the <#66438#><#34561#>draw-triangle<#34561#><#66438#> application mentioned earlier. To combine the solutions of the three problems, we use an <#66439#><#34562#>and<#34562#>-expression<#66439#>, which ensures that all three recursions must succeed. Figure~#figsierpinskicode#34563> collects all the relevant definitions, including two small functions based on domain knowledge from geometry.
<#71483#>;; <#66440#><#34568#>sierpinski<#34568#> <#34569#>:<#34569#> <#34570#>posn<#34570#> <#34571#>posn<#34571#> <#34572#>posn<#34572#> <#34573#><#34573#><#34574#>-;SPMgt;<#34574#><#34575#><#34575#> <#34576#>true<#34576#><#66440#><#71483#>
<#71484#>;; to draw a Sierpinski triangle down at <#66441#><#34577#>a<#34577#><#66441#>, <#66442#><#34578#>b<#34578#><#66442#>, and <#66443#><#34579#>c<#34579#><#66443#>,<#71484#> 
<#34580#>;; assuming it is large enough<#34580#> 
<#34581#>(d<#34581#><#34582#>efine<#34582#> <#34583#>(sierpinski<#34583#> <#34584#>a<#34584#> <#34585#>b<#34585#> <#34586#>c)<#34586#> 
  <#34587#>(c<#34587#><#34588#>ond<#34588#> 
    <#34589#>[<#34589#><#34590#>(too-small?<#34590#> <#34591#>a<#34591#> <#34592#>b<#34592#> <#34593#>c)<#34593#> <#34594#>true<#34594#><#34595#>]<#34595#> 
    <#34596#>[<#34596#><#34597#>e<#34597#><#34598#>lse<#34598#> 
      <#34599#>(l<#34599#><#34600#>ocal<#34600#> <#34601#>(<#34601#><#34602#>(define<#34602#> <#34603#>a-b<#34603#> <#34604#>(mid-point<#34604#> <#34605#>a<#34605#> <#34606#>b))<#34606#> 
              <#34607#>(define<#34607#> <#34608#>b-c<#34608#> <#34609#>(mid-point<#34609#> <#34610#>b<#34610#> <#34611#>c))<#34611#> 
              <#34612#>(define<#34612#> <#34613#>c-a<#34613#> <#34614#>(mid-point<#34614#> <#34615#>a<#34615#> <#34616#>c)))<#34616#> 
        <#34617#>(a<#34617#><#34618#>nd<#34618#> 
          <#34619#>(draw-triangle<#34619#> <#34620#>a<#34620#> <#34621#>b<#34621#> <#34622#>c)<#34622#>     
          <#34623#>(sierpinski<#34623#> <#34624#>a<#34624#> <#34625#>a-b<#34625#> <#34626#>c-a)<#34626#> 
          <#34627#>(sierpinski<#34627#> <#34628#>b<#34628#> <#34629#>a-b<#34629#> <#34630#>b-c)<#34630#> 
          <#34631#>(sierpinski<#34631#> <#34632#>c<#34632#> <#34633#>c-a<#34633#> <#34634#>b-c)))]<#34634#><#34635#>))<#34635#> 
<#71485#>;; <#66444#><#34636#>mid-point<#34636#> <#34637#>:<#34637#> <#34638#>posn<#34638#> <#34639#>posn<#34639#> <#34640#><#34640#><#34641#>-;SPMgt;<#34641#><#34642#><#34642#> <#34643#>posn<#34643#><#66444#><#71485#> 
<#71486#>;; to compute the mid-point between <#66445#><#34644#>a-posn<#34644#><#66445#> and <#66446#><#34645#>b-posn<#34645#><#66446#><#71486#> 
<#34646#>(d<#34646#><#34647#>efine<#34647#> <#34648#>(mid-point<#34648#> <#34649#>a-posn<#34649#> <#34650#>b-posn)<#34650#> 
  <#34651#>(m<#34651#><#34652#>ake-posn<#34652#> 
    <#34653#>(mid<#34653#> <#34654#>(posn-x<#34654#> <#34655#>a-posn)<#34655#> <#34656#>(posn-x<#34656#> <#34657#>b-posn))<#34657#> 
    <#34658#>(mid<#34658#> <#34659#>(posn-y<#34659#> <#34660#>a-posn)<#34660#> <#34661#>(posn-y<#34661#> <#34662#>b-posn))))<#34662#> 
<#71487#>;; <#66447#><#34663#>mid<#34663#> <#34664#>:<#34664#> <#34665#>number<#34665#> <#34666#>number<#34666#> <#34667#><#34667#><#34668#>-;SPMgt;<#34668#><#34669#><#34669#> <#34670#>number<#34670#><#66447#><#71487#> 
<#71488#>;; to compute the average of <#66448#><#34671#>x<#34671#><#66448#> and <#66449#><#34672#>y<#34672#><#66449#><#71488#> 
<#34673#>(d<#34673#><#34674#>efine<#34674#> <#34675#>(mid<#34675#> <#34676#>x<#34676#> <#34677#>y)<#34677#> 
  <#34678#>(/<#34678#> <#34679#>(+<#34679#> <#34680#>x<#34680#> <#34681#>y)<#34681#> <#34682#>2))<#34682#> 
<#34686#>Figure: The Sierpinski algorithm<#34686#>
Since <#66450#><#34688#>sierpinski<#34688#><#66450#> is based on generative recursion, collecting the code and testing it is not the last step. We must also consider why the algorithm terminates for any given legal input. The inputs of <#66451#><#34689#>sierpinski<#34689#><#66451#> are three positions. The algorithm terminates if the corresponding triangle is too small. But, each recursive step subdivides the triangle so that the sum of its sides is only half of the given triangle. Hence, the size of the triangles indeed decrease and <#66452#><#34690#>sierpinski<#34690#><#66452#> is bound to produce <#66453#><#34691#>true<#34691#><#66453#>.
<#34694#>Exercise 27.1.1<#34694#> Develop the functions
  1. ;; <#66454#><#34697#>draw-triangle<#34697#>\ <#34698#>:<#34698#>\ <#34699#>posn<#34699#>\ <#34700#>posn<#34700#>\ <#34701#>posn<#34701#>\ <#34702#><#34702#><#34703#>-;SPMgt;<#34703#><#34704#><#34704#>\ <#34705#>true<#34705#><#66454#>
  2. ;; <#66455#><#34706#>too-small?<#34706#>\ <#34707#>:<#34707#>\ <#34708#>posn<#34708#>\ <#34709#>posn<#34709#>\ <#34710#>posn<#34710#>\ <#34711#><#34711#><#34712#>-;SPMgt;<#34712#><#34713#><#34713#>\ <#34714#>bool<#34714#><#66455#>
to complete the definitions in figure~#figsierpinskicode#34716>. Use the teachpack <#34717#>draw.ss<#34717#> to test the code. For a first test of the complete function, use the following definitions:
<#34722#>(define<#34722#> <#34723#>A<#34723#> <#34724#>(make-posn<#34724#> <#34725#>200<#34725#> <#34726#>0))<#34726#>
<#34727#>(define<#34727#> <#34728#>B<#34728#> <#34729#>(make-posn<#34729#> <#34730#>27<#34730#> <#34731#>300))<#34731#> 
<#34732#>(define<#34732#> <#34733#>C<#34733#> <#34734#>(make-posn<#34734#> <#34735#>373<#34735#> <#34736#>300)<#34736#> 
Create a canvas with <#66456#><#34740#>(start<#34740#>\ <#34741#>400<#34741#>\ <#34742#>400)<#34742#><#66456#>. Experiment with other end points and canvas dimensions. external Solution<#66457#><#66457#> <#34748#>Exercise 27.1.2<#34748#> external ~<#71489#>domain knowledge -- geometry -- esp some knowledge about <#66458#><#34751#>sin<#34751#><#66458#> and <#66459#><#34752#>cos<#34752#><#66459#> are helpful -- provide if necessary<#71489#> The process of drawing a Sierpinski triangle usually starts from an equilateral shape. To compute the endpoints of an equilateral Sierpinski triangle, we can pick a large circle and three points on the circle that are 120 degrees apart. For example, they could be at 0, 120, 240:
<#34757#>(define<#34757#> <#34758#>CENTER<#34758#> <#34759#>(make-posn<#34759#> <#34760#>200<#34760#> <#34761#>200))<#34761#>
<#34762#>(define<#34762#> <#34763#>RADIUS<#34763#> <#34764#>200)<#34764#> 
<#71490#>;; <#66460#><#34765#>cicrcl-pt<#34765#> <#34766#>:<#34766#> <#34767#>number<#34767#> <#34768#><#34768#><#34769#>-;SPMgt;<#34769#><#34770#><#34770#> <#34771#>posn<#34771#><#66460#><#71490#> 
<#71491#>;; to compute a position on the circle with <#66461#><#34772#>CENTER<#34772#><#66461#><#71491#> 
<#71492#>;; and <#66462#><#34773#>RADIUS<#34773#><#66462#> as defined above <#71492#> 
<#34774#>(define<#34774#> <#34775#>(circle-pt<#34775#> <#34776#>factor)<#34776#> <#34777#>...)<#34777#> 
<#34778#>(define<#34778#> <#34779#>A<#34779#> <#34780#>(circle-pt<#34780#> <#34781#>120/360))<#34781#> 
<#34782#>(define<#34782#> <#34783#>B<#34783#> <#34784#>(circle-pt<#34784#> <#34785#>240/360))<#34785#> 
<#34786#>(define<#34786#> <#34787#>C<#34787#> <#34788#>(circle-pt<#34788#> <#34789#>360/360))<#34789#> 
Develop the function <#66463#><#34793#>circle-pt<#34793#><#66463#>. <#34794#>Hints:<#34794#> Recall that DrScheme's <#66464#><#34795#>sin<#34795#><#66464#> and <#66465#><#34796#>cos<#34796#><#66465#> compute the sine and cosine in terms of radians, not degrees. Also keep in mind that on-screen positions grow downwards not upwards.~ external Solution<#66466#><#66466#> <#34802#>Exercise 27.1.3<#34802#> Rewrite the function in figure~#figsierpinskicode#34804> to use structures for the representation of triangles. Then apply the new function to a list of triangles and observe the effect.~ external Solution<#66467#><#66467#> <#34810#>Exercise 27.1.4<#34810#> external ~<#71493#>domain knowledge -- geometry -- esp some knowledge about <#66468#><#34813#>sin<#34813#><#66468#> and <#66469#><#34814#>cos<#34814#><#66469#> are helpful -- provide if necessary<#71493#> Take a look at the following two pictures: rawhtml47 The left one is the basic step for the generation of the ``Savannah'' tree on the right. It is analogous to the middle picture on page~#picsiepic#34815>. Develop a function that draws trees like the one in the right picture. <#34816#>Hint:<#34816#> Think of the problem as drawing a straight line, given its starting point and an angle in, say, radians. Then, the generative step divides a single straight line into three pieces and uses the two intermediate points as new starting points for straight lines. The angle changes at each step in a regular manner.~ external Solution<#66470#><#66470#> <#34822#>Exercise 27.1.5<#34822#> In mathematics and computer graphics, people must often connect some given points with a smooth curve. One popular method for this purpose is due to Bezier. Here is a sequence of pictures that illustrate the idea: rawhtml48 For simplicity, we start with three points: <#66471#><#34825#>p1<#34825#><#66471#>, <#66472#><#34826#>p2<#34826#><#66472#>, and <#66473#><#34827#>p3<#34827#><#66473#>. They form the outermost triangle in all three pictures, with <#66474#><#34828#>p1<#34828#><#66474#> being the leftmost and <#66475#><#34829#>p3<#34829#><#66475#> the rightmost point. If the triangle is small enough, draw it. It appears as a large point. If not, generate two smaller triangles as illustrated in the center picture. The outermost points, <#66476#><#34830#>p1<#34830#><#66476#> and <#66477#><#34831#>p3<#34831#><#66477#>, remain the end points. The new center points, <#66478#><#34832#>r2<#34832#><#66478#> and <#66479#><#34833#>q2<#34833#><#66479#>, are the mid-points between <#66480#><#34834#>p1<#34834#><#66480#> and <#66481#><#34835#>p2<#34835#><#66481#> and between <#66482#><#34836#>p3<#34836#><#66482#> and <#66483#><#34837#>p2<#34837#><#66483#>, respectively. The new leftmost and rightmost endpoints, respectively, is the midpoint between <#66484#><#34838#>r2<#34838#><#66484#> and <#66485#><#34839#>q2<#34839#><#66485#>. To test the function, use the teachpack <#34840#>draw.ss<#34840#>. Here is some good test data:
<#34845#>(define<#34845#> <#34846#>p1<#34846#> <#34847#>(make-posn<#34847#> <#34848#>50<#34848#> <#34849#>50))<#34849#>
<#34850#>(define<#34850#> <#34851#>p2<#34851#> <#34852#>(make-posn<#34852#> <#34853#>150<#34853#> <#34854#>150))<#34854#> 
<#34855#>(define<#34855#> <#34856#>p3<#34856#> <#34857#>(make-posn<#34857#> <#34858#>250<#34858#> <#34859#>100))<#34859#> 
Use <#66486#><#34863#>(start<#34863#>\ <#34864#>300<#34864#>\ <#34865#>200)<#34865#><#66486#> to create the canvas. Experiment with other positions.~ external Solution<#66487#><#66487#>