# Home

This site will provide guides, tutorials, and documentation for pyDraw and its submodules.

Welcome to pyDraw's official guides and documentation page! Here you can find resources to help you get started with pyDraw or view certain example programs to see it in action!

## What is pyDraw?

pyDraw is a library designed for teachers and students that allows for easy and simple graphics development. Its strengths lie in Object-Oriented Structures and methods, pythonic methods and interfaces, and an automatic input system that simplifies input to a new degree.

pyDraw is written in Python (hence the name), but if you prefer Java, it has a sister-library, [javaDraw](https://javadraw.graphics), which contains the majority of the content in pydraw, with some differences.

## Getting Started

{% content-ref url="/pages/-MU91ZkdxoMXB4bJJspz" %}
[Installation and Setup](/guides/installation)
{% endcontent-ref %}

{% content-ref url="/pages/-MU95q4mLoDMn2Cj6bjp" %}
[Getting Started](/guides/getting-started)
{% endcontent-ref %}

{% content-ref url="/pages/-MU99hDJBE3a2O3abcIk" %}
[Quickstart Examples](/guides/quickstart)
{% endcontent-ref %}

## Documentation and API Reference

{% content-ref url="/pages/-MUWArdBIZqZATHg1BnV" %}
[Documentation](/documentation)
{% endcontent-ref %}

{% hint style="info" %}
pyDraw was created and is currently maintained by Noah Coetsee; pay me a visit:

<https://noahcoetsee.me>
{% endhint %}


# Documentation

This page will serve as a quick hub for documentation of pyDraw (Quick Reference and Full API)

## Quick Reference

You can access Quick Reference to view quick lists of available methods for commonly used classes. It will also have some examples for more extensive features.

{% content-ref url="/pages/-MUAOnbzy2V4Q\_po0myU" %}
[Quick Reference](/quick-reference/screen)
{% endcontent-ref %}

## Full API Reference

You can access the full API reference for pydraw here:

{% embed url="<https://pydraw.graphics/api>" %}


# Installation and Setup

In order to install pydraw you must, of course, have Python installed and added to the PATH.

## Installation

Installing pyDraw via the command-line is as simple as one command:

```
pip install pydraw
```

It is also possible to use pydraw by placing the library file in the same directory as your project:

{% embed url="<https://github.com/pydraw/pydraw/releases/1.4.1>" %}

{% hint style="info" %}
&#x20;Using the library file for pyDraw will work normally for most of the time, but you may have to install [Tkinter](https://tkdocs.com/tutorial/install.html) and/or [Pillow](https://pillow.readthedocs.io/en/stable/installation.html) if they are not installed!
{% endhint %}

Once you've got pyDraw installed, you're all set. You can test your installation like so:

```python
from pydraw import *

screen = Screen(800, 600)

print('Hello, pyDraw!')
screen.stop()
```

Then run your file with Python, and you're all set:

```bash
python main.py
```


# Getting Started

Starter Template and an Example Program for pyDraw

Now that we have pydraw installed, we can write a base program:

```python
from pydraw import *

screen = Screen(800, 600)

# code goes here!

screen.stop()
```

{% hint style="info" %}
Note that you could also replace line 7 with this to support animation:

```python
fps = 30
running = True
while running:
    screen.update()
    screen.sleep(1 / fps)
```

{% endhint %}

## Example Program

We can go ahead and add some shapes:

```python
from pydraw import *

screen = Screen(800, 600)

box = Rectangle(screen, 50, 50, 50, 50)
triangle = Triangle(screen, 150, 150, 50, 50)
circle = Oval(screen, 250, 250, 50, 50)
hexagon = Polygon(screen, 6, 350, 350, 50, 50)

screen.stop()
```

And the following output will be produced:

![Output for the Example Above](https://951329544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU0bfJb1iwSHS-dicLI%2F-MUK6EfMWSdMvuc1y97Y%2F-MUK7hVh6TKhFRnx4IaX%2Fimage.png?alt=media\&token=5754ecd2-fe98-4968-a8f4-d439d476d59a)


# Quickstart Examples

Some examples to quickly begin creating amazing things with pyDraw!

## Example #1: Drawing

We can draw a simple emoji onto the screen with Ovals. We will mimic: ":hushed:" with a gray background.

```python
from pydraw import *

screen = Screen(800, 600)
screen.color(Color('gray'))

face = Oval(screen, 100, 0, 600, 600, Color('yellow'))
face.wedges(40)

eye1 = Oval(screen, 200, 200, 85.71, 100)
eye2 = Oval(screen, 500, 200, 85.71, 100)

eyebrow1 = Rectangle(screen, 157.14, 150, 100, 5, rotation=-16)
eyebrow2 = Rectangle(screen, 517.85, 150, 100, 5, rotation=16)

mouth = Oval(screen, 350, 400, 85.71, face.height() / 109)

screen.stop()
```

![Example #1 Output](https://951329544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU0bfJb1iwSHS-dicLI%2F-MU90N0Z_e7MhNk4HvAb%2F-MU9U36agmmayxE_h-lh%2Fimage.png?alt=media\&token=21e59e62-4bde-403a-bd0d-41af0303db97)

{% hint style="info" %}
Note that we calculated those numbers using these formulas:

```python
eye1 = Oval(screen, face.x() + face.width() / 6, 200, face.width() / 7, face.height() / 6)
eye2 = Oval(screen, (face.x() + face.width()) - (face.width() / 6) * 2, 200, face.width() / 7, face.height() / 6)

eyebrow1 = Rectangle(screen, eye1.x() - eye1.width() / 2, 200 - 50, 100, 5, rotation=-16)
eyebrow2 = Rectangle(screen, eye2.x() - 25 + eye2.width() / 2, 200 - 50, 100, 5, rotation=16)
```

{% endhint %}

## Example #2: Animation

We can animate our face now by adding an animation loop and having our eyes blink every 3 seconds (note that we've now switched to the proportions-based calculations):

```python
from pydraw import *

screen = Screen(800, 600)
screen.color(Color('gray'))

face = Oval(screen, 100, 0, 600, 600, Color('yellow'))
face.wedges(40)

eye1 = Oval(screen, face.x() + face.width() / 6, 200, face.width() / 7, face.height() / 6)
eye2 = Oval(screen, (face.x() + face.width()) - (face.width() / 6) * 2, 200, face.width() / 7, face.height() / 6)

eyebrow1 = Rectangle(screen, eye1.x() - eye1.width() / 2, 200 - 50, 100, 5, rotation=-16)
eyebrow2 = Rectangle(screen, eye2.x() - 25 + eye2.width() / 2, 200 - 50, 100, 5, rotation=16)

mouth = Oval(screen, 400 - 50, 400, face.width() / 7, face.height() / 5.5)

count = 0
fps = 30
running = True
while running:
    seconds = count / fps

    if 1.9 < seconds < 2:
        eye1.height(1)
        eye2.height(1)
    if seconds >= 2.1:
        eye1.height(face.height() / 6)
        eye2.height(face.height() / 6)
        count = 0
    count += 1

    screen.update()
    screen.sleep(1 / fps)
```

![Example #2 Output](https://i.imgur.com/q15mZZR.gif)

{% hint style="info" %}
Notice how we replaced:

```python
screen.stop()
```

with *this* in order to create an animation loop:

```python
fps = 30
running = True
while running:
    screen.update()
    screen.sleep(1 / fps)
```

{% endhint %}

## Example #3: Input

Now we will move our emoji's mouth based on the mouse's position:

```python
from pydraw import *

screen = Screen(800, 600)
screen.color(Color('gray'))

face = Oval(screen, 100, 0, 600, 600, Color('yellow'))
face.wedges(40)

eye1 = Oval(screen, face.x() + face.width() / 6, 200, face.width() / 7, face.height() / 6)
eye2 = Oval(screen, (face.x() + face.width()) - (face.width() / 6) * 2, 200, face.width() / 7, face.height() / 6)

eyebrow1 = Rectangle(screen, eye1.x() - eye1.width() / 2, 200 - 50, 100, 5, rotation=-16)
eyebrow2 = Rectangle(screen, eye2.x() - 25 + eye2.width() / 2, 200 - 50, 100, 5, rotation=16)

mouth_default = Location(400 - 50, 400)
mouth = Oval(screen, 400 - 50, 400, face.width() / 7, face.height() / 5.5)

def mousemove(location):
    mouth.moveto(mouth_default.x() + (location.x() - mouth_default.x()) / 7,
                    mouth_default.y() + (location.y() - mouth_default.y()) / 7)

screen.listen()

count = 0
fps = 30
running = True
while running:
    seconds = count / fps

    if 1.9 < seconds < 2:
        eye1.height(1)
        eye2.height(1)
    if seconds >= 2.1:
        eye1.height(face.height() / 6)
        eye2.height(face.height() / 6)
        count = 0
    count += 1

    screen.update()
    screen.sleep(1 / fps)
```

![Example #3 Output](https://i.imgur.com/PouDHJe.gif)

{% hint style="info" %}
Notice how a new method was added:

```python
def mousemove(location):
    # code
```

And below that method we tell the screen to listen for input:

```python
screen.listen()
```

{% endhint %}


# Screen

The Screen is the base object for any pyDraw program, and can perform a variety of different tasks.

{% hint style="warning" %}
Quick Reference can help with simple issues or give you a basic understanding of the methods available, but it can never replace full documentation which is available [here](https://pydraw.graphics/api).
{% endhint %}

## Initialization

You can initialize a Screen by passing a width and height (in pixels), and you can also pass an optional title. The default title is: "pydraw".

```python
screen = Screen(800, 600)
screen = Screen(800, 600, 'Title')
```

## Title

The title may be set in the constructor, but can also be modified later with:

```python
screen.title(title)
```

## Size

The Screen maintains its width and height and is not resizable. The values are retrievable via:

```python
screen.width()
screen.height()
```

Also, you can resize the Screen manually with:

```python
screen.resize(width, height)
```

## Locations

The Screen contains some basic helper methods to quickly grab commonly used locations. You can access those locations via:

```python
screen.top_left()  # 0, 0
screen.top_right()  # width, 0
screen.center()  # width / 2, height / 2
screen.bottom_left()  # 0, height
screen.bottom_right()  # width, height
```

## Color

The Screen's background is white by default, but it can be modified (or retrieved) with:

```python
screen.color()  # Retrieve the current Color
screen.color(color)  # Set a new Color
```

{% hint style="info" %}
Note: Colors in pyDraw are wrapped with the [Color](/quick-reference/color) class.
{% endhint %}

## Background Image

You can also set the background image of the screen with:

```python
screen.picture(image_file)
```

## Input

You can have the screen listen for input by calling this after defining input methods:

```python
screen.listen()
```

#### Example

```python
def mousedown(location, button):
    # input handling code

screen.listen()  # Register any input methods
```

Check the dedicated page for more info:

{% content-ref url="/pages/-MUL\_myXUcPIbW07LwjB" %}
[Input](/quick-reference/input)
{% endcontent-ref %}

## Scene Change

You can create Scenes which serve essentially as boxed up pyDraw programs that can be applied to the Screen!

```python
screen.scene(scene)  # will apply scene and override all previous input hooks
```

Get more info on the dedicated page:

{% content-ref url="/pages/a3jwWoYk1FWmVqPUm7nP" %}
[Scene](/quick-reference/scene)
{% endcontent-ref %}

## Alert

You can pop an alert up onto the screen with:

```python
screen.alert(text)  # Default title and buttons
screen.alert(text, title)  # Custom title, default buttons
screen.alert(text, title, accept_text, cancel_text)  # Custom

# Returns True for "accept" and False for "cancel
result = alert('some text')
print(result)  # True or False
```

## Text Input (Prompt)

You can initiate a prompt to collect user input simply:

```python
screen.prompt(text)  # Ask a question
screen.prompt(text, title)  # Specify a title for the dialog
```

## Mouse Location

You can retrieve the mouse's current (or last known) location like so:

```python
screen.mouse()
```

## Screen Capture

It is possible to capture the contents of the Screen and write it to a specified image file:

```python
screen.grab()  # Just get an image and give it a random name
screen.grab(filename)  # Specify a filename to save to
```

## Updating and Clearing

You can update/clear the screen as expected:

```python
screen.update()  # Update the screen to display new changes
screen.clear()  # Clear all objects off the Screen
```

## Reset

You can reset the screen, which removes all objects and input hooks:

```python
screen.reset()
```

## Sleeping and Delay

The Screen has the ability to delay the program by a specified amount in seconds, but will also calculate a deltaTime in order to keep program execution delays as consistent as possible:

```python
screen.sleep(seconds)
```

{% hint style="info" %}
This method can only be used while within a while loop, if you want to normally delay a program, you should call:

```python
import time
time.sleep(seconds)
```

{% endhint %}

## Removing Objects

Although a method exists on all objects to remove themselves, the Screen is also able to remove objects from itself:

```python
screen.remove(obj)
```

## Objects List

You can retrieve a list of all active objects (not including grid and helpers):

```python
screen.objects()  # Returns a tuple (immutable list) of all objects.
```

## Check if an Object Exists

You can check if an object is on the screen:

```python
screen.contains(obj)
```

## Grids and Helpers

Screens have a fairly advanced grid system, allowing you to specify the number of rows or columns, or optionally the size of each cell (the default size of the cells will be 50x50):

```python
screen.grid(rows, cols)  # Specify # of rows and cols
screen.grid(cellsize=(50, 50))  # Specify the cellsize

screen.toggle_grid(state)  # Toggle the grid's visibility
```

You can also activate helper-labels for coordinates like so (every 100 pixels):

```python
screen.grid(rows, cols, helpers=True)  # Label coordinates
```

{% hint style="warning" %}
Be careful when creating or destroying grids, as it is a somewhat intensive process.
{% endhint %}


# Color

All Color values are wrapped in pyDraw as to make constructors and methods more clear. Colors are immutable, and can be created from names, RGB values, or a hex string.

## Initialization

You can create a Color by passing in one of three options:

* Name (string from Tkinter dataset)
* RGB (either a tuple or just three arguments, \[0-255])
* Hex (supports 3-char: '#fff' and 6-char: '#ffffff')

```python
color = Color('red')  # Get the red color
color = Color(255, 0, 0)  # Create red from RGB
color = Color('#ff0000')  # Create red from hex
```

## Retrieving Values

The RGB values are available no matter how the Color was initialized:

```python
color.red()  # Retrieve the red value (0-255)
color.green()  # Retrieve the green value (0-255)
color.blue()  # Retrieve the blue value (0-255)

color.rgb()  # Retrieve a tuple of all three color values

color.name()  # If initialized with a name, it is available
color.hex()  # If initialized with a hex, it is available
```

## Random Color and All Colors

You can retrieve a random Color via:

```python
Color.random()
```

Or you can generate it yourself by retrieving the full list of (named) Colors:

```python
Color.all()
```

{% hint style="info" %}
Note that these are *static* methods and are called on the Color class, rather than an instance.
{% endhint %}

{% tabs %}
{% tab title="How to view All Colors" %}
To view all named colors, please see the second tab.
{% endtab %}

{% tab title="All Colors" %}
'snow', 'ghost white', 'white smoke', 'gainsboro', 'floral white', 'old lace', 'linen', 'antique white', 'papaya whip', 'blanched almond', 'bisque', 'peach puff', 'navajo white', 'lemon chiffon', 'mint cream', 'azure', 'alice blue', 'lavender', 'lavender blush', 'misty rose', 'dark slate gray', 'dim gray', 'slate gray', 'light slate gray', 'gray', 'light grey', 'midnight blue', 'navy', 'cornflower blue', 'dark slate blue', 'slate blue', 'medium slate blue', 'light slate blue', 'medium blue', 'royal blue', 'blue', 'dodger blue', 'deep sky blue', 'sky blue', 'light sky blue', 'steel blue', 'light steel blue', 'light blue', 'powder blue', 'pale turquoise', 'dark turquoise', 'medium turquoise', 'turquoise', 'cyan', 'light cyan', 'cadet blue', 'medium aquamarine', 'aquamarine', 'dark green', 'dark olive green', 'dark sea green', 'sea green', 'medium sea green', 'light sea green', 'pale green', 'spring green', 'lawn green', 'medium spring green', 'green yellow', 'lime green', 'yellow green', 'forest green', 'olive drab', 'dark khaki', 'khaki', 'pale goldenrod', 'light goldenrod yellow', 'light yellow', 'yellow', 'gold', 'light goldenrod', 'goldenrod', 'dark goldenrod', 'rosy brown', 'indian red', 'saddle brown', 'sandy brown', 'dark salmon', 'salmon', 'light salmon', 'orange', 'dark orange', 'coral', 'light coral', 'tomato', 'orange red', 'red', 'hot pink', 'deep pink', 'pink', 'light pink', 'pale violet red', 'maroon', 'medium violet red', 'violet red', 'medium orchid', 'dark orchid', 'dark violet', 'blue violet', 'purple', 'medium purple', 'thistle', 'snow2', 'snow3', 'snow4', 'seashell2', 'seashell3', 'seashell4', 'AntiqueWhite1', 'AntiqueWhite2', 'AntiqueWhite3', 'AntiqueWhite4', 'bisque2', 'bisque3', 'bisque4', 'PeachPuff2', 'PeachPuff3', 'PeachPuff4', 'NavajoWhite2', 'NavajoWhite3', 'NavajoWhite4', 'LemonChiffon2', 'LemonChiffon3', 'LemonChiffon4', 'cornsilk2', 'cornsilk3', 'cornsilk4', 'ivory2', 'ivory3', 'ivory4', 'honeydew2', 'honeydew3', 'honeydew4', 'LavenderBlush2', 'LavenderBlush3', 'LavenderBlush4', 'MistyRose2', 'MistyRose3', 'MistyRose4', 'azure2', 'azure3', 'azure4', 'SlateBlue1', 'SlateBlue2', 'SlateBlue3', 'SlateBlue4', 'RoyalBlue1', 'RoyalBlue2', 'RoyalBlue3', 'RoyalBlue4', 'blue2', 'blue4', 'DodgerBlue2', 'DodgerBlue3', 'DodgerBlue4', 'SteelBlue1', 'SteelBlue2', 'SteelBlue3', 'SteelBlue4', 'DeepSkyBlue2', 'DeepSkyBlue3', 'DeepSkyBlue4', 'SkyBlue1', 'SkyBlue2', 'SkyBlue3', 'SkyBlue4', 'LightSkyBlue1', 'LightSkyBlue2', 'LightSkyBlue3', 'LightSkyBlue4', 'SlateGray1', 'SlateGray2', 'SlateGray3', 'SlateGray4', 'LightSteelBlue1', 'LightSteelBlue2', 'LightSteelBlue3', 'LightSteelBlue4', 'LightBlue1', 'LightBlue2', 'LightBlue3', 'LightBlue4', 'LightCyan2', 'LightCyan3', 'LightCyan4', 'PaleTurquoise1', 'PaleTurquoise2', 'PaleTurquoise3', 'PaleTurquoise4', 'CadetBlue1', 'CadetBlue2', 'CadetBlue3', 'CadetBlue4', 'turquoise1', 'turquoise2', 'turquoise3', 'turquoise4', 'cyan2', 'cyan3', 'cyan4', 'DarkSlateGray1', 'DarkSlateGray2', 'DarkSlateGray3', 'DarkSlateGray4', 'aquamarine2', 'aquamarine4', 'DarkSeaGreen1', 'DarkSeaGreen2', 'DarkSeaGreen3', 'DarkSeaGreen4', 'SeaGreen1', 'SeaGreen2', 'SeaGreen3', 'PaleGreen1', 'PaleGreen2', 'PaleGreen3', 'PaleGreen4', 'SpringGreen2', 'SpringGreen3', 'SpringGreen4', 'green2', 'green3', 'green4', 'chartreuse2', 'chartreuse3', 'chartreuse4', 'OliveDrab1', 'OliveDrab2', 'OliveDrab4', 'DarkOliveGreen1', 'DarkOliveGreen2', 'DarkOliveGreen3', 'DarkOliveGreen4', 'khaki1', 'khaki2', 'khaki3', 'khaki4', 'LightGoldenrod1', 'LightGoldenrod2', 'LightGoldenrod3', 'LightGoldenrod4', 'LightYellow2', 'LightYellow3', 'LightYellow4', 'yellow2', 'yellow3', 'yellow4', 'gold2', 'gold3', 'gold4', 'goldenrod1', 'goldenrod2', 'goldenrod3', 'goldenrod4', 'DarkGoldenrod1', 'DarkGoldenrod2', 'DarkGoldenrod3', 'DarkGoldenrod4', 'RosyBrown1', 'RosyBrown2', 'RosyBrown3', 'RosyBrown4', 'IndianRed1', 'IndianRed2', 'IndianRed3', 'IndianRed4', 'sienna1', 'sienna2', 'sienna3', 'sienna4', 'burlywood1', 'burlywood2', 'burlywood3', 'burlywood4', 'wheat1', 'wheat2', 'wheat3', 'wheat4', 'tan1', 'tan2', 'tan4', 'chocolate1', 'chocolate2', 'chocolate3', 'firebrick1', 'firebrick2', 'firebrick3', 'firebrick4', 'brown1', 'brown2', 'brown3', 'brown4', 'salmon1', 'salmon2', 'salmon3', 'salmon4', 'LightSalmon2', 'LightSalmon3', 'LightSalmon4', 'orange2', 'orange3', 'orange4', 'DarkOrange1', 'DarkOrange2', 'DarkOrange3', 'DarkOrange4', 'coral1', 'coral2', 'coral3', 'coral4', 'tomato2', 'tomato3', 'tomato4', 'OrangeRed2', 'OrangeRed3', 'OrangeRed4', 'red2', 'red3', 'red4', 'DeepPink2', 'DeepPink3', 'DeepPink4', 'HotPink1', 'HotPink2', 'HotPink3', 'HotPink4', 'pink1', 'pink2', 'pink3', 'pink4', 'LightPink1', 'LightPink2', 'LightPink3', 'LightPink4', 'PaleVioletRed1', 'PaleVioletRed2', 'PaleVioletRed3', 'PaleVioletRed4', 'maroon1', 'maroon2', 'maroon3', 'maroon4', 'VioletRed1', 'VioletRed2', 'VioletRed3', 'VioletRed4', 'magenta2', 'magenta3', 'magenta4', 'orchid1', 'orchid2', 'orchid3', 'orchid4', 'plum1', 'plum2', 'plum3', 'plum4', 'MediumOrchid1', 'MediumOrchid2', 'MediumOrchid3', 'MediumOrchid4', 'DarkOrchid1', 'DarkOrchid2', 'DarkOrchid3', 'DarkOrchid4', 'purple1', 'purple2', 'purple3', 'purple4', 'MediumPurple1', 'MediumPurple2', 'MediumPurple3', 'MediumPurple4', 'thistle1', 'thistle2', 'thistle3', 'thistle4', 'gray1', 'gray2', 'gray3', 'gray4', 'gray5', 'gray6', 'gray7', 'gray8', 'gray9', 'gray10', 'gray11', 'gray12', 'gray13', 'gray14', 'gray15', 'gray16', 'gray17', 'gray18', 'gray19', 'gray20', 'gray21', 'gray22', 'gray23', 'gray24', 'gray25', 'gray26', 'gray27', 'gray28', 'gray29', 'gray30', 'gray31', 'gray32', 'gray33', 'gray34', 'gray35', 'gray36', 'gray37', 'gray38', 'gray39', 'gray40', 'gray42', 'gray43', 'gray44', 'gray45', 'gray46', 'gray47', 'gray48', 'gray49', 'gray50', 'gray51', 'gray52', 'gray53', 'gray54', 'gray55', 'gray56', 'gray57', 'gray58', 'gray59', 'gray60', 'gray61', 'gray62', 'gray63', 'gray64', 'gray65', 'gray66', 'gray67', 'gray68', 'gray69', 'gray70', 'gray71', 'gray72', 'gray73', 'gray74', 'gray75', 'gray76', 'gray77', 'gray78', 'gray79', 'gray80', 'gray81', 'gray82', 'gray83', 'gray84', 'gray85', 'gray86', 'gray87', 'gray88', 'gray89', 'gray90', 'gray91', 'gray92', 'gray93', 'gray94', 'gray95', 'gray97', 'gray98', 'gray99'
{% endtab %}
{% endtabs %}

## Cloning

You can easily clone a color (like most objects in pyDraw) as:

```python
color.clone()
```

## Advanced

{% hint style="info" %}
[🚀](https://emojipedia.org/rocket/) Experienced Only: You can retrieve a "turtle-friendly" version of the Color with:

```python
color.__value__()
```

{% endhint %}


# Location

All Locations in pyDraw are wrapped with this class, however some methods and constructors will automatically convert tuples to Locations automatically.

## Initialization

Locations can be initialized with two coordinates, a tuple, another location, or by specifying only one coordinate (such as x or y), defaulting the other coordinate's value to zero.

```python
location = Location(0, 0)  # Two arguments
location = Location((200, 100))  # A single tuple
location = Location(another_location)  # Copy constructor

location = Location(x=100)  # Create a location at (100, 0)
```

## Values

There is a variety of ways to retrieve the values from a Location instance:

```python
location.x()  # Get the x-coordinate
location.y()  # Get the y-coordinate
```

Locations are also readable as tuples:

```python
location[0]  # Get the x-coordinate
location[1]  # Get the y-coordinate
```

## Changing Locations

The methods above can also be used to modify coordinates:

```python
location.x(new_x)  # Set a new x-coordinate value
location.y(new_y)  # Set a new y-coordinate value
```

You can also call the handy `.move()` and `.moveto()` methods:

```python
location.move(dx, dy)  # Move the location by passed values
location.move((dx, dy))  # You can also pass in tuples!
location.move(location)  # Not sure why you'd need this :P
location.move(dx=100)  # Only move the x-coordinate by +100

location.moveto(x, y)  # Move to a new position
location.moveto((x, y))  # Pass in tuple
location.moveto(location)  # Move to the same position as another Location instance
location.moveto(y=100)  # Change the y-coordinate to 100
```

## Advanced

{% hint style="info" %}
It is possible to convert to turtle-based coordinates by calling:

```python
turtle_location = screen.create_location(location)
```

Or to convert to Tkinter:

```python
tkinter_location = screen.canvas_location(location)
```

{% endhint %}


# Renderable

Renderable serves as the base class for any object with a width and height (most notably excluding lines and dots). The Renderable class does extend the Object class which tracks location and updates.

{% hint style="warning" %}
Quick Reference can help with simple issues or give you a basic understanding of the methods available, but it can never replace full documentation which is available [here](https://pydraw.graphics/api).
{% endhint %}

## Initialization

We can initialize the default 3 shapes by calling their respective constructors:

```python
rectangle = Rectangle(screen, x, y, width, height)
oval = Oval(screen, x, y, width, height)
triangle = Triangle(screen, x, y, width, height)
```

The constructor has more arguments that are optional:

```python
Renderable(screen, x, y, width, height, color, border, fill, rotation, visible)
```

{% hint style="info" %}
It's crucial to note that all Renderables are rendered from the top-left. So the passed (x, y) for a Rectangle would be its top left corner.
{% endhint %}

## Types

There are a few different Renderables that can be created:

```python
rectangle = Rectangle(screen, x, y, width, height)
oval = Oval(screen, x, y, width, height)
triangle = Triangle(screen, x, y, width, height)

# Note that for Polygon we specify num_sides before (x, y)
polygon = Polygon(screen, num_sides, x, y, width, height)

# We can create an irregular polygon by specifying vertices
irregular = CustomPolygon(screen, vertices, color, border, fill, rotation, visible)
```

{% hint style="info" %}
Although Text is classified as a Renderable, it is not directly resizable and has specific methods unique to Text: [Reference](/quick-reference/text).
{% endhint %}

## Movement

Moving any Renderable is alike to moving a Location:

```python
renderable.x(new_x)  # Get or set the x-coordinate
renderable.y(new_y)  # Get or set the y-coordinate

renderable.move(dx, dy)  # Move by (dx, dy)
renderable.move((dx, dy))  # Tuple representation of (dx, dy)
renderable.move(dx=100)  # Move the x-coordinate by +100

renderable.moveto(x, y)  # Move to (x, y)
renderable.moveto((x, y))  # Tuple representation of (x, y)
renderable.moveto(y=100)  # Move the y-coordinate to +100
```

We can also make a Renderable move forward at its current heading/angle via:

```python
renderable.forward(distance)  # Move forward at current angle by distance
renderable.backward(distance)  # Move backward at current angle by distance
```

{% hint style="info" %}
Note that these methods utilize the Renderables [rotation](/quick-reference/renderable#rotation).
{% endhint %}

## Location

You can also retrieve the Location with:

```python
renderable.location()
```

## Center

You can get the Location of the center of any Renderable:

```python
renderable.center()
```

{% hint style="info" %}
Note: This calculates the center by default (except for CustomPolygons), but you can get the [*centroid*](https://en.wikipedia.org/wiki/Centroid) of the shape by setting `centroid=True` in the method arguments.
{% endhint %}

You can also use this method to move the Renderable to place its center in a certain location:

```python
renderable.center(x, y)
renderable.center(location)
renderable.center((x, y))
```

## Rotation

You can get or modify the rotation of a Renderable like so:

```python
renderable.rotation()  # Get the current angle
renderable.rotation(angle)  # Set a new angle of rotation

renderable.rotate(angle_change)  # Change the angle by a specified argument
```

You can also just make a Renderable look at a Location or Renderable:

```python
renderable.lookat(other)  # Look at another Renderable
renderable.lookat(location)  # Look at a Location
```

You can also check the angle of the Renderable against any Object or Location:

```python
renderable.angleto(obj)
renderable.angleto(location)
renderable.angleto((x, y))
```

## Size

You can retrieve or modify the size of the Renderable like so:

```python
renderable.width()  # Get the current width
renderable.width(width)  # Modify the width
renderable.width(width, ratio=True)  # Maintain the ratio of the Renderable

erable.height()  # Get the current height
renderable.height(height)  # Modify the height
renderable.height(height, ratio=True)  # Maintain the ratio of the Renderable
```

{% hint style="info" %}
Note: Width and Height refer to the width and height of the original shape, regardless of rotation.
{% endhint %}

## Color

All Renderables have a default Color of black; the color can be retrieved or set via:

```python
renderable.color()  # Get the color
renderable.color(color)  # Set a new color
```

## Border and Fill

Renderables also have an optional border that is set to Color.NONE by default. You can set or retrieve the border like so:

```python
renderable.border()  # Get the border's color. If none is set, returns Color.NONE
renderable.border(color)  # Set a new color for the border
renderable.border(color, width=5)  # Set a new color and a borderwidth
renderable.border(color, fill=False)  # Set a new color and disable the fill
```

You can get or set the `border_width` seperately with:

```python
renderable.border_width()  # returns border width
renderable.border_width(5)  # sets to 5
```

Fill exists (as seen above) to create Framed Renderables with ease. Fill can be toggled without calling the `border()` method like so:

```python
renderable.fill(False)  # Change the fill to False.
```

## Visibility

You can make any Object in pyDraw invisible with:

```python
renderable.visible(False)  # Make the Object invisible
```

## Ordering

You can move objects to the front or back of layers with:

```python
renderable.front()  # Move to the front
renderable.back()  # Move to the back
```

## Distance

You can get the distance between a Renderable and another Renderable, or Location like so:

```python
renderable.distance(other)  # Pass in another renderable
renderable.distance(location)  # Pass in a Location
```

## Transform and Cloning

A transform is a data structure that represents the width, height, and rotation. You can copy the transform of a Renderable and set it to another transform:

```python
renderable.transform()  # Retrieve the transform
renderable.transform(transform)  # Set a new transform
```

{% hint style="warning" %}
You should only set the transform to other transforms retrieved from Renderables, however, it is possible to create one yourself:

```python
transform = (width, height, angle);
```

{% endhint %}

You can also clone a Renderable by calling the aptly named:

```python
renderable.clone()
```

## Vertices

For those who want to perform more advanced mathematics with their shapes, you can retrieve a (copy) list of vertices:

```python
renderable.vertices()
```

{% hint style="info" %}
Vertices usually will begin at the top left and work clockwise.
{% endhint %}

## Bounds

You can get the location and dimensions of a bounding box calculated by pyDraw around any Renderable:

```python
renderable.bounds()  # returns (Location, width, height)
```

## Contains and Overlaps

You can check if a point is contained in any Renderable like so:

```python
renderable.contains(location)  # Pass in a normal location
renderable.contains(x, y)  # Or you can specify x and y
renderable.contains((x, y))  # Or you can pass in a tuple
```

Or you can check if two Renderables are overlapping:

```python
renderable.overlaps(other)
```


# Text

A brief reference to the Text class.

{% hint style="info" %}
Text extends the Renderable class but has some unique methods that are listed below.

[Renderable Quick Reference](/quick-reference/renderable)\
[Text Documentation](https://pydraw.graphics/api/pydraw.html#pydraw.objects.Text)
{% endhint %}

## Initialization

Text can be initialized like this:

```python
text = Text(screen, text, x, y)
```

{% hint style="info" %}
There an extended constructor that contains most attributes, check the full documentation: [Text](https://pydraw.graphics/pydraw.html?highlight=text#pydraw.objects.Text)
{% endhint %}

## Text

You can get or change the text displayed with:

```python
text.text()  # Get the current text
text.text('new string!')  # Set new text
```

## Dimensions

pyDraw will automatically calculate a width and height once you've created a Text object (or when you update it's attributes), so you can get those values like so:

```python
text.width()
text.height()
```

## Font

You can access the font of the text with:

```python
text.font()  # The current font
text.font(font)  # Set a new font
```

## Size

You can also modify the font-size as expected:

```python
text.size()  # Retrieve the current font-size
text.size(16)  # Set a new font-size (~height in pixels)
```

{% hint style="warning" %}
The `width()` and `height()` methods will only return the height of the text created as they are determined by text, font, and font-size, and cannot be set.
{% endhint %}

## Align

You can specify an alignment for cases where multiple lines of text are given:

Possible Values (string):

* left
* center
* right

```python
text.align(alignment)
```

## Decorations

You can decorate text with the following methods:

```python
text.bold(True)
text.italic(True)
text.underline(True)
text.strikethrough(True)
```

##

##

##


# Line

A brief reference to the Line class.

{% hint style="info" %}
Line contains some methods also found in [Renderable](/quick-reference/renderable), but is not a Renderable, as it does not possess a width and height.
{% endhint %}

## Initialization

You can initialize a Line in a few different ways:

```python
line = Line(screen, x1, y1, x2, y2)  # Pass two points
line = Line(screen, (x1, y1), (x2, y2))  # Two points as tuples
line = Line(screen, location1, location2)  # Two points as Locations
```

## Movement

You can move the Line using slightly modified methods from Object:

We can translate by a specified amount with:

```python
line.move(dx, dy)  # Move both points by dx, dy
line.move(dx, dy, point=1)  # Move only the first point by dx, dy
```

We can move both positions to new locations with:

```python
line.moveto(x1, y1, x2, y2)
line.moveto((x1, y1), (x2, y2))
line.moveto(location1, location2)
```

We can change only one of the positions with either:

```python
line.pos1(location)
```

```python
line.pos2(location)
```

{% hint style="info" %}
Most methods in Line are able to take numbers, tuples, or Locations because Lines deal with more coordinates than Renderables.
{% endhint %}

## Location

Retrieve both positions in a tuple using the familiar:

```python
line.location()  # Returns both endpoints positions as a tuple (pos1, pos2)
```

## Rotation

You can rotate lines just as you would expect:

```python
line.rotation()  # Get the current rotation
line.rotation(rotation)  # Set a new angle
```

```python
line.rotate(angle_change)  # Change angle by angle_change
```

Or we can pass a location in for the line to look at:

```python
line.lookat(location)  # Look at the passed location (moves second endpoint)
line.lookat(location, point=1)  # Move the first point instead
```

## Color

You can change the Color of a Line just like a Renderable:

```python
line.color()  # Retrieve the current Color
line.color(color)  # Set a new Color
```

## Thickness

You can modify the thickness of the line in pixels with:

```python
line.thickness(thickness)
```

## Dashes

You may decide to change the line to a dotted line with:

```python
line.dashes(3)  # Add dashes 3px in length with 3px between them
line.dashes((3, 2))  # Specify dash length and distance separately.
```

## Length

You can get the length of the line by calling:

```python
line.length()
```

## Visibility

Visibility is accessed exactly as it is in Renderable:

```python
line.visible(False)
```

## Transform and Cloning

Transforms and cloning work as in Renderable:

```python
line.transform()  # (pos1, pos2, angle)
```

{% hint style="warning" %}
Line's transform is a tuple with both positions and current angle.
{% endhint %}

Cloning works as expected:

```python
line2 = line.clone()  # new line that's the same!
```

## Intersects (Overlaps)

The Line's version of [Renderable](/quick-reference/renderable#contains-and-overlaps)'s `overlaps()` is `intersects()`. It can take any Renderable or Line and will check if the line intersects with any of their lines.

```python
line.intersects(line2)
line.intersects(rectangle)
```


# Image

A brief reference to the Image class.

{% hint style="info" %}
Image is a Renderable with some methods modified, add, or removed.

[Renderable Quick Reference](/quick-reference/renderable)\
[Image Documentation](https://pydraw.graphics/api/pydraw.html#pydraw.objects.Image)
{% endhint %}

{% hint style="warning" %}
If you have pyDraw installed as the library file in your project directory, you must ensure '[Pillow](https://pillow.readthedocs.io/en/stable/installation.html)' is installed, or you won't be able to resize the image, modify any image attributes, or use image types other than PNG, GIF, and PPM.
{% endhint %}

## Initialization

You can create an image with a similar constructor:

*(Supported image types: PNG, GIF, JPG, and PPM)*

```python
image = Image(screen, image_file, x, y, width, height)
```

{% hint style="info" %}
Again, if you are not using the PIP version of pyDraw, you may need to install '[Pillow](https://pillow.readthedocs.io/en/stable/installation.html)' before passing the width and height into the constructor.
{% endhint %}

## Color

It is still possible to call the `color()` method and colorize the image:

```python
image.color(color)  # Tints the image by the passed Color
```

## Flip

You can flip the image across the X or Y axes ([the plural of axis 😜](https://www.grammar-monster.com/plurals/plural_of_axis.htm)):

```python
image.flip(axis)  # accepts 'x' or 'y'
```

## Animation (GIF)

It is possible to access the individual frames of an animated GIF file by first calling this after initialization:

```python
image.load()
```

Then you can access individual frames via:

```python
image.frame()  # Get the current frame
image.frame(frame)  # Set the current frame
```

Or you can just push the frame forward by one with:

```python
image.next()  # Goes to next frame.
```

{% hint style="info" %}
`next()` will automatically loop back to index 0 when it reaches the last frame.
{% endhint %}

The total number of frames is available too:

```python
image.frames()  # Returns integer number of frames
```


# Input

The Quick Reference for Input just lists all methods available to be registered for input.

{% hint style="info" %}
Remember: In order to register input methods, [`screen.listen()`](/quick-reference/screen#input) must be called after your methods in your program!
{% endhint %}

## All Input Methods

* `mousedown` | When the mouse is pressed or is held, this will be called
* `mouseup`     | When the mouse is released this is called
* `mousedrag` | When the mouse is dragged, this is called anytime the mouse moves and is held.
* `mousemove` | When the mouse moves, this method is called.
* `keydown`     | When a key is pressed or is currently being held, this is called
* `keyup`         | When a key is released this is called
* `exit`           | Called when the Screen is abruptly closed or [`screen.exit()`](/quick-reference/screen) is called.

## Example

```python
from pydraw import *

screen = Screen(800, 600, 'Input Example')

box = Rectangle(screen, 375, 275, 50, 50, Color('gray17'))

# another cool thing to know is the 'button' parameter is optional!
def mousedown(location, button):
    box.color(Color.random())


def mouseup(location, button):
    box.rotate(1)


def mousedrag(location, button):
    box.border(Color.random())


def mousemove(location):
    box.lookat(location)


def keydown(key):
    if key == 'w':
        box.move(dy=-5)
    elif key == 's':
        box.move(dy=5)
    if key == 'a':
        box.move(dx=-5)
    elif key == 'd':
        box.move(dx=5)


def keyup(key):
    if key == 'v':
        box.clone()


screen.listen()

fps = 30
running = True
while running:
    screen.update()
    screen.sleep(1 / fps)
```


# Scene

Create containerized pyDraw programs and apply them to the Screen independently!

## Initialization

You can get started with Scenes by creating a new class that extends the base Scene class from pyDraw:

```python
import * from pydraw

class MyScene(Scene):
    # you should declare your variables outside of any of the methods (static!)
    some_variable = 34
    
    some_shape = None  # set pyDraw objects or uninitiated variables to None
    to_set_later = None
    
    # now we can define a "start" method which will run when the scene is displayed
    def start(self):
        self.some_shape = Rectangle(self.screen(), 10, 10, 75, 50, Color('red'))
        self.to_set_later = self.some_shape.width() / 2
    
    # you can setup the Scene's input hooks by defining the methods in the class
    def keydown(self, key):
        if key == 'x':
            self.to_set_later += 1
        else:
            self.some_shape.move(x=3)
    
    def keyup(self, key):
        if key == 'x':
            self.to_set_later += self.to_set_later % 3
    
    def mousedown(self, location):
        print(location)
        
    # you won't even need to call screen.listen(), it happens automatically!
    
    # next up we setup a "run" method which happens after our input methods!
    def run():
        # as you can see, it's just like a normal pyDraw program!
    
        running = True
        fps = 30
        while running:
            self.some_shape.color(Color.random())
        
            self.screen().update()
            self.screen().sleep(1 / fps)
```

## Methods

### Start

Should instantiate your variables and prepare everything that the input hooks will use (unless you have checks in your hooks)

### Run

Should contain your actual loop and main program logic.

### Screen

Get the Screen that the Scene is tied to! (can throw errors if methods are being called without being attached to a Screen)

### Input Methods

Supports all the same input methods that any other pyDraw program supports:

* `mousedown` | When the mouse is pressed or is held, this will be called
* `mouseup`     | When the mouse is released this is called
* `mousedrag` | When the mouse is dragged, this is called anytime the mouse moves and is held.
* `mousemove` | When the mouse moves, this method is called.
* `keydown`     | When a key is pressed or is currently being held, this is called
* `keyup`         | When a key is released this is called
* `exit`           | Called when the Screen is abruptly closed or [`screen.exit()`](/quick-reference/screen) is called.


