Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.
/ soda-old Public archive

Simple drawing tool, running on Python 3 + Pillow

License

Notifications You must be signed in to change notification settings

evtn/soda-old

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

soda

This README file will be updated as soda2.0 approaches.

Contents

Quickstart

Requirements

Soda runs on Python3, using Pillow for rendering and imageio for GIFs (optional)

Basics

The main object of Soda environment is canvas, an object that contains shapes and renders to an image:

import soda

# let's create our canvas
my_first_canvas = soda.Canvas(size=(1000, 1000), # size is a tuple/list with two elements: width and height.
                              color="green") # color can be either an (r, g, b) tuple, or a hex color, or even a CSS color (PIL.ImageColor)

# if one argument is given for Rectangle, soda assumes it's both width and height
square = soda.Rectangle(500, color="#fff")

# we need to put our square on the canvas.
my_first_canvas.put(square, position=(250, 250))

# then we can save our canvas and look at this ugly color scheme that we've chose (._.)
my_first_canvas.save("super_picture.png")

Oops, no picture
Remember, kids, 'green' is the worst choice.

Canvas can be rendered and saved as an image at any point of your code, and it still would be a canvas. At this point, it doesn't make any sense - why we need these shapes and canvas? The answer is simplicity. You don't have to handle some unnecessary aspects of a picture, and you can create tons of different pictures with a few lines of code.

Despite the fact that Soda is intended for creating images, its main feature is dynamics. So, let's make a better example with GIF animation:

Building a GIF

Soda has an easy-to-use GIF builder: soda.GIF class. Default use is something like this:

import soda
canvas = soda.Canvas()
gif = soda.GIF(canvas)

for frame in range(100):
    # some changes to canvas
    gif.add() # image=None
gif.save() # name=None, framerate=60

Imagine that you want to make an animation of 1600 squares randomly changing color. Pretty strange example, but here you are:

import soda
multiplier = 5
# let's create our canvas again
canvas = soda.Canvas(size=(42 * multiplier, 42 * multiplier),
                     color="#fff")
