My God, It's Full of Stars!
Let's use our new tools to draw some stars.
For the purpose of this exercise let's assume that a star is a polygon with p
points.
However, instead of connecting each point to its neighbours,
we'll connect them to the nth
point around the circumference.
For example, Figure sequences:stars shows stars with p=11
and n=1 to 5
.
n=1
produces a regular polygon while
values of n
from 2
upwards produce stars with increasingly sharp points:
Write code to draw the diagram above.
Start by writing a method to draw a star
given p
and n
:
def star(p: Int, n: Int, radius: Double): Image =
???
Hint: use the same technique we used for polygon
previously.
<div class="solution">
Here's the star
method. We've renamed p
and n
to points
and skip
for clarity:
def star(sides: Int, skip: Int, radius: Double): Image = {
import Point._
import PathElement._
val rotation = 360.degrees * skip / sides
val start = moveTo(polar(radius, 0.degrees))
val elements = (1 until sides).toList map { index =>
val point = polar(radius, rotation * index)
lineTo(point)
}
Image.path(ClosedPath(start :: elements)).strokeWidth(2)
}
</div>
Using structural recursion and beside
write a method allBeside
with the signature
def allBeside(images: List[Image]): Image =
???
We'll use allBeside
to create the row of stars.
To create the picture we only need to use values of skip
from 1
to sides/2
rounded down. For example:
allBeside(
(1 to 5).toList map { skip =>
star(11, skip, 100)
}
)
<div class="solution"> We can use the structural recursion skeleton to write this method.
We start with
def allBeside(images: List[Image]): Image =
images match {
case Nil => ???
case hd :: tl => ???
}
Remembering the recursion gives us
def allBeside(images: List[Image]): Image =
images match {
case Nil => ???
case hd :: tl => /* something here */ allBeside(tl)
}
Finally we can fill in the base and recursive cases.
def allBeside(images: List[Image]): Image =
images match {
case Nil => Image.empty
case hd :: tl => hd.beside(allBeside(tl))
}
</div>
When you've finished your row of stars,
try constructing a larger image from different values of p
and n
.
There is an example in Figure sequences:all-star. Hint: You will need to create a method allAbove
similar to allBeside
.
<div class="solution"> To create the image in Figure sequences:stars2 we started by creating a method to style a star.
def style(img: Image, hue: Angle): Image = {
img.
strokeColor(Color.hsl(hue, 1.0, 0.25)).
fillColor(Color.hsl(hue, 1.0, 0.75))
}
We then created allAbove
, which you will notice is very similar to allBeside
(wouldn't it be nice if we could abstract this pattern?)
def allAbove(imgs: List[Image]): Image =
imgs match {
case Nil => Image.empty
case hd :: tl => hd above allAbove(tl)
}
The updated scene then becomes:
allAbove((3 to 33 by 2).toList map { sides =>
allBeside((1 to sides/2).toList map { skip =>
style(star(sides, skip, 20), 360.degrees * skip / sides)
})
})
</div>