euclid

What Is It?

euclid is a module that allows the user to create simple shapes - points, segments, polygons, circles, etc. It's a thin wrapper for a small subset of the functions in the pygame library of functions. I created euclid with two goals in mind:

euclid consists of a set of functions and the Pen class. The former gives the user the ability to create and alter the page on which pens draw, change the animation speed, save the page to  a file, etc. The Pen class allows the user to create multiple pens and then use them to draw to the page.

Get euclid here. You'll find the euclid module there and a bit of sample code. If you wish to use euclid elsewhere, you'll have to install pygame.

Code Samples

Let's begin with examples. After each, I'll analyze. Make sure to read the code too!

Example One: Equilateral Triangle

# Equilateral Triangle


# Do not alter the two lines below.

import euclid

from colors import *


# Now we create the page on which pens will draw.

# Set its size to 400 by 400, set its colors to PALETURQUOISE,

# set the tracer to 0 (increase the value of the tracer to increase the animation speed).

# Do not delete this line, but change if you like.

euclid.createPage(size = (400, 400), color = PALETURQUOISE, tracer = 0)


# Next we create a pen name logos.

# Its color is VIOLETRED, it begins at position (160, 240)

# and direction 0 (which is directly right).

# The delay of 6 slows it down a bit; decrease delay to increase animation speed.

# Do not  delete, but but change if you like.

# You may also create additional pens.

logos = euclid.Pen(color = VIOLETRED, position = (160, 240), direction = 0, delay = 6)


# Now we draw with logos.

# The result will be an equilateral triangle.

for i in range(3):

   logos.forward(96)

   logos.turn(120)


euclid.finish() # only necessary if tracer isn't 0


euclid.wait()  # keep window open

