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
- ;; <#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#>
- ;; <#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. Solution<#66457#><#66457#>
<#34748#>Exercise 27.1.2<#34748#>
~<#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.~ 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.~ Solution<#66467#><#66467#>
<#34810#>Exercise 27.1.4<#34810#>
~<#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.~ 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.~ Solution<#66487#><#66487#>