Vivid.Frame (vivid v1.0.0)

Copy Markdown View Source

Frame represents a collection of colours and shapes.

Frame implements both the Enumerable and Collectable protocols.

Examples

  iex> use Vivid
  ...> Enum.map(1..5, fn i ->
  ...>   line = Line.init(Point.init(1,1), Point.init(20, i * 4))
  ...>   {line, RGBA.black}
  ...> end)
  ...> |> Enum.into(Frame.init(24, 21, RGBA.white))
  ...> |> to_string
  "@@@@@@@@@@@@@@@@@@@@ @@@\n" <>
  "@@@@@@@@@@@@@@@@@@@ @@@@\n" <>
  "@@@@@@@@@@@@@@@@@@ @@@@@\n" <>
  "@@@@@@@@@@@@@@@@@ @@@@@@\n" <>
  "@@@@@@@@@@@@@@@@ @@@ @@@\n" <>
  "@@@@@@@@@@@@@@@ @@@ @@@@\n" <>
  "@@@@@@@@@@@@@@ @@  @@@@@\n" <>
  "@@@@@@@@@@@@@ @@ @@@@@@@\n" <>
  "@@@@@@@@@@@@ @@ @@@@ @@@\n" <>
  "@@@@@@@@@@@ @@ @@@  @@@@\n" <>
  "@@@@@@@@@@ @  @@  @@@@@@\n" <>
  "@@@@@@@@@ @ @@  @@@@@@@@\n" <>
  "@@@@@@@@ @ @@ @@@@@  @@@\n" <>
  "@@@@@@@   @  @@@   @@@@@\n" <>
  "@@@@@@  @  @@@  @@@@@@@@\n" <>
  "@@@@@  @ @@   @@@@@@@@@@\n" <>
  "@@@@       @@@@@@    @@@\n" <>
  "@@@     @@@      @@@@@@@\n" <>
  "@@         @@@@@@@@@@@@@\n" <>
  "@    @@@@@@@@@@@@@@@@@@@\n" <>
  "@@@@@@@@@@@@@@@@@@@@@@@@\n"

Summary

Functions

Return the background colour of the frame.

Change the background colour of the frame.

Render a frame into a buffer for display horizontally.

Render a frame into a buffer for display.

Clear the frame of any shapes.

Return the height of the frame.

Add a shape to the frame buffer.

Return how many samples per pixel, on each axis, the frame is rendered with.

Render the frame with samples samples per pixel on each axis.

Return the width of the frame.

Types

t()

@type t() :: %Vivid.Frame{
  background_colour: Vivid.RGBA.t(),
  height: pos_integer(),
  samples: pos_integer(),
  shapes: [{Vivid.Shape.t(), Vivid.RGBA.t()}],
  width: pos_integer()
}

Functions

background_colour(frame)

@spec background_colour(t()) :: Vivid.RGBA.t()

Return the background colour of the frame.

Example

iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.background_colour
Vivid.RGBA.init(0, 0, 0, 0)

background_colour(frame, colour)

@spec background_colour(t(), Vivid.RGBA.t()) :: t()

Change the background colour of the frame.

Example

iex> Vivid.Frame.init(80,25)
...> |> Vivid.Frame.background_colour(Vivid.RGBA.white)
...> |> Vivid.Frame.background_colour
Vivid.RGBA.init(1, 1, 1, 1)

buffer(frame)

@spec buffer(t()) :: Vivid.Buffer.t()

Render a frame into a buffer for display horizontally.

Returns a one-dimensional List of RGBA colours with alpha-compositing completed.

buffer(frame, atom)

@spec buffer(t(), :horizontal | :vertical) :: Vivid.Buffer.t()

Render a frame into a buffer for display.

You can specify either :horizontal or :vertical mode, where in :horizontal mode the buffer is rendered row-by-row then column-by-column and in :vertical mode the buffer is rendered column-by-column then row-by-row.

Returns a one-dimensional List of RGBA colours with alpha-compositing completed.

clear(frame)

@spec clear(t()) :: t()

Clear the frame of any shapes.

height(frame)

@spec height(t()) :: integer()

Return the height of the frame.

Example

iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.height
25

init(width \\ 128, height \\ 64, colour \\ RGBA.init(0, 0, 0, 0))

@spec init(pos_integer(), pos_integer(), Vivid.RGBA.t()) :: t()

Initialize a frame buffer.

  • width the width of the frame, in pixels.
  • height the height of the frame, in pixels.
  • colour the default colour of the frame.

Example

iex> Vivid.Frame.init(4, 4)
Vivid.Frame.init(4, 4, Vivid.RGBA.init(0, 0, 0, 0))

push(frame, shape, colour)