The first line executes the code in the file named "euclid.py"; and once executed, all its definitions are available to use. (That's what the import command does. It executes a python file and so makes all definitions in that file available in the program that executes the import.)

The next line - from colors import * -  executes the code in the filed named "colors.py". Note though that the syntax here is a bit different. We now say from ... import, not just import. What's the difference? If we import in this way, we can use the definitions in the file with no need to prefix them with colors.; for instance, we can say just PALETURQUOISE instead of colors.PALETURQUOISE.

What's the star at the end of from colors import *?  This means that we import every definition in colors.

Take a look at the "colors.py" file. You can use any of the color names found there.

Next we create a page on which to draw and we set its color - that's euclid.createPage(size = (400, 400), color = PALETURQUOISE, tracerHere's what that means: "Look in the euclid module. There you'll find a createPage function. Execute it, and send the values size = (400, 400), color = PALETURQOUISE and tracer = 0 when you do." (The greater the tracer, the faster the animation speed.) 

We have here a new way to send a value to a function parameter.  We don't simply say euclid.createPage((400, 400), PALETURQUOISE, 0) (though we could have said that if we wanted). Instead we give the parameter name and its value. We can always do this if we want; that is, we could always send values to functions with the syntax parameter = value. (Argument values so sent to a function are called "keyword arguments".) Why do I do it here but not elsewhere? Primarily readability. 

Next we create a pen named logos and set the attributes of that pen, namely its color, initial position, initial direction (0 points directly right) and a delay that slows the speed of the pen. This is the line logos = euclid.Pen(color = VIOLETRED, position = (160, 240), direction = 0, delay = 6). Later we will issue commands to logos.

I should say a little about Euclid's coordinate system. The position (0, 0) is the top left corner of the window. The first coordinate gives horizontal position, which increases as we go right. The second coordinate gives vertical position, which increases as we go down. So the position (160, 240) is 160 right and 240 down from top left. Note too that a direction of 0 points directly right, and that an increase in direction turns us counterclockwise and a decrease turns us clockwise. So a direction of 90 points directly up, 180 points to the left and 270 points down; and -90 points down, -180 points to the left and -270 points up. (We can even reach or exceed 360 and -360 if we want.)

Now, after we create the pen, we enter a for loop. Since we wish to draw a triangle, we use range(3) - one iteration for each side. Inside the loop we tell logos to move forward  a distance of 96 and to turn 120 degrees. (A positive turn is counterclockwise. Negative would be clockwise.) Notice the syntax here: it's object.action(arguments). This an example of the so-called object-oriented paradigm (oop). logos is our object. logos is of Pen type, and associated with any object of Pen type are a set of actions that it can perform. To make a pen perform an action possible for objects of its type, we use the all-powerful dot. object.action(arguments) (read as "object dot action").  This means "Hey you! You object you! I know your type. You can do action. So do it!" (There's more to oop, but that's enough for now.)

Example Two: Disc

Try the code below. (At replit, you can fork the project you created above, and then replace the code in main.py with the code below.)

# Disc at Center of Window


import euclid

from colors import *


euclid.createPage(color = LIGHTGRAY)

logos = euclid.Pen(color = IVORYBLACK)


x_max, y_max = euclid.getScreenSize() # x- and y-coords of bottom right

logos.up() # so no mark will be left when logos moves

logos.goto(x_max // 2, y_max // 2) # center of screen

logos.disc(48, IVORY) # IVORYBLACK border, IVORY interior


euclid.wait() # keep window open

Most of  this is clear, but I should make a few comments. Notice that when we create a page, we send a value only for color. How can we get away with that? Why don't we have to send values for size and tracer too? The answer is that the createPage function specifies defaults values for these parameters. If we send a value, it overrides the default; if we don't, the default is used. (See the Functions section below for the default values.)

Example Three: Load and Alter an Image

Read the code AND the comments. You should be able to follow.

# Load image, draw on it, save it


import euclid

from colors import *


# load image from file named "fry.jpg"

# this should reside in the same folder as the program itself

euclid.loadPage("fry.jpg")

logos = euclid.Pen(color = DARKGRAY, position = (400, 100))


# add some circles to image

for i in range(12):

    logos.circle(i * 4)

    logos.up()

    logos.turn(30)

    logos.forward(i + 12)


# save image; include file type

# see the pygame docs for the file types that can be used here

euclid.savePage("fry_circles.jpg")


euclid.wait()  # keep window open

Functions: Syntax and Description

I should now give you a complete run-down of euclid's capabilities.

euclid defines each of the functions described below. For each I give the syntax and then explain the effect of the function. Note: if you import euclid with the syntax import euclid, you'll need to use euclid. to call these functions. So for instance, you would write not simply createPage(), but must use euclid.createPage() instead.

createPage

Syntax: createPage(size, color, tracer)

Creates a  blank page on which pens will leave their marks. size is a two member tuple; it's default value is (640, 480). color is a RGB tuple; for instance (255, 255, 255) sets the screen to white. tracer is an integer - 0 or positive - that controls animation speed. If for instance the value of tracer is 3, only every third change made to the page will be rendered to the screen. This increases animation speed. (I borrow the name from Python's turtle module. It's clever. A tracer round is typically every fifth round discharged.)

loadPage

Syntax: loadPage(name, tracer)

The second way to create the page on which pens will leave their marks. An image will be loaded from the file with the given name. See the discussion of tracer above in createPage.

setTracer

Syntax: setTracer(n)

n must be an integer 0 or greater. 0 is the default. If the value of n is greater than 0, only every nth change made to the page will be rendered to the screen. This increases animation speed.

finish

Syntax: finish()

If a tracer has been set, the script should end with a finish. This will make any last changes to the page appear on the screen.

getScreenSize

Syntax: getScreenSize()

Returns the width and height  of the current page as a two member tuple.

clear

Syntax: clear(color)

Wipes the page clear and gives it a (potentially new) color. If no color is given, the page will remain the color it was before the clear.

savePage

Syntax: savePage(name)

Save the current contents of the page to a file of the given name. Include the file type, e.g. "fry.jpg".

wait

Syntax: wait(n)

The last line of code (unless you want euclid's window to close when your script is complete). If no value is given, euclid's window remains open until the user closes it. A value passed to n results in a pause of n seconds before the script continues execution.

Pen Class: Constructor

As I explained in the section Code Samples above, we must create a pen before we can draw; and to do this, we (oop talk here) call the Pen constructor. The syntax is penName = euclid.Pen(arguments). We thereby create on object in the Pen class, i.e. a pen, and assign that object to penName.

The constructor for the Pen class has five parameters: color, position, direction, delay and draw. The defaults are:

color = BLACK, position = (0, 0), direction = 0, delay = 0, draw = True

The position (0, 0) is top left. The direction 0 points left to right. delay imposes a wait between marks made by the pen.  If draw is set to False, the pen will leave no mark when it moves.

The block below create three pens named logos, nomos and arche.

import euclid

from colors import *


logos = euclid.Pen()  # create with defaults

nomos = euclid.Pen(color = MELON, position = (320, 240), delay = 3)  # override defaults except for draw

arche = euclid.Pen(SIENNA, (100, 100), 12, False)  # parameter names are of course optional

Pen Class: Methods

In the object-oriented paradigm, the functions that we call on an object with the object. (read "object dot") syntax are called "methods".  Below are the  methods of the Pen class. I assume that a pen named logos has been created.

distanceTo

Syntax: logos.distanceTo(x, y)

Return the distance from logos' current position to the point with coordinates (x, y). 

getX

Syntax: logos.getX()

Return the x-coordinates of logos' current position.

getY

Syntax: logos.getY()

Return the y-coordinate of logos' current position.

getDir

Syntax: logos.getDir()

Return logos' current direction in degrees. 0 points right, 90 up, 180 left and 270 down.

getColor

Syntax: logos.getColor()

Return logos' current color as a RGB triple.

isDown

Syntax: logos.isDown()

Return a Boolean that answers the question, "Is logos currently down?" If down, logos will leave marks with a goto or a foward.

goto

Syntax: logos.goto(x, y)

Send logos to the position (x, y). If logos is down, a mark  will be left.

forward

Syntax: logos.forward(d)

Move logos forward a distance d in logos' current direction. A negative distance results in motion backwards. If logos is down, a mark will be left.

setDir

Syntax: logos.setDir(d)

Set logos' direction to d given in degrees. 0 points right, 90 up, 180 left and 270 down.

turn

Syntax: logos.turn(d)

Turn logos by d degrees. Positive is counterclockwise, negative is clockwise.

setColor

Syntax: logos.setColor(color)

Set the color of the mark that logos makes. The color should be given as a RGB triple.

up

Syntax: logos.up()

Pick logos up from the page.  If up, logos will not leave a mark if told to goto or to go forward.

down

Syntax: logos.down()

Put logos down on the page. If down, logos will leave a mark if told to goto or to go forward

setDelay

Syntax: logos.setDelay(d)

Set the delay between marks made by logos. 0 is no delay.

point

Syntax: logos.point()

Drop a point of logos' current color at logos' current position. A point is a single pixel.

beginRegion, endRegion

Syntax: logos.beginRegion(color), logos.endRegion()

A region is a filled polygonal shape. Place the first before a block of code that draws a polygon by use of some combination of goto's and forward's. Place the second after the polygon is complete and the polygon will be filled with logos' current color.

If no color value is sent to beginRegion, the fill color will be logos' current color; if a color  is sent, that will become the fill color and the boundary color will be logos' current color.

circle

Syntax: logos.circle(r)

Draw a circle of radius r. logos' current position is the center.

disc

Syntax: logos.disc(r, color)

Draw a disc of radius r. (A disc is a filled circle.) logos' current position is the center.

If a color value is sent to disc, that will become the fill color and the boundary color will be logos' current color; if no color value is sent, the boundary and interior will both be logos' current color.

write

Syntax: logos.write(text, fontName, fontSize)

Write the given text in the given font at the given size. The font name should be passed as a string, e.g. "Times New Roman". The default font is Arial and the default font size is 30.

Filled Star

Let's end with a few more examples. This one a bit more complex. The one below ... more than a bit.

You'll find the line of code logos.goto(*center). What's the star do? Of course it isn't part of the name center. I suggest a Google search.

import euclid

from colors import *


euclid.createPage(tracer = 0)

logos = euclid.Pen(color = RED, delay = 12)

w, h = euclid.getScreenSize()

center = (w//2 - 100, h//2)


logos.up()

logos.goto(*center)

logos.down()

logos.down()

logos.beginRegion(YELLOW)  # make the fill color YELLOW

while True:

    logos.forward(200)

    logos.turn(170)

    if logos.distanceTo(*center) < 1:

        break

logos.endRegion()  # fill the polygon just completed


euclid.finish()

euclid.wait()

Spirograph

# We have two circles, one inside and tangent to the other.

# The outer circle remains fixed.

# The inner circles rotates around the circumference of the outer.

# R is the radius of the outer circle.

# r is the radius of the inner

# The pen is placed in a hole in the inner circle

# and traverses a curve as the inner circle spins.

# p is the distance from the center of the inner circle to the pen hole.


# For a visualization, go to: nathanfriend.io/inspirograph/

# References: en.wikipedia.org/wiki/Spirograph,

# en.wikipedia.org/wiki/Hypotrochoid


import euclid, math, random

from colors import *


def translate(w, h, x, y):

    # Translate Cartesian coordinates to image coordinates.

    # w and h are screen width and height respectively.

    return x + w/2, -y + h/2 


def gcf(m, n):

    # Parameters: positive integers m and n

    # Return: the greatest common factor of m and n

    # The Euclidean Algorithm is used.

    if m == n:

        return m

    elif m > n:

        m, n = n, m

    while n % m != 0:

        n, m = m, n % m 

    return m


def draw_spirograph(pen, w, h, R, r, d):

    L = d / r

    K = r / R

    # Place the pen at the start of the spirograph.

    t = 0

    x = R * ((1-K) * math.cos(t) + L*K*math.cos(((1-K)/K)*t))

    y = R * ((1-K) * math.sin(t) - L*K*math.sin(((1-K)/K)*t))

    pen.up()

    # rTanslate coordinates to the usual Cartesian variety

    pen.goto(*translate(w, h, x, y))

    pen.down()

    # Compute the period of the spirograph

    # and the number of revolutions necessary to complete it.

    numRevolutions = r // gcf(R - r, r)

    period = numRevolutions * 2 * math.pi

    print('\nNumber of revolutions:', numRevolutions)

    # Choose an increment for angle measure.

    # A smaller increment means a smoother curve.

    increment = math.pi / 360

    revolution = 0

    while t <= period:

        t = t + increment

        revolution = math.trunc(t % numRevolutions)

        x = R * ((1-K) * math.cos(t) + L*K*math.cos(((1-K)/K)*t))

        y = R * ((1-K) * math.sin(t) - L*K*math.sin(((1-K)/K)*t))

        pen.goto(*translate(w, h, x, y))


logos = euclid.Pen(color = BLUEVIOLET)

euclid.createPage(tracer = 30)

width, height = euclid.getScreenSize()

outer, inner, dist = 140, 47, 44  # try different values!


draw_spirograph(logos, width, height, outer, inner, dist)


euclid.finish()

euclid.wait()