All Star Polygons with Python Turtle
Tags: python, turtle, geometry, math
Published: 2021-06-18 17:54:19

In the [previous article](https://dev.to/taarimalta/regular-star-polygon-with-python-turtle-k37) we showed how to draw a regular star polygon which was not degenerate i.e it could be drawn without lifting the pen from the paper. In this one we will attempt to come up with a way to draw two types star polygons. 

> By two types I mean degenerate (must lift pen) and non-degenerate/regular (no lift pen) but I don't know the true names of these star polygons types. But I hope I get the idea across

# Repeated regular polygon method

In this method we are going to look at how to draw a degenerate star polygon by repeatedly drawing regular polygon polygons (like triangles, squares and pentagons)

## The Star of David

Let's begin with the simplest degenerate star we know of the Star of David. This type of star can be draw by simply drawing one equilateral triangle and then overlapping it with another one which is upside down

Note that you can draw a triangle using the circle function! See the code below

```python
import turtle as t

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

LENGTH=200

t.circle(LENGTH)
t.circle(LENGTH,steps=3)
t.circle(LENGTH,steps=4)
t.circle(LENGTH,steps=5)
```

In the code above, we are drawing a triangle, square and a pentagon inscribed within the same circle making use of the `steps` parameter. Here's what we get:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/erigf26yszutx0p2bqcd.png)

Now we know how to use the `steps` argument, we can use it twice from two different places on a circle to draw our star

```python
import turtle as t

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

LENGTH=200

def star_of_david():
    """Draws two overlapping triangles
    """
    t.circle(LENGTH,steps=3)
    t.penup()
    t.circle(LENGTH, 180)
    t.pendown()
    t.circle(LENGTH,steps=3)

t.circle(LENGTH)
star_of_david()

```

In the code above `t.circle(LENGTH, 180)` makes the turtle go to the top of the circle so that it can draw another triangle from there. Here's what we get

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6pw0dsqqcwt571sqyyb8.png)

## Degenerate Octagram

You can draw a degenerate octagram using a similar method

```python
import turtle as t

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

LENGTH=200

def degenerate_octagram():
    t.circle(LENGTH,steps=4)
    segments=8
    t.circle(LENGTH,360/segments)
    t.circle(LENGTH,steps=4)
    
t.circle(LENGTH)
degenerate_octagram()
```

After drawing the first square, we are moving the turtle to a location determined by `360 / segments`. `segments = 8` because an octagram has 8 equal segments.

> Remember that a n-gram star has n equal segments

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5nl2ot28pdt85ubyiyaf.png)

## Degenerate enneagram

According to [wikipedia](https://en.wikipedia.org/wiki/Enneagram_(geometry)), enneagram is a 9 pointed star. Unlike the hexagram and the decagram, the enneagram cannot be drawn with just two regular polygons. But how many do we need? 

A degenerate enneagram should be drawn by using 3 equilateral triangles (You can try drawing this on a piece of paper too).

```python
import turtle as t

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

LENGTH=200

def enneagram():
    segments=9
    
    t.circle(LENGTH, steps=3)
    t.circle(LENGTH, 360/segments)
    t.circle(LENGTH, steps=3)
    t.circle(LENGTH, 360/segments)
    t.circle(LENGTH, steps=3)
     
t.circle(LENGTH)
enneagram()
    
```

In the code above, we are calling the `circle(LENGTH, steps=3` function 3 times

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zihtx0p1p6fikrblg1uz.png)

## A General method - 1 (Regular non star Polygon based)

Now, how do we know the number of times we need to repeat the circle and step commands? To answer that we can make a few observations about the examples above:

- A hexagram is a (6,2) star where the vertices - 6 is divisible the order- 2, 3 times
- A octagram is a (8,2) star where the vertices - 8 is divisible the order- 2, 4 times
- A enneagram is a (9,3) star where the vertices - 9 is divisible the order- 3, 3 times

With the above, we might be able to say that for a degenerate `n`-gram with the order `m`, the quotient of n/m is the number of sides of the polygon while the order m is the number of times we need to draw the polygon

So, we should try the following:

> For an n,m degenerate star draw m number of regular polygons
> And the polygons drawn should have n/m sides

Let's code that

```python
import turtle as t

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

LENGTH=200

def degenerate(n,m):
    segments = n
    order = m
    
    sides = int(n/m)
        
    for _ in range(order):
        t.circle(LENGTH, steps=sides)
        t.circle(LENGTH, 360/segments)
         
t.circle(LENGTH)
degenerate(10,2)
```
The code above works! It drew the 10 gram and the 12 grams below

|(10,2)|(12,2)|
|--|--|
|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t9urcix5adeusk8ohwpx.png)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ksb02zykam1c4iaocs8y.png)
 |

You can also draw (12,3) and (12,4)

|(12,3)|(12,4)|
|--|--|
|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bho0v86xw0b3cv2it15q.png)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o6ebxl1ez2rrsc14psc4.png)|

Interestingly if you try to draw (10,5) and (12,6) it draws this:

|(10,5)|(12,6)|
|--|--|
|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lxhltp836dv93qnxo6xd.png)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhtfxlmg76fk0vg3kcwm.png)|

This is a reminder that the possible orders (values of m) are 
2 through (n-1)/2-1. Basically the order can't be 1 and it also can't be half of the number of vertices

With the general code above, have we covered all the degenerate regular star polygons?

## The (10,4) star

In the examples that we have looked at a degenerate star polygon can be composed of multiple regular (non-star) polygons. But (10,4) is a curious case. In the previous article we had written a code that draws regular stars. Let's see what happens when we use it here

