Linocut

Pictures written in code, for games on cauldron_2d. What tuning_fork is to sound, this is to sprites: notation in Elixir, pure render functions, a disk cache, and a way to look at the result.

Installation

def deps do
  [
    {:linocut, "~> 0.2"}
  ]
end
ship =
  Linocut.sprite("""
  ..RR..
  .RWWR.
  RRWWRR
  """, R: "#c33", W: :white)

headings = Linocut.headings([{15, 0}, {-8, 8}, {-8, -8}], 64, size: {16, 16}, fill: "#e8e8e8")
red_team = Linocut.recolour(ship, %{{204, 51, 51, 255} => {51, 51, 204, 255}})
spin = Linocut.Strip.new(headings, 30)
Linocut.sprite/3 a picture with a palette legend, . and space transparent
Linocut.outline/2 a filled or stroked shape from its points, x right and y up
Linocut.headings/3 the shape at every heading, 0 pointing right, turning counter-clockwise
Linocut.recolour/2, tint/3, scale/2 derivations on any raster
Linocut.Derive a picture from another by a line on its keys: from knight; colour C #c02020; flip h; over hat 0 -2
Linocut.Pic the .pic file: a palette, a blank line, the picture; frames separated by ---
Linocut.Strip frames at a rate, looping
Linocut.Cache rasters rendered once, kept under ~/.cache/linocut
Linocut.Preview a sheet of many rasters, terminal output, PNG
Linocut.Quantise a picture reduced to a small palette, chosen by median cut over its opaque pixels
Linocut.Sheet a sprite sheet cut into frames, found as bands of rows and runs of columns between blank ones
Linocut.Frames frames of one animation lined up on one canvas, standing on the same floor, and written as a .pic with a shared palette
Linocut.Pose a figure's joints, and the OpenPose skeleton a ControlNet reads them as
Linocut.Resample a picture redrawn at another size, by area or nearest pixel
Linocut.Seam a picture made to repeat without a visible join, its far edge faded into the near one
mix linocut.import turns an image file into a .pic at a chosen size and palette
mix linocut.sheet cuts a sprite sheet into the frames of one .pic
mix linocut.join joins separately drawn pictures into the frames of one .pic
mix linocut.pose writes OpenPose skeletons for an animation cycle, one PNG a frame

Every result is a FrenchCurve.Raster, which Cauldron2D.Atlas.put/4 takes as it is.

Deriving

A .pic is keys, and its keys mean something: C is the cloak whatever colour it is. So a second view is a line, not a second drawing:

from knight; colour C #c02020            the same knight in red
from knight; flip h                       facing the other way
from knight; over hat 0 -2                with a hat on
from walk; frame 3                        one frame of an animation

Linocut.Derive.derive/2 runs such a line against a function that gives each name's text; Cauldron2D.Atlas.load/3 reads the same lines from the fifth field of atlas.txt and builds the derived arts after the drawn ones. The operations work on the text of the picture — reversing a row, transposing the grid, rewriting a palette line — so nothing is rendered until the atlas is built, and a derived picture is a picture like any other.

Looking at it

mix linocut.view ship.exs
mix linocut.view ship.exs --scale 3 --columns 8

mix linocut.view takes a script whose last expression is a raster, a list of them, or a strip, and shows it in this terminal as pixels or braille. The example scripts are in the repository.

License

MIT