# Drawing Paths

Doodle provides another type of `Image`

that makes particular use of sequences.
`Paths`

represent arbitrary shapes created using sequences of pen movements:

```
val image = OpenPath(List(
MoveTo(Vec(0, 0).toPoint),
LineTo(Vec(100, 0).toPoint),
LineTo(Vec(50, 100).toPoint),
BezierCurveTo(Vec(50, 0).toPoint, Vec(0, 100).toPoint, Vec(0, 0).toPoint)
))
// image: Path = // ...
image.draw
```

Pen movements come in three varieties:

`MoveTo(point)`

---move the pen to`point`

without drawing a line;

`LineTo(point)`

---move the pen to`point`

drawing a straight line;

`BezierCurveTo(cp1, cp2, point)`

---move the pen to`point`

drawing a bezier curve---`cp1`

and`cp2`

are "control points" determining the shape of the curve.

The arguments in each case are objects of type `Vec`

, which are 2D vectors representing `x,y`

points (the name `Vector`

is already taken by the `scala.collection.Vector`

class). There are various ways we can create and transform `Vecs`

:

Code Result Description Example
-------------------------- --------- ------------------------------- -----------------------------------
`Vec(num, num)`

`Vec`

Create a vector using `Vec(3, 4)`

`x,y`

coordinates

`Vec.polar(angle, length)`

`Vec`

Create a vector using `Vec.polar(30.degrees, 100)`

polar coordinates

`Vec.zero`

`Vec`

A zero vector (`0,0`

) `Vec.zero`

`Vec.unitX`

`Vec`

A unit X vector (`1,0`

) `Vec.unitX`

`Vec.unitY`

`Vec`

A unit Y vector (`0,1`

) `Vec.unitY`

`vec * num`

`Vec`

Multiply `vec`

by `num`

`Vec(2, 1) * 10`

`vec / num`

`Vec`

Divide `vec`

by `num`

`Vec(20, 10) / 10`

`vec + vec`

`Vec`

Add vectors `Vec(2, 1) + Vec(1, 3)`

`vec - vec`

`Vec`

Subtract vectors `Vec(5, 5) - Vec(2, 1)`

`vec rotate angle`

`Vec`

Rotate anticlockwise by `angle`

`Vec(5, 5) rotate 45.degrees`

`vec.x`

`Double`

Get the X component of `vec`

`Vec(3, 4).x`

`vec.x`

`Double`

Get the Y component of `vec`

`Vec(3, 4).y`

`vec.length`

`Double`

Get the length of `vec`

Vec(3, 4).length`

We can use these operations to create paths quickly by adding vectors. Notice how we start the shape with a `MoveTo`

element (all paths implicitly start at the origin). This is a very common pattern.

```
val elements = (0 to 360 by 36).map { angle =>
val point = (Vec.unitX * 100) rotate angle.degrees toPoint
val element =
if(angle == 0)
MoveTo(point)
else
LineTo(point)
element
}
// elements: scala.collection.immutable.IndexedSeq[doodle.core.PathElement] = // ...
val decagon = OpenPath(elements)
// decagon: doodle.core.Path = // ...
decagon.draw
```

### Exercise: My God, It's Full of Stars!

Let's use this pattern 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, the diagram below 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 =
???
```

Create the points for your star using ranges and `Vec.polar`

:

Use your choice of recursion and `beside`

or iteration and `allBeside`

to create the row of stars.

<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) = {
val centerAngle = 360.degrees * skip / sides
val elements = (0 to sides) map { index =>
val point = Vec.polar(centerAngle * index, radius)
if(index == 0)
MoveTo(point)
else
LineTo(point)
}
Path(elements) strokeWidth 2
}
```

We'll use `allBeside`

to create the row of stars.
We only need to use values of `skip`

from `1`

to `sides/2`

rounded down:

```
(allBeside((1 to 5) map { skip =>
star(sides, skip, 100)
})).draw
```

</div>

When you've finished your row of stars,
try constructing a larger image from different values of `p`

and `n`

.
Here's an example:

<div class="solution">
To create the image above, we started by adding colours
and a chunkier outline to the definition of `star`

:

```
def star(sides: Int, skip: Int, radius: Double) = {
val centerAngle = 360.degrees * skip / sides
val elements = (0 to sides) map { index =>
val point = Vec.polar(centerAngle * index, radius).toPoint
if(index == 0)
MoveTo(point)
else
LineTo(point)
}
OpenPath(elements).
strokeWidth(2).
strokeColor(Color.hsl(centerAngle, 1.normalized, .25.normalized)).
fillColor(Color.hsl(centerAngle, 1.normalized, .75.normalized))
}
```

The updated scene then becomes:

```
allAbove((3 to 33 by 2) map { sides =>
allBeside((1 to sides/2) map { skip =>
star(sides, skip, 20)
})
})
```

</div>