```python
import turtle as t

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

LENGTH=150

def regular_star(n,m):
    """ n = number of pointies
        m = order of the star,
            (number of skips + 1)
    """
    
    angle = 360*m/n
    
    for count in range(n):
       t.forward(LENGTH)
       t.right(angle)
   
regular_star(10,4)
```

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7e979s4dokbrfugaclci.png)

Yes we get a pentagram! But we were supposed to have 10 vertices :(

## A new type of degenerate

Hence, (10,4) is a new type of degenerate star where `n` is not divisible by `m` and as such we cannot draw it with the `degenerate` function we wrote earlier.

We can draw this type of degenerate stars by overlapping two stars. Unfortunately we cannot use the circle's `step` parameter anymore. We need to draw a regular star then rotate it to draw another star. To do that we will first begin by modifying our regular_star function as follows

```
def regular_star(n,m):
    """ n = number of pointies
        m = order of the star,
            (number of skips + 1)
    """
    
    angle = 360*m/n
    
    t.left(180-angle/2)

    for count in range(n):
       t.forward(LENGTH)
       t.right(angle)
       
    t.right(180-angle/2)
```

The code above adds `t.left(180-angle/2)` to place the regular star to the left side of the turtle. This is similar to how a circle command behaves

|||
|--|--|
|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxz4neou4e9j245u29ws.png)| ![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bskgdzjxcdo32slg6lxg.png)|

Notice above that both the stars are drawn to the left side of the turtle

We will additionally make the following changes to our code

```python
import turtle as t
import math

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

RADIUS=150


def regular_star(n,m):
    """ n = number of pointies
        m = order of the star,
            (number of skips + 1)
    """
    
    angle = 360*m/n
    center_angle = 2*m*math.pi/n
    
    t.left(180-angle/2)
    
    
    length=2*RADIUS*math.sin(center_angle/2)

    for count in range(n):
       t.forward(length)
       t.right(angle)
       
    t.right(180-angle/2)

regular_star(7,2)
t.circle(RADIUS)
```

In the code above, we have made the following changes

- Replaced the global `LENGTH` with `RADIUS`. This is to allow us to draw the star of a defined radius
- Computed a center angle which is the angle subtended by `m` segments on the center of the circle. `center_angle` is the representation of arch length in radians
- We used the `center_angle` and the `RADIUS` to compute the length of the arms of the star

|(5,2)|(7,2)|(7,3)|
|--|--|--|
|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v2jyuxkji4f3qmvt0k33.png)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qe7uj7b2jlpj57jpfl0t.png)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqjmgfh3pepn4yt16kif.png)|

We can now draw a star to the left side of the turtle having defined radius. We can even overlap them all

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghsiy75d2msmzziza5qf.png)
 
## A better general method

Our goal is to build a better general method to draw any degenerate regular polygon star of a given number of vertices and order. Let's begin again by listing the properties of the degenerate stars

|Star|Properties|
|--|--|
|(6,2)|2 overlapping triangles|
|(8,2)|2 overlapping squares|
|(9,3)|3 overlapping triangles|
|(10,4)|2 overlapping pentagrams|
|(14,4)|2 (7,2) stars|
|(14,6)|2 (7,3) stars|
|(15,6)|3 overlapping pentagrams|

> The above can be verified by trying them out on a piece of paper (or by just thinking way too hard)

Here's an algorithm that we might be able to use to draw all the of the above stars

1. Find the greatest common divisor (gcd) of `n` and `m` 
2. Divide `n` and `m` by the `gcd` to obtain a star (n/gcd,m/gcd)
3. Draw the (n/gcd,m/gcd) star
4. Move the turtle to another position on the circumcircle determined by 360/n degrees
5. Repeat 3 until we have drawn the star `gcd` number of times

Let's try it:

```python
import turtle as t
import math

t.shape('turtle')
t.color('#4F86F7')
t.pensize(2)

RADIUS=150

def regular_star(n,m):
    """ n = number of pointies
        m = order of the star,
            (number of skips + 1)
    """
    angle = 360*m/n
    center_angle = 2*m*math.pi/n
    
    t.left(180-angle/2)
    
    length=2*RADIUS*math.sin(center_angle/2)

    for count in range(n):
       t.forward(length)
       t.right(angle)
       
    t.right(180-angle/2)


def star(n,m):
    """ n = number of pointies
        m = order of the star,
            (number of skips + 1)
    """
    gcd = math.gcd(n,m)
    new_n = int(n/gcd)
    new_m = int(m/gcd)
    segment_angle = 360/n
    
    for _ in range(gcd):
        regular_star(new_n, new_m)
        t.penup()
        t.circle(RADIUS, segment_angle)
        t.pendown()
   
star(10,4)
```

With the code above it turns out that when the gcd is 1, we get either a regular non-degenerate star or a polygon. So we have written code to generalize:

- Regular Polygons
- Regular Non-degenerate stars!
- Regular degenerate stars!~
- Weird stick star :)

Examples:

|Name|Drawing|
|--|--|
|(10,1)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xkvyln6sjnuo68fzwmy.png)|
|(10,2)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwqu45ee409neqlvuoyt.png)|
|(10,3)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mri7tkqtno2h8fcdeud.png)|
|(10,4)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0k161tsgas4jxsab1kvk.png)|
|(10,5)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5bmskgtpxjm6i20wqx2w.png)|
|(14,4)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zkis13m5o6lrreowmrd.png)|
|(14,6)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kpv7t4jtak80r1p91790.png)|
|(15,6)|![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iq39oz1vpfb6nvwn7zv7.png)| 

# Bonus

You can overlap all orders for a fixed number of vertices to get beautiful images such as these

```python
def all_stars(n):
    for x in range(int(n/2)):
        star(n,x+1)


all_stars(10)
t.hide()
```

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mi1kpxh7adqbje2ztpoi.png)

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mnimz24z3wcyjq40npf4.png)


Yay!!~