Stylo Doodles

Christmas Crackers

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 Shape, Circle, Square, Ellipse, Rectangle
from stylo.image import LayeredImage
black = FillColor("000000")
red = FillColor("ff0000")
gold = FillColor("ffff00")
def ored(*args):
    
    xs = False
    
    for condition in args:
        xs = np.logical_or(xs, condition)
        
    return xs
class Cracker(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
    
    def draw(self):
        
        def join(x, y):
            return np.abs(y) < np.abs(x)
           
        left1 = Rectangle(-1, 0, width=0.5, height=0.5)
        right1 = Rectangle(1, 0, width=0.5, height=0.5)
        middle = Rectangle(0, 0, width=1, height=0.5)
        
        def cracker(x, y):
            x = x / self.scale
            y = y / self.scale
            
            left_join = np.logical_and(np.abs(y) < 0.25, join(x - 0.625, y/2))
            left_join = np.logical_and(left_join, np.abs(x - 0.6) < 0.25)
                                
            right_join = np.logical_and(np.abs(y) < 0.25, join(x + 0.625, y/2))
            right_join = np.logical_and(right_join, np.abs(x + 0.6) < 0.25)
         
            body = np.logical_or(left1(x=x, y=y), right1(x=x, y=y))
            body = np.logical_or(body, middle(x=x, y=y))
            
            cracker_shape = np.logical_or(body, left_join)
            cracker_shape = np.logical_or(cracker_shape, right_join)
            
            return cracker_shape
            
        return cracker
    
class Bands(Shape):
    
    def __init__(self, cracker):
        self.cracker = cracker
        
    def draw(self):
        
        def bands(x, y):
            
            x1 = np.logical_and(np.abs(x) > 0.90, np.abs(x) < 0.94)
            x2 = np.logical_and(np.abs(x) > 0.86, np.abs(x) < 0.88)
            x3 = np.logical_and(np.abs(x) > 0.35, np.abs(x) < 0.6)
            
            xs = ored(x1, x2, x3)
            
            return np.logical_and(xs, self.cracker(x=x, y=y))
            
        return bands
    
class Spots(Shape):
    
    def __init__(self, cracker):
        self.cracker = cracker
        
    def draw(self):
        
        xs = np.linspace(-1, 1, 16)
        ys = np.linspace(-1, 1, 16)
        
        dots = [
            Circle(x=x, y=y, fill=True, r=0.02) for x in xs for y in ys
        ]
        
        def spots(x, y):
            
            # 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
            
            conds = [c(x=rx, y=ry) for c in dots]
            
            return np.logical_and(ored(*conds), self.cracker(x=x, y=y))
        
        return spots
    
class Curves(Shape):
    
    def __init__(self, cracker):
        self.cracker = cracker
        
    def draw(self):
        
        def curves(x, y):
            
            ys = np.abs(y*8) < np.cos(4*x*pi) * np.cos(4*x*pi) 
            
            return np.logical_and(ys, self.cracker(x=x, y=y))
            
        return curves
cracker1 = Cracker(scale=0.75) >> rotate(-pi/6) >> translate(-.8, 0.35)
bands = Bands(cracker1) >> rotate(-pi/6) >> translate(-.8, 0.35)

cracker2 = Cracker(scale=0.75) >> rotate(-pi/6)
spots = Spots(cracker2) >> rotate(-pi/6)

cracker3 = Cracker(scale=0.75) >> rotate(-pi/6) >> translate(.8, -0.35)
curves = Curves(cracker3) >> rotate(-pi/6) >> translate(.8, -0.35)
image = LayeredImage(background="000088")

image.add_layer(cracker1, red)
image.add_layer(bands, gold)

image.add_layer(cracker2, red)
image.add_layer(spots, gold)

image.add_layer(cracker3, red)
image.add_layer(curves, gold)