From bde120e28e19d13b89c0f90bd24f3724be655819 Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Wed, 27 May 2020 23:58:32 -0400 Subject: [PATCH] Polygon trebuchets --- example.py | 19 +++++++ setup.py | 9 ++++ treimage/__init__.py | 7 +++ treimage/trebuchet.py | 115 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 example.py create mode 100644 setup.py create mode 100644 treimage/__init__.py create mode 100644 treimage/trebuchet.py diff --git a/example.py b/example.py new file mode 100644 index 0000000..4ee886c --- /dev/null +++ b/example.py @@ -0,0 +1,19 @@ +""" +Example script that uses treimage. +""" +from PIL import Image, ImageDraw +from treimage.trebuchet import D + + +CANVAS_SIZE = (640, 640) +WHITE = (255, 255, 255) +RED = (255, 0, 0) + +a = D(10, 10, 10, 0.3) +print("Size:", a.size) +print(a.points) + +with Image.new('RGB', CANVAS_SIZE, WHITE) as im: + draw = ImageDraw.Draw(im) + draw.polygon(a.points, fill=RED) + im.show() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..35ff1bc --- /dev/null +++ b/setup.py @@ -0,0 +1,9 @@ +from setuptools import setup, find_packages + +setup( + name="treimage", + description="Copy an image out of trebuchets.", + version="0.1", + packages=find_packages(), + install_requires=['Pillow~=7.1.2'] +) \ No newline at end of file diff --git a/treimage/__init__.py b/treimage/__init__.py new file mode 100644 index 0000000..a16ded5 --- /dev/null +++ b/treimage/__init__.py @@ -0,0 +1,7 @@ +""" +treimage provides functionality to take an +image and replicate it with flexible trebuchets. + +Concept was inspired by OptArt by Robert Bosch. +""" +from . import trebuchet diff --git a/treimage/trebuchet.py b/treimage/trebuchet.py new file mode 100644 index 0000000..6a84a91 --- /dev/null +++ b/treimage/trebuchet.py @@ -0,0 +1,115 @@ +""" +Creates a trebuchet out of points. + +Inspired by the OptArt book by Robert Bosch. +""" +from functools import cached_property + +__all__ = ['FlexibleTrebuchet', 'A', 'B', 'C', 'D'] + +class FlexibleTrebuchet: + """ + Flexible trebuchet is like a normal trebuchet, but + instead of a diagnal line going through the center, we move + the end point to make two different lines that connect + to the center. + """ + def __init__(self, x1: float, y1: float, + size: float, brightness: float): + """ + Parameters + ========== + x1: Top left corner, horizontal pos + y1: Top left corner, vertical pos + size: Multiplicative factor for width/height + brightness: Float between 0 and 1 + """ + assert size >= 1 + assert 0 <= brightness <= 1 + self._x1: float = x1 + self._y1: float = y1 + self._size: float = size + self._brightness: float = brightness + + @property + def x1(self): + return self._x1 + + @property + def y1(self): + return self._y1 + + @property + def size(self): + return self._size + + @property + def brightness(self): + return self._brightness + + @cached_property + def x2(self): + return self.x1 + (4 * self.size) + + @cached_property + def y2(self): + return self.y1 + (4 * self.size) + + def scale_position_point(self, point): + scaled_point = (self.size * point[0], self.size * point[1]) + return (self.x1 + scaled_point[0], self.y1 + scaled_point[1]) + + @cached_property + def midpoint(self): + raise NotImplementedError + + @cached_property + def points(self): + raise NotImplementedError + + + +class A(FlexibleTrebuchet): + """Follows the A Trebuchet from OptArt.""" + @cached_property + def midpoint(self): + normalized_point = (-2 * self.brightness + 3, 2 * self.brightness + 1) + return self.scale_position_point(normalized_point) + + @cached_property + def points(self): + return ((self.x1, self.y1), self.midpoint, (self.x2, self.y2), (self.x1, self.y2)) + + +class B(FlexibleTrebuchet): + """Follows the B Trebuchet from OptArt.""" + @cached_property + def midpoint(self): + normalized_point = (-2 * self.brightness + 3, -2 * self.brightness + 3) + return self.scale_position_point(normalized_point) + + @cached_property + def points(self): + return ((self.x1, self.y1), (self.x2, self.y1), self.midpoint, (self.x1, self.y2)) + +class C(FlexibleTrebuchet): + """Follows the C Trebuchet from OptArt.""" + @cached_property + def midpoint(self): + normalized_point = (2 * self.brightness + 1, -2 * self.brightness + 3) + return self.scale_position_point(normalized_point) + + @cached_property + def points(self): + return ((self.x1, self.y1), self.midpoint, (self.x2, self.y2), (self.x2, self.y1)) + +class D(FlexibleTrebuchet): + """Follows the D Trebuchet from OptArt.""" + @cached_property + def midpoint(self): + normalized_point = (2 * self.brightness + 1, 2 * self.brightness + 1) + return self.scale_position_point(normalized_point) + + @cached_property + def points(self): + return ((self.x1, self.y2), (self.x2, self.y2), (self.x2, self.y1), self.midpoint)