Stylo Doodles

North Pole

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
import numpy.random as npr
from math import pi

from stylo.domain.transform import translate
from stylo.color import FillColor
from stylo.shape import Shape, Circle, Rectangle, Ellipse, Triangle
from stylo.image import LayeredImage
from stylo.math import lerp
white = FillColor("ffffff")
moon_color = FillColor("ffffee")
red = FillColor("ff0000")
black = FillColor()
gold = FillColor("ffd700")
class Background(Shape):
    
    def draw(self):
        
        def background(x, y):
            
            s1 = 0.2*np.sin(12*x)
            s2 = 0.3*np.sin(5*x)
            s3 = 0.2*np.sin(17*x)
            s4 = 0.1*np.sin(7*x)
            
            return y*3 < s1 + s2 + s3 + s4
            
        return background
    
class NorthPole(Shape):
    
    def __init__(self, invert=False, stripes=True, scale=1):
        self.invert = invert
        self.stripes = stripes
        self.scale = scale
    
    def draw(self):
        
        circle = Circle(y=0.55, r=0.1, fill=True)
        pole = Rectangle(x=0, y=0, width=0.125, height=1)
        
        def northpole(x, y):
            
            x = x / self.scale
            y = y / self.scale
            
            globe = np.logical_not(circle(x=x, y=y))
            
            shape = pole(x=x, y=y)
            shape = np.logical_and(shape, globe)
            
            if not self.stripes:
                return shape
            
            ry = np.sin(pi/4)*x + np.cos(pi/4)*y
            stripes = np.abs(ry % 0.1) < 0.05
            
            if self.invert:
                stripes = np.logical_not(stripes)
            
            return np.logical_and(shape, stripes)
            
        return northpole
    
class Globe(Shape):
    
    def draw(self):
        
        circle = Circle(r=0.1, pt=0.001)
        highlight = Ellipse(x=0.04, y=0.05, r=0.01, fill=True)
        
        def globe(x, y):
            shape = circle(x=x, y=y)
            shape = np.logical_or(shape, highlight(x=x, y=y))
            
            return shape
            
        return globe
    
class Ski(Shape):

    def draw(self):
        
        base = Rectangle(x=-0.2, y=-0.01, width=1.6, height=0.03)
        outer = Circle(x=0.6, y=0.2, r=0.225, fill=True)
        inner = Circle(x=0.6, y=0.2, r=0.2, fill=True)
        
        def ski(x, y):
            
            cutout = np.logical_or(inner(x=x, y=y), x < 0.6)
            cutout = np.logical_not(cutout)
            
            loop = np.logical_and(outer(x=x, y=y), cutout)
            
            shape = base(x=x, y=y)
            shape = np.logical_or(shape, loop)
            
            return shape
        
        return ski
    
class SuperEllipse(Shape):
    
    def __init__(self, x=0, y=0, a=2, b=1, n=2, r=1):
        self.x = x
        self.y = y
        self.a = a
        self.b = b
        self.r = r
        self.n = n
        
    def draw(self):
        
        def ellipse(x, y):
            xs = x - self.x
            ys = y - self.y
            
            return np.abs(xs / self.a)**self.n + np.abs(ys / self.b)**self.n < self.r
            
        return ellipse
    
