diff --git a/.github/workflows/pypen_ci.yml b/.github/workflows/pypen_ci.yml
index 8b81005..84e03de 100644
--- a/.github/workflows/pypen_ci.yml
+++ b/.github/workflows/pypen_ci.yml
@@ -2,11 +2,7 @@ name: PyPen CI
on:
push:
- branches:
- - master
pull_request:
- branches:
- - master
jobs:
test:
diff --git a/README.md b/README.md
index f9a4e41..613341b 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,20 @@ TL;DR:
3. type ```pypen example``` and a window should popup with something looking like this:
4. edit the example.py file by checking out PyPen's documentation and [examples](./examples/)
+## Inspiration from our Examples:
+
+| Example 002 | Example 003 |
+|:-----------:|:-----------:|
+|![Example 002 Gif](./examples/gifs/002.gif)|![Example 003 Gif](./examples/gifs/003.gif)|
+
+| Example 006 | Example 007 |
+|:-----------:|:-----------:|
+|![Example 006 Gif](./examples/gifs/006.gif)|![Example 007 Gif](./examples/gifs/007.gif)|
+
+| Example 010 | Example 011 |
+|:-----------:|:-----------:|
+|![Example 010 Gif](./examples/gifs/010.gif)|![Example 011 Gif](./examples/gifs/011.gif)|
+
## _(For Maintainers)_ How does PyPen work?
We have an entire page dedicated to explaining the inner workings of PyPen. If you are a developer interested in helping PyPen's development, you should check it out: [How does PyPen _Really_ Work?](https://pypen.readthedocs.io/en/latest/behind_the_scenes.html#behind-the-scenes)
diff --git a/examples/example_007.py b/examples/example_007.py
index 95a3d3e..640668d 100644
--- a/examples/example_007.py
+++ b/examples/example_007.py
@@ -13,6 +13,7 @@ def __init__(self, x, y):
self.secondary_color = "#565656"
def update(self):
+ reset_style()
save()
translate(self.x, self.y)
rotate(TIME)
@@ -40,7 +41,7 @@ def rectangle(self):
def circle(self):
circle(0, 0, self.width/2, self.color)
if self.variation > 0.6:
- arc(0, 0, self.width/2, 0, PI, self.secondary_color)
+ arc(0, 0, self.width/2, 0, PI, None, self.secondary_color, 2)
def start():
diff --git a/examples/example_008.py b/examples/example_008.py
index d8260ed..ce1e7a6 100644
--- a/examples/example_008.py
+++ b/examples/example_008.py
@@ -9,19 +9,19 @@ def update():
fill_screen("orange")
save()
- translate(60,60)
+ translate(60, 60)
rotate(T*2.5)
ellipse(0, 0, 50, 30, "red", "blue", 3)
restore()
save()
- translate(200,200)
+ translate(200, 200)
rotate(T*1.5+0.5)
ellipse(40*sin(T), 0, 30, 50, stroke_width=10)
restore()
-
+
save()
- translate(400,300)
+ translate(400, 300)
rotate(T)
ellipse(0, 100*cos(T*2), 30, 100, "green")
- restore()
\ No newline at end of file
+ restore()
diff --git a/examples/example_009.py b/examples/example_009.py
new file mode 100644
index 0000000..667c06f
--- /dev/null
+++ b/examples/example_009.py
@@ -0,0 +1,42 @@
+from pypen import *
+
+
+def start():
+ settings.fps = 60
+
+
+def update():
+ fill_screen("#343434")
+
+ begin_shape()
+ vertex(40, 40)
+ vertex(40, 140)
+ vertex(FRAME, 140)
+ end_shape("red")
+
+ begin_shape()
+ vertex(40+FRAME, 240+FRAME)
+ vertex(40, 340)
+ vertex(FRAME, 340)
+ vertex(340, 280)
+
+ end_shape("yellow", "blue", 5)
+
+ reset_style()
+
+ angle = 0
+ points = 10 + sin(TIME*2)*5
+ radius = 100
+
+ save()
+ translate(WIDTH/2, HEIGHT/2)
+
+ begin_shape()
+
+ while angle <= TAU:
+ angle += TAU/points
+ vertex(sin(angle)*radius, cos(angle)*radius)
+
+ end_shape("orange")
+ restore()
+ reset_style()
diff --git a/examples/example_010.py b/examples/example_010.py
new file mode 100644
index 0000000..efd6d50
--- /dev/null
+++ b/examples/example_010.py
@@ -0,0 +1,19 @@
+from pypen import *
+
+
+def start():
+ settings.fps = 60
+
+def update():
+ save()
+ translate(WIDTH/2, HEIGHT/2)
+
+ x1 = 200 * cos(TIME - 10 * sin(TIME)) * cos(TIME)
+ y1 = 200 * sin(TIME - FRAME/10) * sin(TIME)
+
+ x2 = 200 * cos(TIME + FRAME ** 0.5) * cos(TIME-0.5)
+ y2 = 200 * sin(TIME + FRAME/10 - 10*cos(TIME/10)) * sin(TIME - 0.5)
+
+ line(x1, y1, x2, y2, (150, 30, 230, 10), 0.2)
+
+ restore()
diff --git a/examples/example_011.py b/examples/example_011.py
new file mode 100644
index 0000000..3ca8b8a
--- /dev/null
+++ b/examples/example_011.py
@@ -0,0 +1,22 @@
+from pypen import *
+
+
+def pseudo_random(seed: float):
+ return abs(sin(seed % 478562)*cos(seed % 3579))
+
+def update():
+ clear_screen()
+
+ i = 0
+ offset_y = TIME*170
+
+ for x, y in grid(spacing=30):
+ seed = i+x*y
+
+ actual_y = (y + offset_y) % HEIGHT - 30
+ percentage = (1-(actual_y/HEIGHT)**5)
+ color = (80, 130, 50, pseudo_random(seed)*255*percentage)
+
+ circle(x, actual_y, pseudo_random(seed)*14*percentage, color)
+
+ i += 1
diff --git a/examples/gifs/002.gif b/examples/gifs/002.gif
new file mode 100644
index 0000000..8de0b85
Binary files /dev/null and b/examples/gifs/002.gif differ
diff --git a/examples/gifs/003.gif b/examples/gifs/003.gif
new file mode 100644
index 0000000..a2a686d
Binary files /dev/null and b/examples/gifs/003.gif differ
diff --git a/examples/gifs/006.gif b/examples/gifs/006.gif
new file mode 100644
index 0000000..e5cb686
Binary files /dev/null and b/examples/gifs/006.gif differ
diff --git a/examples/gifs/007.gif b/examples/gifs/007.gif
new file mode 100644
index 0000000..3b938a2
Binary files /dev/null and b/examples/gifs/007.gif differ
diff --git a/examples/gifs/010.gif b/examples/gifs/010.gif
new file mode 100644
index 0000000..b7275cc
Binary files /dev/null and b/examples/gifs/010.gif differ
diff --git a/examples/gifs/011.gif b/examples/gifs/011.gif
new file mode 100644
index 0000000..a98aa08
Binary files /dev/null and b/examples/gifs/011.gif differ
diff --git a/pypen/drawing/__init__.py b/pypen/drawing/__init__.py
index 0501720..edf3ede 100644
--- a/pypen/drawing/__init__.py
+++ b/pypen/drawing/__init__.py
@@ -8,4 +8,4 @@
from pypen.drawing.pypen_window import PyPenWindow
# Primitives used for intellisense and documentation
-from pypen.drawing.fake_primitives import *
+from pypen.drawing.fake_pypen import *
diff --git a/pypen/drawing/fake_primitives.py b/pypen/drawing/fake_primitives.py
index 757f68e..3988413 100644
--- a/pypen/drawing/fake_primitives.py
+++ b/pypen/drawing/fake_primitives.py
@@ -1,4 +1,33 @@
+def rotate(angle):
+ """Rotates the context"""
+ raise NotImplementedError("rotate() is not implemented")
+
+
+def translate(x, y):
+ """Translates the context by x and y"""
+ raise NotImplementedError("translate() is not implemented")
+
+
+def scale(factor):
+ """Scales the context by the provided factor"""
+ raise NotImplementedError("scale() is not implemented")
+
+
+def save():
+ """Saves the current context's translation, rotation and scaling"""
+ raise NotImplementedError("save() is not implemented")
+
+
+def restore():
+ """Restores the context's translation, rotation and scaling to that of the latest save"""
+ raise NotImplementedError("restore() is not implemented")
+
+
+def reset_style():
+ """Resets PyPen's current setting surrounding style to their default_values, which includes fill_color, stroke_color, stroke_width"""
+ raise NotImplementedError("reset_style() is not implemented")
+
def clear_screen():
"""Clears the screen"""
@@ -20,6 +49,21 @@ def fill():
raise NotImplementedError("fill() not implemented")
+def begin_shape():
+ """Tells PyPen that a shape is a bout to be created"""
+ raise NotImplementedError("begin_shape() not implemented")
+
+
+def vertex(x, y):
+ """Adds a vertex to current shape at (x, y)"""
+ raise NotImplementedError("vertex() not implemented")
+
+
+def end_shape(fill_color="", stroke_color="", stroke_width=-1):
+ """Ends shape and styles it"""
+ raise NotImplementedError("end_shape() not implemented")
+
+
def rectangle(x, y, width, height, fill_color="", stroke_color="", stroke_width=-1):
"""Draws a rectangle on the given coordinate with the given width, height and color"""
raise NotImplementedError("rectangle() not implemented")
@@ -39,33 +83,10 @@ def arc(x, y, radius, start_angle, stop_angle, fill_color="", stroke_color="", s
"""Draws an arc on the given coordinate with the given radius, angles and color"""
raise NotImplementedError("arc() not implemented")
+def triangle(x1, y1, x2, y2, x3, y3, fill_color="", stroke_color="", stroke_width=-1):
+ """Draws a triangle between the supplied coordinates with the given color"""
+ raise NotImplementedError("triangle() not implemented")
-def rotate(angle):
- """Rotates the context"""
- raise NotImplementedError("rotate() is not implemented")
-
-
-def translate(x, y):
- """Translates the context by x and y"""
- raise NotImplementedError("translate() is not implemented")
-
-
-def scale(factor):
- """Scales the context by the provided factor"""
- raise NotImplementedError("scale() is not implemented")
-
-
-def save():
- """Saves the current context's translation, rotation and scaling"""
- raise NotImplementedError("save() is not implemented")
-
-
-def restore():
- """Restores the context's translation, rotation and scaling to that of the latest save"""
- raise NotImplementedError("restore() is not implemented")
-
-
-def reset_style():
- """Resets PyPen's current setting surrounding style to their default_values, which includes fill_color, stroke_color, stroke_width"""
- raise NotImplementedError("reset_style() is not implemented")
-
+def line(x1, y1, x2, y2, stroke_color="", stroke_width=-1):
+ """Draws a line between the supplied coordinates with the given stroke_color and stroke_width"""
+ raise NotImplementedError("triangle() not implemented")
diff --git a/pypen/drawing/fake_pypen.py b/pypen/drawing/fake_pypen.py
new file mode 100644
index 0000000..cdfa756
--- /dev/null
+++ b/pypen/drawing/fake_pypen.py
@@ -0,0 +1,126 @@
+
+def rotate(angle):
+ """Rotates the context.
+
+ Args:
+ angle (float): The amount by which the context should be rotated.
+ """
+ raise NotImplementedError("rotate() is not implemented")
+
+
+def translate(x, y):
+ """Translates the context by x and y.
+
+ Args:
+ x (float): Horizontal coordinate.
+ y (float): Vertical coordinate.
+ """
+ raise NotImplementedError("translate() is not implemented")
+
+
+def scale(factor):
+ """Scales the context by the provided factor
+
+ Args:
+ factor (float): The amount by which the current context should be scaled.
+ """
+ raise NotImplementedError("scale() is not implemented")
+
+
+def save():
+ """Saves the current context's translation, rotation and scaling"""
+ raise NotImplementedError("save() is not implemented")
+
+
+def restore():
+ """Restores the context's translation, rotation and scaling to that of the latest save"""
+ raise NotImplementedError("restore() is not implemented")
+
+
+def reset_style():
+ """Resets PyPen's current setting surrounding style to their default_values, which includes fill_color, stroke_color, stroke_width"""
+ raise NotImplementedError("reset_style() is not implemented")
+
+
+def clear_screen():
+ """Clears the screen"""
+ raise NotImplementedError("clear_screen() not implemented")
+
+
+def clear():
+ """Clears the screen"""
+ raise NotImplementedError("clear() not implemented")
+
+
+def fill_screen(fill_color):
+ """Fills the screen with the specified color
+
+ Args:
+ fill_color (Color): The color by which to fill the screen. Defaults to the theme's default background color.
+ """
+ raise NotImplementedError("fill_screen() not implemented")
+
+
+def fill():
+ """Fills the current path. Different from fill_screen."""
+ raise NotImplementedError("fill() not implemented")
+
+
+def rectangle(x, y, width, height, fill_color, stroke_color, stroke_width):
+ """Draws a rectangle on the given coordinate with the given width, height and color
+
+ Args:
+ x (float): Horizontal coordinate.
+ y (float): Vertical coordinate.
+ width (float): Width of the triangle.
+ height (float): Height of the triangle.
+ fill_color (Color, optional): The color by which to fill the rectangle.
+ stroke_color (Color, optional): The color of the rectangle's outline
+ stroke_width (float, optional): The width of the outline.
+ """
+ raise NotImplementedError("rectangle() not implemented")
+
+
+def circle(x, y, radius, fill_color, stroke_color, stroke_width):
+ """Draws a circle on the given coordinate with the given radius and color
+
+ Args:
+ x (float): Horizontal coordinate.
+ y (float): Vertical coordinate.
+ radius (float): Radius of the circle.
+ fill_color (Color, optional): The color by which to fill the circle.
+ stroke_color (Color, optional): The color of the circle's outline
+ stroke_width (float, optional): The width of the outline.
+ """
+ raise NotImplementedError("circle() not implemented")
+
+
+def ellipse(x, y, width, height, fill_color="", stroke_color="", stroke_width=-1):
+ """Draws an ellipse on the given coordinate with the given width, height and color
+
+ Args:
+ x (float): Horizontal coordinate.
+ y (float): Vertical coordinate.
+ width (float): Width of the ellipse.
+ height (float): Height of the ellipse.
+ fill_color (Color, optional): The color by which to fill the ellipse.
+ stroke_color (Color, optional): The color of the ellipse's outline
+ stroke_width (float, optional): The width of the outline.
+ """
+ raise NotImplementedError("ellipse() not implemented")
+
+
+def arc(x, y, radius, start_angle, stop_angle, fill_color="", stroke_color="", stroke_width=-1):
+ """Draws an arc on the given coordinate with the given radius, angles and color
+
+ Args:
+ x (float): Horizontal coordinate.
+ y (float): Vertical coordinate.
+ radius (float): Radius of the arc.
+ start_angle (float): The angle from which to begin drawing the arc.
+ stop_angle (float): The angle at which to stop drawing the arc.
+ fill_color (Color, optional): The color by which to fill the arc.
+ stroke_color (Color, optional): The color of the arc's outline
+ stroke_width (float, optional): The width of the outline.
+ """
+ raise NotImplementedError("arc() not implemented")
diff --git a/pypen/drawing/pypen_class.py b/pypen/drawing/pypen_class.py
index 31cfbac..6c31127 100644
--- a/pypen/drawing/pypen_class.py
+++ b/pypen/drawing/pypen_class.py
@@ -2,7 +2,7 @@
from pypen.drawing.color import Color
from pypen.utils.math import TAU
-from pypen.settings import default_settings
+from pypen.settings import default_settings
import cairo
from pyglet import gl, image
@@ -22,10 +22,15 @@ def _fix_primitive_functions(self):
self.user_sketch.clear_screen = self.clear_screen
self.user_sketch.clear = self.clear
+ self.user_sketch.begin_shape = self.begin_shape
+ self.user_sketch.vertex = self.vertex
+ self.user_sketch.end_shape = self.end_shape
self.user_sketch.rectangle = self.rectangle
self.user_sketch.circle = self.circle
self.user_sketch.ellipse = self.ellipse
self.user_sketch.arc = self.arc
+ self.user_sketch.triangle = self.triangle
+ self.user_sketch.line = self.line
self.user_sketch.arc = self.arc
@@ -104,6 +109,25 @@ def fill_screen(self, color="default_background_color"):
self.context.fill()
self.context.restore()
+ def begin_shape(self):
+ self.user_sketch.settings._shape_begun = True
+
+ def vertex(self, x, y):
+ if self.user_sketch.settings._shape_begun:
+ self.context.move_to(x, y)
+ self.user_sketch.settings._starting_point = (x, y)
+ self.user_sketch.settings._shape_begun = False
+ else:
+ self.context.line_to(x, y)
+
+ def end_shape(self, fill_color="", stroke_color="", stroke_width=-1):
+ if self.user_sketch.settings._starting_point is not None:
+ starting_point = self.user_sketch.settings._starting_point
+ self.context.line_to(starting_point[0], starting_point[1])
+ self.user_sketch.settings._starting_point = None
+ self._stroke(stroke_color, stroke_width)
+ self._fill(fill_color)
+
def rectangle(self, x, y, width, height, fill_color="", stroke_color="", stroke_width=-1):
self.context.rectangle(x, y, width, height)
self._stroke(stroke_color, stroke_width)
@@ -127,3 +151,36 @@ def arc(self, x, y, radius, start_angle, stop_angle, fill_color="", stroke_color
self.context.arc(x, y, radius, start_angle, stop_angle)
self._stroke(stroke_color, stroke_width)
self._fill(fill_color)
+
+ def triangle(self, x1_or_x, y1_or_y, x2_or_width, y2_or_height, x3_or_p=0.5, y3=None, fill_color="", stroke_color="", stroke_width=-1):
+ if y3 is not None:
+ x1 = x1_or_x
+ y1 = y1_or_y
+ x2 = x2_or_width
+ y2 = y2_or_height
+ x3 = x3_or_p
+ else:
+ x = x1_or_x
+ y = y1_or_y
+ width = x2_or_width
+ height = y2_or_height
+ p = x3_or_p
+
+ x1 = x - width/2
+ y1 = y + height/2
+ x2 = x + width/2
+ y2 = x + height/2
+ x3 = (x2 - x1) * p + x1
+ y3 = y - height/2
+
+ self.context.move_to(x1, y1)
+ self.context.line_to(x2, y2)
+ self.context.line_to(x3, y3)
+ self.context.line_to(x1, y1)
+ self._stroke(stroke_color, stroke_width)
+ self._fill(fill_color)
+
+ def line(self, x1, y1, x2, y2, stroke_color="", stroke_width=-1):
+ self.context.move_to(x1, y1)
+ self.context.line_to(x2, y2)
+ self._stroke(stroke_color, stroke_width)
diff --git a/pypen/settings.py b/pypen/settings.py
index d22a597..24b86a6 100644
--- a/pypen/settings.py
+++ b/pypen/settings.py
@@ -2,7 +2,8 @@
class Settings:
def __init__(self, fps, width, height, default_pypen_name, _is_executing_with_python,
- _user_has_start, _user_has_update, fill_color, stroke_color, stroke_width):
+ _user_has_start, _user_has_update, fill_color, stroke_color, stroke_width,
+ _shape_begun, _starting_point):
self.fps = fps
self.width = width
self.height = height
@@ -15,6 +16,9 @@ def __init__(self, fps, width, height, default_pypen_name, _is_executing_with_py
self._user_has_start = _user_has_start
self._user_has_update = _user_has_update
+ self._shape_begun = False
+ self._starting_point = None
+
settings = Settings(width=640,
height=480,
@@ -26,6 +30,9 @@ def __init__(self, fps, width, height, default_pypen_name, _is_executing_with_py
_is_executing_with_python=False,
_user_has_start=True,
- _user_has_update=True)
+ _user_has_update=True,
+
+ _shape_begun=False,
+ _starting_point=None)
default_settings = copy(settings)