gif = soda.GIF(canvas)
squares = []
for i in range(1600):
    squares.append(soda.Rectangle(multiplier, color="#4680c2", position=(multiplier, multiplier))) # local position - square would have an additional offset
    canvas.put(squares[-1], position=((i % 40) * multiplier,
                                      (i // 40) * multiplier)) # calculating the position according to the index

renders = []

for i in range(100):
    for j in range(len(squares)):
        squares[j].color_set(soda.hsl()) # h=None, s=None, l=None
    gif.add()
gif.save(framerate=30, name="random_squares.gif")

Oops, no picture
Blinking

Positioning

You've probably seen some position argument before on this page. Seems intuitive, but what does this argument do in Rectangle?
Soda has levels of position: You can put an ellipse on canvas on (200, 200), and then set the position of this ellipse itself to (120, 90). The final position would be (320, 290)


Reference

Objects system

There are 5 main object types:

  • Canvas
  • Shape
  • Point
  • Color
  • Template

Canvas

Arguments:

size size of the canvas in pixels as tuple of (x, y) or as an integer (would be converted to a tuple) default: (1000, 1000)
color background color of canvas (check Color). default: "white"
mode mode of the picture (check Pillow Image Modes) default: "RGB"
background picture to use as canvas. if value equals "color", no picture would be used default: "color"

Methods

put(obj: Shape, position=(0, 0), index=None)
Puts a shape to the canvas. If index is specified, would insert the shape on that index.
returns: None

pop(index)
Removes a shape with chosen index.
returns: removed Shape

move(index, position)
Moves a shape with chosen index to a specified position.
returns: None

render()
Renders an image of the canvas.
returns: PIL.Image.Image

save(file, extension=None)
Saves an image of the canvas, equivalent to .render().save(file, extension)
returns: None

corners_get() and get_center()
returns: list of Point, containing corners of the canvas; returns: Point of canvas center


Shape

This is the default Shape reference, for built-in shapes check Built-in Shapes

Methods

Shapes have to implement a few common methods:

render(draw, position)
Renders itself using draw on specified position
returns: None

resized(k)
Returns a copy of itself, multiplied in size by k
returns: Shape

box_get()
Returns a tuple with shape size in pixels: (width, height).
returns: tuple(width, height)

Next block contains methods fully-implemented in Shape class:

color_set(color)
Sets a color of the shape. color can be either a Color object or any type of color notation the Color.parse accepts
returns: None

color_get()
Returns a color of the shape
returns: Color


Color

Defines color.

Attributes

change(color)
Changes its color to specified
returns: None

color
A tuple of (r, g, b) values (each one from 0 to 255)

parse method

Color.parse(col, mode="RGBA")
Parses the color (from a list/tuple or from a string)
Accepts:

  • Strings:
    • hex: #fff, #103515
    • css: white
    • hsl: hsl(360, 82%, 60%)
    • rgb: rgb(255, 0, 0)
  • RGB Tuple:
    • rgb: (240, 100, 10)
    • rgba: (255, 70, 12, 45)

returns: rgb(a) tuple


Point

Defines an abstract point on any canvas.
point-like object is a tuple or list of 2 integers, or the Point itself.

Arguments

x, y - point position

Methods

move(x=None, y=None)
Moves a point to another position. If some parameter isn't specified, it would stay the same.


Template

Defines a template to create similiar shapes.

Arguments

o_class desired object class, i.e. Ellipse.
arg_names tuple/list containing names of arguments as they would be passed.
**params static default params passed to the shape constructor.

Methods

create(*args, **params)
Creates a shape with specified args, default params and passed params.
Priority of arguments (from high to low): params - default params - args.
returns: shape instance


Built-in Shapes

Soda has a few types of unique built-in shapes.

Polygon

The most popular shape in AppStore.

import soda
import math  # let's do some fancy math

radiuses = [150 / 3 ** 0.5, 150]
points = [(math.cos(angle * math.pi / 6) * radiuses[angle % 2],
         math.sin(angle * math.pi / 6) * radiuses[angle % 2]) for angle in range(12)]

canvas = soda.Canvas(size=(500, 500),
                     color="#121212")
poly = soda.Polygon(points=points, 
                    color=soda.hsl(s=82, l=62))
canvas.put(poly, 
           position=(250, 250))
canvas.save("poly.png")

Oops, no picture
oh my stars

Polygon constructor takes two arguments:

  • points - tuple/list of point-like objects.
  • color - see Color notation. default: "black"

Specific methods:
to_list(pos)
Takes a position and returns a list of points according to this position
(returns: list of point-like lists)

Rectangle

So straight

import soda
canvas = soda.Canvas(size=(500, 500), color="#121212")
rect = soda.Rectangle(width=300, height=200, color=soda.hsl(s=82, l=62), position=(100, 150))
canvas.put(rect)
canvas.save("rect.png")

Oops, no picture
Take a rect

Rectangle constructor takes four arguments:

  • width - width of the rectangle in pixels
  • height - height in pixels. default: passed width
  • color - see Color notation. default: "black"
  • position - point-like object. default: (0, 0)

Specific methods:
size_set(width, height=None)
Changes the size of rectangle
(returns: None)

Ellipse

The guy who is just fooling around.

import soda
canvas = soda.Canvas(size=(500, 500), color="#121212")
circ = soda.Ellipse(center=(250, 250), x_radius=150, color=soda.hsl(s=82, l=62))
canvas.put(circ)
canvas.save("circ.png")

Oops, no picture
Actually, there is antialiasing in soda, but it's very buggy so it's disabled by default

Ellipse constructor takes four arguments:

  • center - point-like object that defines a center of ellipse
  • x_radius - horizontal radius of ellipse
  • y_radius - vertical radius of ellipse. default: passed x_radius
  • color - see Color notation. default: "black"

Ellipse has two specific methods:
Specific methods:
to_list(pos)
Takes a position and returns a two corners according to this position
(returns: list of point-like lists)

Pieslice

A little Pacman for free.

import soda
canvas = soda.Canvas(size=(500, 500), color="#121212")
pies = soda.Pieslice(center=(250, 250), x_radius=150, color=soda.hsl(s=82, l=62), start=45, stop=-45)
canvas.put(pies)
canvas.save("pies.png")

Oops, no picture
Circle is just a fancy pieslice

Pieslice constructor takes four arguments:

  • center - point-like object that defines a center of ellipse.
  • x_radius - horizontal radius of ellipse.
  • y_radius - vertical radius of ellipse. default: passed x_radius
  • color - see Color notation. default: "black"
  • start - start angle of the pie. default: 0
  • stop - stop angle of the pie. default: 360

Pieslice is rendered from start to stop, clockwise.
It shares methods with Ellipse and Shape. Also, it has methods start_set(start) and stop_set(stop) to set start and stop values.

Text

A simple way to write "Hello World" on your first image

import soda
canvas = soda.Canvas(size=(500, 500), color="#121212")
font_path = "/path/to/my/font.ttf"
text = soda.Text("soda", font=font_path, size=150, align="cc", color=soda.hsl(s=82, l=62))
canvas.put(text, position=(250, 250))
canvas.save("text.png")

Oops, no picture
The font is called TT Commons if you're curious

Text constructor takes six arguments:

  • text - a string itself.
  • font - path of the font file you want to use
  • size - size in px
  • position - point-like object that defines position. default: (0, 0)
  • align - string that defines align mode (more in Align section below). default: "cs"
  • color - see Color notation. default: "black"

Specific methods:
font_set(path, size)
Changes font and size
(returns: None)

size_set(size)
Changes size
(returns: None)

corners_get(position=(0, 0))
Returns a list of points considering align and specified position (returns: list of point-like lists)

Align

align is a string of two chars (first is horizontal align, second is vertical): c centers text regarding its position s places start of the text (left or top) to the position e places end of the text (right or bottom) to the position

In example, default "cs" centers text horizontally and pins its top to the vertical position

MaskShape

He was a boring gray mask, but became a colored shape

import soda
canvas = soda.Canvas(size=(500, 500), color="#121212")
mask = soda.MaskShape(mask="mask_pic.png", color=soda.hsl(s=82, l=62), position=(100, 100))
canvas.put(mask)
canvas.save("mask.png")

Oops, no picture
Another picture of UFO

MaskShape is a shape defined by a lightness mask. Black pixels of mask are transparent, and white pixels are "solid". Pretty simple.

MaskShape constructor takes four arguments:

  • mask - the mask itself (check Image notation)
  • color - color of the shape (check Color notation)
  • position - point-like object that defines position. default: (0, 0)
  • size - size to fit the shape in. If not specified, the shape would be at the size of original image default: None

Specific methods:
mask_set(mask)
Sets a mask to the shape (returns: None) mask_get Returns current mask of the shape. (returns: PIL.Image.Image object of the mask)


This part is still not documented. You might wait a few days weeks years.

FitBox

SodaImage

About

Simple drawing tool, running on Python 3 + Pillow

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages