Stylo Doodles

Gingerbread People

Details

Author:

Alex Carney

Github:

alcarney

Dimensions:

1920 x 1080

Stylo Version:

0.9.0
Show Source Show Notes Show Both

Source

import numpy as np
from math import pi

from stylo.domain.transform import translate, rotate
from stylo.color import FillColor
from stylo.shape import Circle, Shape, Triangle, Rectangle, Ellipse
from stylo.image import LayeredImage
red = FillColor("ff0000")
green = FillColor("008800")
brown = FillColor("D2691E")
darkbrown = FillColor("C1580D")
black = FillColor()
blue = FillColor("0000dd")
white = FillColor("ffffff")
class Pattern(Shape):
    
    def draw(self):
        
        def pattern(x, y):
            
            ry = np.sin(pi/4)*x + np.cos(pi/4)*y
            return np.abs(ry % 0.1) < 0.05
        
        return pattern
    
class Grid(Shape):
    
    def __init__(self, scale=1, invert=False):
        self.scale = scale
        self.invert = invert
    
    def draw(self):
        
        def grid(x, y):
            
            x = x / self.scale
            y = y / self.scale
            
            rx = np.cos(-pi/4)*x - np.sin(-pi/4)*y
            ry = np.sin(-pi/4)*x + np.cos(-pi/4)*y
            
            square1_y = np.abs(ry % 0.4) < 0.2
            square2_y = np.abs(ry % 0.4) > 0.2
            square1_x = np.abs(rx % 0.4) < 0.2
            square2_x = np.abs(rx % 0.4) > 0.2
            
            square1 = np.logical_and(square1_y, square1_x)
            square2 = np.logical_and(square2_y, square2_x)
            
            pattern = np.logical_or(square1, square2)
            
            if self.invert:
                pattern = np.logical_not(pattern)
            
            return pattern
        
        return grid
    
class GingerBoy(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
        
    def draw(self):
        
        head = Circle(y=0.5, r=0.3, fill=True)
        body = Triangle((0, 0.6), (-0.29, -0.5), (0.29, -0.5))
        
        arm = Rectangle(x=0.2, y=0, width=0.5, height=0.25)
        hand = Circle(x=0.45, y=0, r=0.125, fill=True)
        
        leg = Rectangle(x=0, y=0, width=0.26, height=0.4)
        foot = Circle(x=0.05, y=-0.78, r=0.129, fill=True)
        
        def ginger(x, y):
            x = x / self.scale
            y = y / self.scale
            
            # Mirror the x values to get the symmetry for free.
            mx = np.abs(x)
            
            # Rotation for the arm1
            a1 = -pi/12
            rx1 = np.cos(a1)*x - np.sin(a1)*y
            ry1 = np.sin(a1)*x + np.cos(a1)*y
            
            # Rotation for the legs
            a2 = -pi/16
            rx2 = np.cos(a2)*mx - np.sin(a2)*y
            ry2 = np.sin(a2)*mx + np.cos(a2)*y          
            
            # Rotation for arm2
            a3 = pi/6
            rx3 = np.cos(a3)*(-x) - np.sin(a3)*y
            ry3 = np.sin(a3)*(-x) + np.cos(a3)*y
            
            bread = head(x=x, y=y)
            bread = np.logical_or(bread, body(x=x, y=y))
            
            bread = np.logical_or(bread, arm(x=rx1, y=ry1))
            bread = np.logical_or(bread, hand(x=rx1, y=ry1))
                        
            bread = np.logical_or(bread, arm(x=rx3, y=ry3))
            bread = np.logical_or(bread, hand(x=rx3, y=ry3))
            
            bread = np.logical_or(bread, leg(x=(rx2 - 0.05), y=(ry2 + 0.6)))
            bread = np.logical_or(bread, foot(x=rx2, y=ry2))
        
            return bread
        
        return ginger   
    
    
class GingerGirl(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
        
    def draw(self):
        
        head = Circle(y=0.5, r=0.3, fill=True)
        body = Triangle((0, 0.6), (-0.39, -0.5), (0.39, -0.5))
        
        arm = Rectangle(x=0.2, y=0, width=0.5, height=0.25)
        hand = Circle(x=0.45, y=0, r=0.125, fill=True)
        
        leg = Rectangle(x=0, y=0, width=0.26, height=0.4)
        foot = Circle(x=0.05, y=-0.78, r=0.129, fill=True)
        
        cutout = Ellipse(x=0.05, y=-0.54, a=1.1, r=0.126, fill=True) 
        
        def ginger(x, y):
            x = x / self.scale
            y = y / self.scale
            
            # Mirror the x values to get the symmetry for free.
            mx = np.abs(x)
            
            # Rotation for the arms
            a1 = -pi/12
            rx1 = np.cos(a1)*mx - np.sin(a1)*y
            ry1 = np.sin(a1)*mx + np.cos(a1)*y
            
            # Rotation for the legs
            a2 = -pi/16
            rx2 = np.cos(a2)*mx - np.sin(a2)*y
            ry2 = np.sin(a2)*mx + np.cos(a2)*y          
            
            bread = head(x=x, y=y)
            bread = np.logical_or(bread, body(x=x, y=y))
            
            bread = np.logical_or(bread, arm(x=rx1, y=ry1))
            bread = np.logical_or(bread, hand(x=rx1, y=ry1))
            
            bread = np.logical_or(bread, leg(x=(rx2 - 0.05), y=(ry2 + 0.6)))
            bread = np.logical_or(bread, foot(x=rx2, y=ry2))
            
            return bread
        
        return ginger  
            
class Bow(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
        
    def draw(self):
        
        center = Circle(r=0.1, fill=True)
        tri = Triangle((0,0), (0.4, -0.2), (0.4, 0.2))
        
        def bow(x, y):
            
            x = x / self.scale
            y = y / self.scale
            
            shape = center(x=x, y=y)
            shape = np.logical_or(shape, tri(x=np.abs(x), y=y))
            
            return shape
            
        return bow
    
class Mouth(Shape):
    
    def draw(self):
        
        shape = Circle(r=0.2, pt=0.01)
        
        x1, y1 = 0.2*np.cos(0.9 - pi/2), 0.2*np.sin(0.9 - pi/2)
        cap1 = Circle(x=x1, y=y1, r=0.03, fill=True)
        
        x2, y2 = 0.2*np.cos(-0.9 - pi/2), 0.2*np.sin(-0.9 - pi/2)
        cap2 = Circle(x=x2, y=y2, r=0.03, fill=True)
        
        def mouth(x, y, t):
            bounds = np.abs(t + pi/2) < 0.9
            
            curve =  np.logical_and(shape(x=x, y=y), bounds)
            curve = np.logical_or(curve, cap1(x=x, y=y))
            curve = np.logical_or(curve, cap2(x=x, y=y))
            
            return curve
        
        return mouth
          
class Cane(Shape):
    
    def draw(self):
            
        y1 = 0.3
        
        stem = Rectangle(x=0, y=-0.1, width=0.08, height=0.8)
        outer_hook = Circle(x=0.16, y=y1, r=0.2, fill=True)
        inner_hook = Circle(x=0.16, y=y1, r=0.12, fill=True)
        
        cap1 = Circle(x=0.32, y=y1, r=0.04, fill=True)
        cap2 = Circle(y=-0.5, r=0.04, fill=True)
        
        def cane(x, y):
            
            inner = np.logical_not(inner_hook(x=x, y=y))
            ring = np.logical_and(outer_hook(x=x, y=y), inner)
            
            hook = np.logical_and(ring, y >= y1)
            
            candy = np.logical_or(stem(x=x, y=y), hook)
            candy = np.logical_or(candy, cap1(x=x, y=y))
            
            return np.logical_or(candy, cap2(x=x, y=y))
            
        return cane
    
class CandyCane(Shape):
    
    def __init__(self, invert=False, flip=False, scale=1):
        self.invert = invert
        self.flip = flip
        self.scale = scale
    
    def draw(self):
        
        cane = Cane()
        
        def candy(x, y):
            
            x = x / self.scale
            y = y / self.scale
            
            if self.flip:
                x = -x
            
            # Rotate the domain by hand.
            rx = np.cos(-pi/4)*x - np.sin(-pi/4)*y
            ry = np.sin(-pi/4)*x + np.cos(-pi/4)*y
            
            stripes = rx/4 < -np.sin(28*ry*pi)
            
            if self.invert:
                stripes = np.logical_not(stripes)
            
            candycane = np.logical_and(cane(x=x, y=y), stripes)
            
            return candycane
            
        return candy
    
class HatRim(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
        
    def draw(self):
        
        c1 = Circle(r=0.2, fill=True)
        c2 = Circle(x=0.2, y=-0.03, r=0.18, fill=True)
        c3 = Circle(x=0.4, y=-0.06, r=0.16, fill=True)
        
        def hat(x, y):
            x = x / self.scale
            y = y / self.scale
            
            x = np.abs(x)
            
            shape = np.logical_or(c1(x=x, y=y), c2(x=x, y=y))
            shape = np.logical_or(shape, c3(x=x, y=y))
            
            return shape
        return hat
    
class Basket(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
        
    def draw(self):
        
        handle = Rectangle(x=0, y=0.15, width=0.05, height=0.3)
        base = Ellipse(r=0.2, a=1.6, fill=True)
        
        def basket(x, y):

            mask = y < 0
            return np.logical_and(base(x=x, y=y), mask)
        
        return basket
    
class Cover(Shape):
    
    def __init__(self, scale=1, invert=False):
        self.scale = scale
        self.invert = invert
    
    def draw(self):
        
        tri = Triangle((0.4, 0), (0.25, -0.2), (0, 0))
        grid = Grid(scale=self.scale, invert=self.invert)
        
        def cover(x, y):
            mask = grid(x=x, y=y)
            shape = tri(x=np.abs(x), y=y)

            return np.logical_and(shape, mask)
            
        return cover
pattern = Pattern()
gingerboy = GingerBoy() >> translate(-0.75, 0)

button1 = Circle(x=-0.75, y=-0.1, r=0.05, fill=True)
button2 = Circle(x=-0.75, y=-0.25, r=0.05, fill=True)
button3 = Circle(x=-0.75, y=0.05, r=0.05, fill=True)

bow1 = Bow(scale=0.3) >> translate(-0.75, 0.2)

eye1 = Circle(x=-0.85, y=0.55, r=0.05, fill=True)
eye2 = Circle(x=-0.65, y=0.55, r=0.05, fill=True)
mouth1 = Mouth() >> translate(-0.75, 0.52)

cane = CandyCane(flip=True, scale=0.55) >> rotate(pi/8) >> translate(-1.25, -0.6)
inv_cane = CandyCane(True, flip=True, scale=0.55) >> rotate(pi/8) >> translate(-1.25, -0.6)

hat_rim = HatRim(scale=0.45) >> rotate(pi/5) >> translate(-0.57, 0.73)
hat = Ellipse(y=0.07, r=0.15, a=1.5, fill=True) >> rotate(pi/5) >> translate(-0.57, 0.73)
hat_top = Triangle((-0.15, 0.18), (0.1, 0), (0.5, 0.3)) >> rotate(pi/5) >> translate(-0.57, 0.73)
hat_fluff = Circle(x=-0.05, y=0.7, fill=True, r=0.06)
gingergirl = GingerGirl() >> translate(0.75, 0)

eye3 = Circle(x=0.85, y=0.55, r=0.05, fill=True)
eye4 = Circle(x=0.65, y=0.55, r=0.05, fill=True)
mouth2 = Mouth() >> translate(0.75, 0.52)

button4 = Circle(x=0.75, y=-0.1, r=0.05, fill=True)
button5 = Circle(x=0.75, y=0.1, r=0.05, fill=True)

hat_rim2 = HatRim(scale=0.45) >> rotate(pi/5) >> translate(0.89, 0.73)
hat2 = Ellipse(y=0.07, r=0.15, a=1.5, fill=True) >> rotate(pi/5) >> translate(0.89, 0.73)
hat_top2 = Triangle((-0.15, 0.18), (0.1, 0), (0.5, 0.3)) >> rotate(pi/5) >> translate(0.89, 0.73)
hat_fluff2 = Circle(x=0.52, y=0.7, fill=True, r=0.06) >> translate(0.89, 0)

basket = Basket() >> translate(1.25, -0.3)
cover = Cover(scale=0.4) >> translate(1.25, -0.3)
inv_cover = Cover(scale=0.4, invert=True) >> translate(1.25, -0.3)
handle = Rectangle(x=0, y=0.2, height=0.5, width=0.05) >> translate(1.25, -0.3)
image = LayeredImage(background="006600")

image.add_layer(pattern, green)
image.add_layer(gingerboy, brown)

image.add_layer(eye1, white)
image.add_layer(eye2, white)
image.add_layer(mouth1, white)

image.add_layer(button1, blue)
image.add_layer(button2, blue)
image.add_layer(button3, blue)

image.add_layer(bow1, red)

image.add_layer(cane, red)
image.add_layer(inv_cane, white)

image.add_layer(hat, red)
image.add_layer(hat_top, red)
image.add_layer(hat_rim, white)
image.add_layer(hat_fluff, white)



image.add_layer(gingergirl, brown)

image.add_layer(eye3, white)
image.add_layer(eye4, white)
image.add_layer(mouth2, white)

image.add_layer(button4, red)
image.add_layer(button5, red)

image.add_layer(hat2, red)
image.add_layer(hat_top2, red)
image.add_layer(hat_rim2, white)
image.add_layer(hat_fluff2, white)

image.add_layer(basket, darkbrown)
image.add_layer(cover, red)
image.add_layer(inv_cover, white)
image.add_layer(handle, darkbrown)