class Sleigh(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
    
    def draw(self):
        
        base = SuperEllipse(x=-1, y=-1, a=2.7, n=4, r=0.2)
        front = SuperEllipse(x=0.5, y=-0.5, a=1, b=2.3, r=0.2)
        back = SuperEllipse(x=-2.5, y=-0.25, a=1, b=3, r=0.2)
        
        def sleigh(x, y):
            
            x = x / self.scale
            y = y / self.scale * 1.3
            
            x1 = x - 0.6
            
            s1 = 0.4*np.cos(2*x1)
            s2 = 0.2*np.cos(3*x1)
            s3 = 0.1*np.cos(5*x1)
            
            side = y + (x/2.5) < s1 + s2 + s3
            
            bounds = np.logical_and(-2.5 < x, x < 0.5)
            bounds = np.logical_and(bounds, y > -0.5)
            
            shape = np.logical_and(side, bounds)
            shape = np.logical_or(shape, base(x=x, y=y))
            shape = np.logical_or(shape, front(x=x, y=y))
            shape = np.logical_or(shape, back(x=x, y=y))
            
            return shape
        
        return sleigh
    
class Snowflake(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
    
    def draw(self):
        
        def flake(x, y, r):
            
            x = x / self.scale
            y = y / self.scale
            r = r / self.scale
            
            xs = np.abs(x) < 0.015
            ys = np.abs(y) < 0.01
            
            shape = np.logical_or(xs, ys)
            shape = np.logical_or(shape, np.abs(x - y) < 0.01)
            shape = np.logical_or(shape, np.abs(x + y) < 0.01)
            shape = np.logical_and(shape, r < 0.2)
            
            return shape
            
        return flake
    
class Star(Shape):
    
    def __init__(self, scale=1):
        self.scale = scale
    
    def draw(self):
        
        t1 = Triangle((0.5, 0), (-0.5, 0), (0, 0.75))
        t2 = Triangle((0.5, 0.5), (-0.5, 0.5), (0, -0.25))
        
        def star(x, y):
            
            x = x / self.scale
            y = y / self.scale
            
            return np.logical_or(t1(x=x, y=y), t2(x=x, y=y))
            
        return star
    
background = Background() >> translate(0, -0.2)
moon = Circle(x=1.3, y=0.75, r=0.2, fill=True) & ~Circle(x=1.4, y=0.8, r=0.2, fill=True)

northpole = NorthPole() >> translate(0.9, -0.3)
northpole1 = NorthPole(invert=True) >> translate(0.9, -0.3)
northpole_outline = Rectangle(x=0, y=0, width=0.13, height=1.02) >> translate(0.90, -0.3)

globe = Globe() >> translate(0.9, 0.25)

ski = Ski() >> translate(-0.65, -0.8)

posts = Rectangle(x=-0.9, y=-0.75, width=0.05, height=0.1) |\
        Rectangle(x=-1.4, y=-0.75, width=0.05, height=0.1) |\
        Rectangle(x=-0.4, y=-0.75, width=0.05, height=0.1)

sleigh1 = Sleigh(scale=0.415) >> translate(-0.49, -0.19)
sleigh = Sleigh(scale=0.4) >> translate(-0.5, -0.2)

flake = Snowflake(scale=0.2) >> translate(-1.5, 0)
flake1 = Snowflake(scale=0.2) >> translate(-1.55, -0.25)
flake2 = Snowflake(scale=0.25) >> translate(-1.5, -0.5)

flake3 = Snowflake(scale=0.25) >> translate(-0.25, -0.15)
flake4 = Snowflake(scale=0.25) >> translate(-0.2, -0.35)
flake5 = Snowflake(scale=0.25) >> translate(-0.3, -0.55)
image = LayeredImage(background="000022")

image.add_layer(background, white)
image.add_layer(moon, moon_color)

xs = lerp(-1.8, 1.4)
ys = lerp(0, 1)
sizes = lerp(0.03, 0.08)
npr.seed(128)

for s, t, u in npr.rand(25, 3):
    star = Star(scale=sizes(u)) >> translate(xs(s), ys(t))
    image.add_layer(star, moon_color)

image.add_layer(northpole_outline, black)
image.add_layer(northpole, red)
image.add_layer(northpole1, white)

image.add_layer(globe, white)

image.add_layer(ski, gold)
image.add_layer(sleigh1, gold)
image.add_layer(posts, gold)
image.add_layer(sleigh, red)

image.add_layer(flake, gold)
image.add_layer(flake1, gold)
image.add_layer(flake2, gold)
image.add_layer(flake3, gold)
image.add_layer(flake4, gold)
image.add_layer(flake5, gold)