@spec push(t(), Vivid.Shape.t(), Vivid.RGBA.t()) :: t()

Add a shape to the frame buffer.

  • frame is the frame to modify.
  • shape is the shape to add.
  • colour is the colour of the shape being added.

Examples

iex> Vivid.Frame.init(5,5)
...> |> Vivid.Frame.push(Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(3,3)), Vivid.RGBA.white)
...> |> to_string
"     \n" <>
"   @ \n" <>
"  @  \n" <>
" @   \n" <>
"     \n"

iex> Vivid.Frame.init(5,5)
...> |> Vivid.Frame.push(
...>      Vivid.Path.init([
...>        Vivid.Point.init(1,1),
...>        Vivid.Point.init(1,3),
...>        Vivid.Point.init(3,3),
...>        Vivid.Point.init(3,1),
...>      ]), Vivid.RGBA.white
...>    )
...> |> to_string
"     \n" <>
" @@@ \n" <>
" @ @ \n" <>
" @ @ \n" <>
"     \n"

iex> Vivid.Frame.init(5,5)
...> |> Vivid.Frame.push(
...>      Vivid.Polygon.init([
...>        Vivid.Point.init(1,1),
...>        Vivid.Point.init(1,3),
...>        Vivid.Point.init(3,3),
...>        Vivid.Point.init(3,1),
...>      ]), Vivid.RGBA.white
...>    )
...> |> to_string
"     \n" <>
" @@@ \n" <>
" @ @ \n" <>
" @@@ \n" <>
"     \n"

iex> circle = Vivid.Circle.init(Vivid.Point.init(5,5), 4)
...> Vivid.Frame.init(11, 10)
...> |> Vivid.Frame.push(circle, Vivid.RGBA.white)
...> |> to_string
"    @@@    \n" <>
"  @@   @@  \n" <>
"  @     @  \n" <>
" @       @ \n" <>
" @       @ \n" <>
" @       @ \n" <>
"  @     @  \n" <>
"  @@   @@  \n" <>
"    @@@    \n" <>
"           \n"

iex> line = Vivid.Line.init(Vivid.Point.init(0,0), Vivid.Point.init(50,50))
...> Vivid.Frame.init(5,5)
...> |> Vivid.Frame.push(line, Vivid.RGBA.white)
...> |> to_string
"    @\n" <>
"   @ \n" <>
"  @  \n" <>
" @   \n" <>
"@    \n"

samples(frame)

@spec samples(t()) :: pos_integer()

Return how many samples per pixel, on each axis, the frame is rendered with.

Example

iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.samples
1

samples(frame, samples)

@spec samples(t(), pos_integer()) :: t()

Render the frame with samples samples per pixel on each axis.

Shapes are rasterized at samples times the frame's size, and each pixel takes its alpha from the proportion of its samples the shape covered. That's what softens the stepping on any edge which isn't horizontal or vertical.

One sample per pixel - the default - is the aliased rendering this library has always done, and costs nothing extra. Anything higher costs samples squared as much rasterization, so 2 costs four times and 4 sixteen times.

Shapes which approximate a curve with straight lines - Vivid.Circle, Vivid.Arc, Vivid.Bezier, and the glyphs of an outline font - choose how finely to do that from their nominal size, before they know they're being sampled. Sampling magnifies the line segments they already had rather than smoothing them into a curve, so raise their step count too if the flat spots show.

Examples

A diagonal line at the default one sample per pixel, stepping between rows.

iex> use Vivid
...> line = Line.init(Point.init(0, 0), Point.init(11, 5))
...> Frame.init(12, 6, RGBA.white())
...> |> Frame.push(line, RGBA.black())
...> |> to_string()
"@@@@@@@@@@  \n" <>
"@@@@@@@@  @@\n" <>
"@@@@@@  @@@@\n" <>
"@@@@  @@@@@@\n" <>
"@@  @@@@@@@@\n" <>
"  @@@@@@@@@@\n"

The same line with four samples per pixel. The ASCII renderer maps luminance onto ten characters, so partly covered pixels come out as the ones in between.

iex> use Vivid
...> line = Line.init(Point.init(0, 0), Point.init(11, 5))
...> Frame.init(12, 6, RGBA.white())
...> |> Frame.push(line, RGBA.black())
...> |> Frame.samples(4)
...> |> to_string()
"@@@@@@@@@@%%\n" <>
"@@@@@@@@%+*@\n" <>
"@@@@@@#+*@@@\n" <>
"@@@@*+#@@@@@\n" <>
"@@++%@@@@@@@\n" <>
"++@@@@@@@@@@\n"

width(frame)

@spec width(t()) :: integer()

Return the width of the frame.

Example

iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.width
80