diff --git a/spark/core.py b/spark/core.py
index a4f7b08..46ba78b 100644
--- a/spark/core.py
+++ b/spark/core.py
@@ -331,6 +331,9 @@ def clear(self, *args): pass
@extern
def background(self, *args): pass
+ @extern
+ def rotate(self, *args): pass
+
# From util.helper_functions.rect_functions
@extern
diff --git a/spark/util/helper_functions/canvas_functions.py b/spark/util/helper_functions/canvas_functions.py
index 9daa586..ceb6c17 100644
--- a/spark/util/helper_functions/canvas_functions.py
+++ b/spark/util/helper_functions/canvas_functions.py
@@ -35,3 +35,8 @@ def helper_background(self, *args):
self.canvas.fill_style = fill
self.canvas.fill_rect(0, 0, self.width, self.height)
self.canvas.fill_style = old_fill
+
+@validate_args([Real])
+@ignite_global
+def helper_rotate(self, *args):
+ self.canvas.rotate(args[0])
diff --git a/test/RotateTest.ipynb b/test/RotateTest.ipynb
new file mode 100644
index 0000000..e252832
--- /dev/null
+++ b/test/RotateTest.ipynb
@@ -0,0 +1,1141 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import spark\n",
+ "%reload_ext spark"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\\PY{err}{D}\\PY{err}{o}\\PY{err}{n}\\PY{err}{e}\\PY{err}{ }\\PY{err}{d}\\PY{err}{r}\\PY{err}{a}\\PY{err}{w}\\PY{err}{i}\\PY{err}{n}\\PY{err}{g}\\PY{err}{.}\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": [
+ "Done drawing."
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "f49bfc7f742a4372adf4705254417604",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Canvas(height=100, width=100)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "%%ignite\n",
+ "\n",
+ "# Basic rotation in radians\n",
+ "\n",
+ "def setup():\n",
+ " size(300, 300)\n",
+ " background(\"pink\")\n",
+ " rotate(pi / 4)\n",
+ " rect(100, 0, 80, 50)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\\PY{err}{S}\\PY{err}{t}\\PY{err}{o}\\PY{err}{p}\\PY{err}{p}\\PY{err}{e}\\PY{err}{d}\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": [
+ "Stopped"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "7e97ef368f4949c48c3d2bce82b9ddcf",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Button(description='Stop', style=ButtonStyle())"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "78a4bb51d12b49f08babcea7346127e0",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Canvas(height=100, width=100)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "%%ignite\n",
+ "\n",
+ "# Rotation in draw\n",
+ "\n",
+ "def setup():\n",
+ " size(300, 300)\n",
+ " background(\"pink\")\n",
+ " \n",
+ "def draw():\n",
+ " rotate(pi * 0.63931)\n",
+ " rect(100, 0, 80, 50)\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\\PY{err}{D}\\PY{err}{o}\\PY{err}{n}\\PY{err}{e}\\PY{err}{ }\\PY{err}{d}\\PY{err}{r}\\PY{err}{a}\\PY{err}{w}\\PY{err}{i}\\PY{err}{n}\\PY{err}{g}\\PY{err}{.}\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": [
+ "Done drawing."
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "fd30e612028a43e89140916234f59c7f",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Canvas(height=100, width=100)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "%%ignite\n",
+ "\n",
+ "# Rotate 2 pi radians. Expect it to look the same as rotating 0 degrees\n",
+ "\n",
+ "def setup():\n",
+ " size(300, 300)\n",
+ " background(\"yellow\")\n",
+ " \n",
+ " # Draw a non-rotated rectangle - should appear as border to rotated rect\n",
+ " fill_style(\"white\")\n",
+ " rect(95, -5, 90, 60)\n",
+ " \n",
+ " rotate(2 * pi)\n",
+ " \n",
+ " # Expect black rectangle to land perfectly on top of white rectangle\n",
+ " fill_style(\"black\")\n",
+ " rect(100, 0, 80, 50)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\\PY{err}{D}\\PY{err}{o}\\PY{err}{n}\\PY{err}{e}\\PY{err}{ }\\PY{err}{d}\\PY{err}{r}\\PY{err}{a}\\PY{err}{w}\\PY{err}{i}\\PY{err}{n}\\PY{err}{g}\\PY{err}{.}\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": [
+ "Done drawing."
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "5ab00b02d23440618ca83e5b872cbf72",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Canvas(height=100, width=100)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "%%ignite\n",
+ "\n",
+ "# Rotate negative radians. Expect rotating -x radians == 2pi - x radians\n",
+ "\n",
+ "def setup():\n",
+ " size(300, 300)\n",
+ " background(\"yellow\")\n",
+ " \n",
+ " angle = pi / 6 # 30 degrees\n",
+ " \n",
+ " # Draw a positively rotated rect - should appear as border to rotated rect\n",
+ " rotate(2 * pi - angle)\n",
+ " fill_style(\"white\")\n",
+ " rect(95, -5, 90, 60)\n",
+ " rotate(-(2 * pi - angle))\n",
+ " \n",
+ " \n",
+ " rotate(-angle)\n",
+ " \n",
+ " # Expect black rectangle to land perfectly on top of white rectangle\n",
+ " fill_style(\"black\")\n",
+ " rect(100, 0, 80, 50)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Error in setup() function: Invalid types for rotate with 1 argument, expected \n",
+ "\trotate(Real)\n",
+ "received rotate(bad: str)\n",
+ "
\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\\PY{err}{E}\\PY{err}{r}\\PY{err}{r}\\PY{err}{o}\\PY{err}{r}\\PY{err}{ }\\PY{err}{i}\\PY{err}{n}\\PY{err}{ }\\PY{err}{s}\\PY{err}{e}\\PY{err}{t}\\PY{err}{u}\\PY{err}{p}\\PY{err}{(}\\PY{err}{)}\\PY{err}{ }\\PY{err}{f}\\PY{err}{u}\\PY{err}{n}\\PY{err}{c}\\PY{err}{t}\\PY{err}{i}\\PY{err}{o}\\PY{err}{n}\\PY{err}{:}\\PY{err}{ }\\PY{err}{I}\\PY{err}{n}\\PY{err}{v}\\PY{err}{a}\\PY{err}{l}\\PY{err}{i}\\PY{err}{d}\\PY{err}{ }\\PY{err}{t}\\PY{err}{y}\\PY{err}{p}\\PY{err}{e}\\PY{err}{s}\\PY{err}{ }\\PY{err}{f}\\PY{err}{o}\\PY{err}{r}\\PY{err}{ }\\PY{err}{r}\\PY{err}{o}\\PY{err}{t}\\PY{err}{a}\\PY{err}{t}\\PY{err}{e}\\PY{err}{ }\\PY{err}{w}\\PY{err}{i}\\PY{err}{t}\\PY{err}{h}\\PY{err}{ }\\PY{err}{1}\\PY{err}{ }\\PY{err}{a}\\PY{err}{r}\\PY{err}{g}\\PY{err}{u}\\PY{err}{m}\\PY{err}{e}\\PY{err}{n}\\PY{err}{t}\\PY{err}{,}\\PY{err}{ }\\PY{err}{e}\\PY{err}{x}\\PY{err}{p}\\PY{err}{e}\\PY{err}{c}\\PY{err}{t}\\PY{err}{e}\\PY{err}{d}\\PY{err}{ }\n",
+ "\\PY{err}{\t}\\PY{err}{r}\\PY{err}{o}\\PY{err}{t}\\PY{err}{a}\\PY{err}{t}\\PY{err}{e}\\PY{err}{(}\\PY{err}{R}\\PY{err}{e}\\PY{err}{a}\\PY{err}{l}\\PY{err}{)}\n",
+ "\\PY{err}{r}\\PY{err}{e}\\PY{err}{c}\\PY{err}{e}\\PY{err}{i}\\PY{err}{v}\\PY{err}{e}\\PY{err}{d}\\PY{err}{ }\\PY{err}{r}\\PY{err}{o}\\PY{err}{t}\\PY{err}{a}\\PY{err}{t}\\PY{err}{e}\\PY{err}{(}\\PY{err}{b}\\PY{err}{a}\\PY{err}{d}\\PY{err}{:}\\PY{err}{ }\\PY{err}{s}\\PY{err}{t}\\PY{err}{r}\\PY{err}{)}\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": [
+ "Error in setup() function: Invalid types for rotate with 1 argument, expected \n",
+ "\trotate(Real)\n",
+ "received rotate(bad: str)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "2e5f69160a2e42a9b28005b94e36158e",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Canvas(height=100, width=100)"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/latex": [
+ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
+ "\n",
+ "\\end{Verbatim}\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "%%ignite\n",
+ "\n",
+ "def setup():\n",
+ " rotate(\"bad\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}