Run mix format

This commit is contained in:
James Harton 2018-01-19 09:06:04 +13:00
parent 472887e1da
commit 7fe467268e
105 changed files with 861 additions and 627 deletions

View file

@ -4,17 +4,21 @@ defimpl Collectable, for: Vivid.Frame do
@doc """
Collect an enumerable into a Frame.
"""
@spec into(Frame.t) :: {list, function}
@spec into(Frame.t()) :: {list, function}
def into(%Frame{shapes: shapes} = frame) do
{shapes, fn
[], {:cont, {_shape, _colour} = shape} ->
[shape]
new_shapes, {:cont, {_shape, _colour} = shape} ->
[shape | new_shapes]
new_shapes, :done ->
%{frame | shapes: shapes ++ Enum.reverse(new_shapes)}
_, :halt ->
:ok
end}
{shapes,
fn
[], {:cont, {_shape, _colour} = shape} ->
[shape]
new_shapes, {:cont, {_shape, _colour} = shape} ->
[shape | new_shapes]
new_shapes, :done ->
%{frame | shapes: shapes ++ Enum.reverse(new_shapes)}
_, :halt ->
:ok
end}
end
end

View file

@ -9,12 +9,13 @@ defimpl Collectable, for: Vivid.Group do
iex> [Vivid.Point.init(1,1)] |> Enum.into(Vivid.Group.init)
%Vivid.Group{shapes: MapSet.new([%Vivid.Point{x: 1, y: 1}])}
"""
@spec into(Group.t) :: {list, function}
@spec into(Group.t()) :: {list, function}
def into(%Group{shapes: shapes}) do
{shapes, fn
new_shapes, {:cont, shape} -> MapSet.put(new_shapes, shape)
new_shapes, :done -> Group.init(MapSet.union(shapes, new_shapes))
_, :halt -> :ok
end}
{shapes,
fn
new_shapes, {:cont, shape} -> MapSet.put(new_shapes, shape)
new_shapes, :done -> Group.init(MapSet.union(shapes, new_shapes))
_, :halt -> :ok
end}
end
end

View file

@ -9,12 +9,13 @@ defimpl Collectable, for: Vivid.Path do
iex> [Vivid.Point.init(1,1)] |> Enum.into(Vivid.Path.init)
%Vivid.Path{vertices: [%Vivid.Point{x: 1, y: 1}]}
"""
@spec into(Path.t) :: {list, function}
@spec into(Path.t()) :: {list, function}
def into(%Path{vertices: points}) do
{[], fn
list, {:cont, %Point{} = point} -> [point | list]
list, :done -> Path.init(points ++ list)
_, :halt -> :ok
end}
{[],
fn
list, {:cont, %Point{} = point} -> [point | list]
list, :done -> Path.init(points ++ list)
_, :halt -> :ok
end}
end
end

View file

@ -9,12 +9,13 @@ defimpl Collectable, for: Vivid.Polygon do
iex> [Vivid.Point.init(1,1)] |> Enum.into(Vivid.Polygon.init)
%Vivid.Polygon{vertices: [%Vivid.Point{x: 1, y: 1}]}
"""
@spec into(Polygon.t) :: {list, function}
@spec into(Polygon.t()) :: {list, function}
def into(%Polygon{vertices: points, fill: fill}) do
{[], fn
list, {:cont, %Point{} = point} -> [point | list]
list, :done -> Polygon.init(points ++ list, fill)
_, :halt -> :ok
end}
{[],
fn
list, {:cont, %Point{} = point} -> [point | list]
list, :done -> Polygon.init(points ++ list, fill)
_, :halt -> :ok
end}
end
end

View file

@ -8,25 +8,27 @@ defimpl Enumerable, for: Vivid.Buffer do
@doc """
Returns the number of pixels in a buffer.
"""
@spec count(Buffer.t) :: {:ok, non_neg_integer}
@spec count(Buffer.t()) :: {:ok, non_neg_integer}
def count(%Buffer{buffer: buffer}), do: {:ok, Enum.count(buffer)}
@doc """
Returns whether a colour is a member of a buffer.
This is mostly useless, but it's part of the Enumerable protocol.
"""
@spec member?(Buffer.t, RGBA.t) :: {:ok, boolean}
@spec member?(Buffer.t(), RGBA.t()) :: {:ok, boolean}
def member?(%Buffer{buffer: buffer}, colour), do: {:ok, Enum.member?(buffer, colour)}
@doc """
Reduce the buffer into an accumulator.
"""
@spec reduce(Buffer.t, Collectable.t, (any, Collectable.t -> Collectable.t)) :: Collectable.t
@spec reduce(Buffer.t(), Collectable.t(), (any, Collectable.t() -> Collectable.t())) ::
Collectable.t()
def reduce(%Buffer{buffer: buffer}, acc, fun), do: Enumerable.List.reduce(buffer, acc, fun)
@doc """
Slice the buffer.
"""
@spec slice(Buffer.t) :: {:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
@spec slice(Buffer.t()) ::
{:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
def slice(%Buffer{buffer: buffer}), do: Enumerable.List.slice(buffer)
end

View file

@ -14,7 +14,7 @@ defimpl Enumerable, for: Vivid.Frame do
...> |> Enum.count
2
"""
@spec count(Frame.t) :: {:ok, non_neg_integer}
@spec count(Frame.t()) :: {:ok, non_neg_integer}
def count(%Frame{shapes: shapes}), do: {:ok, Enum.count(shapes)}
@doc """
@ -30,7 +30,7 @@ defimpl Enumerable, for: Vivid.Frame do
...> |> Enum.member?(Vivid.Point.init(2,2))
false
"""
@spec member?(Frame.t, Shape.t) :: {:ok, boolean}
@spec member?(Frame.t(), Shape.t()) :: {:ok, boolean}
def member?(%Frame{shapes: shapes}, shape), do: {:ok, Enum.member?(shapes, shape)}
@doc """
@ -41,12 +41,14 @@ defimpl Enumerable, for: Vivid.Frame do
iex> Vivid.Frame.init([Vivid.Point.init(1,2), Vivid.Point.init(2,4)]) |> Enum.reduce(%{}, fn (%Vivid.Point{x: x, y: y}, acc) -> Map.put(acc, x, y) end)
%{1 => 2, 2 => 4}
"""
@spec reduce(Frame.t, Collectable.t, (any, Collectable.t -> Collectable.t)) :: Collectable.t
@spec reduce(Frame.t(), Collectable.t(), (any, Collectable.t() -> Collectable.t())) ::
Collectable.t()
def reduce(%Frame{shapes: shapes}, acc, fun), do: Enumerable.List.reduce(shapes, acc, fun)
@doc """
Slice the frame.
"""
@spec slice(Frame.t) :: {:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
@spec slice(Frame.t()) ::
{:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
def slice(%Frame{shapes: shapes}), do: Enumerable.List.slice(shapes)
end

View file

@ -14,7 +14,7 @@ defimpl Enumerable, for: Vivid.Group do
...> |> Enum.count
2
"""
@spec count(Group.t) :: {:ok, non_neg_integer}
@spec count(Group.t()) :: {:ok, non_neg_integer}
def count(%Group{shapes: shapes}), do: {:ok, Enum.count(shapes)}
@doc """
@ -30,7 +30,7 @@ defimpl Enumerable, for: Vivid.Group do
...> |> Enum.member?(Vivid.Point.init(2,2))
false
"""
@spec member?(Group.t, Shape.t) :: {:ok, boolean}
@spec member?(Group.t(), Shape.t()) :: {:ok, boolean}
def member?(%Group{shapes: shapes}, shape), do: {:ok, Enum.member?(shapes, shape)}
@doc """
@ -41,12 +41,14 @@ defimpl Enumerable, for: Vivid.Group do
iex> Vivid.Group.init([Vivid.Point.init(1,2), Vivid.Point.init(2,4)]) |> Enum.reduce(%{}, fn (%Vivid.Point{x: x, y: y}, acc) -> Map.put(acc, x, y) end)
%{1 => 2, 2 => 4}
"""
@spec reduce(Group.t, Collectable.t, (Shape.t, Collectable.t -> Collectable.t)) :: Collectable.t
@spec reduce(Group.t(), Collectable.t(), (Shape.t(), Collectable.t() -> Collectable.t())) ::
Collectable.t()
def reduce(%Group{shapes: shapes}, acc, fun), do: Enumerable.MapSet.reduce(shapes, acc, fun)
@doc """
Slice the group.
"""
@spec slice(Group.t) :: {:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
@spec slice(Group.t()) ::
{:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
def slice(%Group{shapes: shapes}), do: Enumerable.MapSet.slice(shapes)
end

View file

@ -15,7 +15,7 @@ defimpl Enumerable, for: Vivid.Line do
...> |> Enum.count
2
"""
@spec count(Line.t) :: non_neg_integer
@spec count(Line.t()) :: non_neg_integer
def count(%Line{}), do: {:ok, 2}
@doc """
@ -34,7 +34,7 @@ defimpl Enumerable, for: Vivid.Line do
...> |> Enum.member?(Point.init(2,2))
true
"""
@spec member?(Line.t, Point.t) :: boolean
@spec member?(Line.t(), Point.t()) :: boolean
def member?(%Line{origin: p0}, point) when p0 == point, do: {:ok, true}
def member?(%Line{termination: p0}, point) when p0 == point, do: {:ok, true}
def member?(_line, _point), do: {:ok, false}
@ -49,12 +49,15 @@ defimpl Enumerable, for: Vivid.Line do
...> |> Enum.reduce(%{}, fn point, points -> Map.put(points, Point.x(point), Point.y(point)) end)
%{1 => 2, 2 => 4}
"""
@spec reduce(Line.t, Collectable.t, (Point.t, Collectable.t -> Collectable.t)) :: Collectable.t
def reduce(%Line{origin: p0, termination: p1}, acc, fun), do: Enumerable.List.reduce([p0, p1], acc, fun)
@spec reduce(Line.t(), Collectable.t(), (Point.t(), Collectable.t() -> Collectable.t())) ::
Collectable.t()
def reduce(%Line{origin: p0, termination: p1}, acc, fun),
do: Enumerable.List.reduce([p0, p1], acc, fun)
@doc """
Slices the line.
"""
@spec slice(Line.t) :: {:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
@spec slice(Line.t()) ::
{:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
def slice(%Line{origin: p0, termination: p1}), do: Enumerable.List.slice([p0, p1])
end

View file

@ -13,7 +13,7 @@ defimpl Enumerable, for: Vivid.Path do
iex> Vivid.Path.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Enum.count
2
"""
@spec count(Path.t) :: {:ok, non_neg_integer}
@spec count(Path.t()) :: {:ok, non_neg_integer}
def count(%Path{vertices: points}), do: {:ok, Enum.count(points)}
@doc """
@ -28,7 +28,7 @@ defimpl Enumerable, for: Vivid.Path do
iex> Vivid.Path.init([Vivid.Point.init(1,1)]) |> Enum.member?(Vivid.Point.init(2,2))
false
"""
@spec member?(Path.t, Point.t) :: {:ok, boolean}
@spec member?(Path.t(), Point.t()) :: {:ok, boolean}
def member?(%Path{vertices: points}, %Point{} = point), do: {:ok, Enum.member?(points, point)}
@doc """
@ -39,12 +39,14 @@ defimpl Enumerable, for: Vivid.Path do
iex> Vivid.Path.init([Vivid.Point.init(1,2), Vivid.Point.init(2,4)]) |> Enum.reduce(%{}, fn (%Vivid.Point{x: x, y: y}, acc) -> Map.put(acc, x, y) end)
%{1 => 2, 2 => 4}
"""
@spec reduce(Path.t, Collectable.t, (Point.t, Collectable.t -> Collectable.t)) :: Collectable.t
@spec reduce(Path.t(), Collectable.t(), (Point.t(), Collectable.t() -> Collectable.t())) ::
Collectable.t()
def reduce(%Path{vertices: points}, acc, fun), do: Enumerable.List.reduce(points, acc, fun)
@doc """
Slices the Path.
"""
@spec slice(Path.t) :: {:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
@spec slice(Path.t()) ::
{:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
def slice(%Path{vertices: points}), do: Enumerable.List.slice(points)
end

View file

@ -13,7 +13,7 @@ defimpl Enumerable, for: Vivid.Polygon do
iex> Vivid.Polygon.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Enum.count
2
"""
@spec count(Polygon.t) :: {:ok, non_neg_integer}
@spec count(Polygon.t()) :: {:ok, non_neg_integer}
def count(%Polygon{vertices: points}), do: {:ok, Enum.count(points)}
@doc """
@ -28,8 +28,9 @@ defimpl Enumerable, for: Vivid.Polygon do
iex> Vivid.Polygon.init([Vivid.Point.init(1,1)]) |> Enum.member?(Vivid.Point.init(2,2))
false
"""
@spec member?(Polygon.t, Point.t) :: {:ok, boolean}
def member?(%Polygon{vertices: points}, %Point{} = point), do: {:ok, Enum.member?(points, point)}
@spec member?(Polygon.t(), Point.t()) :: {:ok, boolean}
def member?(%Polygon{vertices: points}, %Point{} = point),
do: {:ok, Enum.member?(points, point)}
@doc """
Reduces the Polygon's vertices into an accumulator.
@ -39,12 +40,14 @@ defimpl Enumerable, for: Vivid.Polygon do
iex> Vivid.Polygon.init([Vivid.Point.init(1,2), Vivid.Point.init(2,4)]) |> Enum.reduce(%{}, fn (%Vivid.Point{x: x, y: y}, acc) -> Map.put(acc, x, y) end)
%{1 => 2, 2 => 4}
"""
@spec reduce(Polygon.t, Collectable.t, (Point.t, Collectable.t -> Collectable.t)) :: Collectable.t
@spec reduce(Polygon.t(), Collectable.t(), (Point.t(), Collectable.t() -> Collectable.t())) ::
Collectable.t()
def reduce(%Polygon{vertices: points}, acc, fun), do: Enumerable.List.reduce(points, acc, fun)
@doc """
Slices the Polygon.
"""
@spec slice(Polygon.t) :: {:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
@spec slice(Polygon.t()) ::
{:ok, size :: non_neg_integer(), Enumerable.slicing_fun()} | {:error, module()}
def slice(%Polygon{vertices: points}), do: Enumerable.List.slice(points)
end

View file

@ -3,20 +3,22 @@ defimpl Inspect, for: Vivid.Arc do
import Inspect.Algebra
@doc false
@spec inspect(Arc.t, any) :: String.t
@spec inspect(Arc.t(), any) :: String.t()
def inspect(arc, opts) do
center = arc |> Arc.center
radius = arc |> Arc.radius
start_angle = arc |> Arc.start_angle
range = arc |> Arc.range
steps = arc |> Arc.steps
center = arc |> Arc.center()
radius = arc |> Arc.radius()
start_angle = arc |> Arc.start_angle()
range = arc |> Arc.range()
steps = arc |> Arc.steps()
details = [
center: center,
radius: radius,
center: center,
radius: radius,
start_angle: start_angle,
range: range,
steps: steps
range: range,
steps: steps
]
concat ["#Vivid.Arc<", to_doc(details, opts), ">"]
concat(["#Vivid.Arc<", to_doc(details, opts), ">"])
end
end

View file

@ -5,8 +5,8 @@ defimpl Inspect, for: Vivid.Bounds do
@doc """
Defines the inspect protocol for `Bounds`.
"""
@spec inspect(Bounds.t, any) :: String.t
@spec inspect(Bounds.t(), any) :: String.t()
def inspect(%Bounds{min: min, max: max}, opts) do
concat ["#Vivid.Bounds<", to_doc([min: min, max: max], opts), ">"]
concat(["#Vivid.Bounds<", to_doc([min: min, max: max], opts), ">"])
end
end

View file

@ -5,12 +5,12 @@ defimpl Inspect, for: Vivid.Box do
@doc """
Defines the inspect protocol for `Box`.
"""
@spec inspect(Box.t, any) :: String.t
@spec inspect(Box.t(), any) :: String.t()
def inspect(%Box{bottom_left: bl, top_right: tr, fill: true}, opts) do
concat ["#Vivid.Box<", to_doc([:filled, bottom_left: bl, top_right: tr], opts), ">"]
concat(["#Vivid.Box<", to_doc([:filled, bottom_left: bl, top_right: tr], opts), ">"])
end
def inspect(%Box{bottom_left: bl, top_right: tr}, opts) do
concat ["#Vivid.Box<", to_doc([bottom_left: bl, top_right: tr], opts), ">"]
concat(["#Vivid.Box<", to_doc([bottom_left: bl, top_right: tr], opts), ">"])
end
end

View file

@ -5,8 +5,8 @@ defimpl Inspect, for: Vivid.Buffer do
@doc """
Defines the inspect protocol for `Buffer`.
"""
@spec inspect(Buffer.t, any) :: String.t
@spec inspect(Buffer.t(), any) :: String.t()
def inspect(%Buffer{rows: r, columns: c}, opts) do
concat ["#Vivid.Buffer<", to_doc([rows: r, columns: c, size: r * c], opts), ">"]
concat(["#Vivid.Buffer<", to_doc([rows: r, columns: c, size: r * c], opts), ">"])
end
end

View file

@ -5,14 +5,14 @@ defimpl Inspect, for: Vivid.Circle do
@doc """
Defines the inspect protocol for `Circle`.
"""
@spec inspect(Cirlcle.t, any) :: String.t
@spec inspect(Cirlcle.t(), any) :: String.t()
def inspect(%Circle{center: c, radius: r, fill: true}, opts) do
details = [center: c, radius: r, fill: true]
concat ["#Vivid.Circle<", to_doc(details, opts), ">"]
concat(["#Vivid.Circle<", to_doc(details, opts), ">"])
end
def inspect(%Circle{center: c, radius: r}, opts) do
details = [center: c, radius: r]
concat ["#Vivid.Circle<", to_doc(details, opts), ">"]
concat(["#Vivid.Circle<", to_doc(details, opts), ">"])
end
end

View file

@ -5,14 +5,16 @@ defimpl Inspect, for: Vivid.Frame do
@doc """
Defines the inspect protocol for `Frame`.
"""
@spec inspect(Frame.t, any) :: String.t
@spec inspect(Frame.t(), any) :: String.t()
def inspect(frame, opts) do
width = Frame.width(frame)
width = Frame.width(frame)
height = Frame.height(frame)
colour = Frame.background_colour(frame)
concat ["#Vivid.Frame<", to_doc([width: width,
height: height,
background_colour: colour
], opts), ">"]
concat([
"#Vivid.Frame<",
to_doc([width: width, height: height, background_colour: colour], opts),
">"
])
end
end

View file

@ -5,9 +5,9 @@ defimpl Inspect, for: Vivid.Group do
@doc """
Defines the inspect protocol for `Group`.
"""
@spec inspect(Group.t, any) :: String.t
@spec inspect(Group.t(), any) :: String.t()
def inspect(%Group{shapes: shapes}, opts) do
shapes = shapes |> Enum.to_list
concat ["#Vivid.Group<", to_doc(shapes, opts), ">"]
shapes = shapes |> Enum.to_list()
concat(["#Vivid.Group<", to_doc(shapes, opts), ">"])
end
end

View file

@ -5,12 +5,13 @@ defimpl Inspect, for: Vivid.Line do
@doc """
Defines the inspect protocol for `Line`.
"""
@spec inspect(Line.t, any) :: String.t
@spec inspect(Line.t(), any) :: String.t()
def inspect(line, opts) do
details = [
origin: Line.origin(line),
origin: Line.origin(line),
termination: Line.termination(line)
]
concat ["#Vivid.Line<", to_doc(details, opts), ">"]
concat(["#Vivid.Line<", to_doc(details, opts), ">"])
end
end

View file

@ -5,8 +5,8 @@ defimpl Inspect, for: Vivid.Path do
@doc """
Defines the inspect protocol for `Path`.
"""
@spec inspect(Path.t, any) :: String.t
@spec inspect(Path.t(), any) :: String.t()
def inspect(%Path{vertices: points}, opts) do
concat ["#Vivid.Path<", to_doc(points, opts), ">"]
concat(["#Vivid.Path<", to_doc(points, opts), ">"])
end
end

View file

@ -5,10 +5,10 @@ defimpl Inspect, for: Vivid.Point do
@doc """
Defines the inspect protocol for `Point`.
"""
@spec inspect(Point.t, any) :: String.t
@spec inspect(Point.t(), any) :: String.t()
def inspect(point, opts) do
x = point |> Point.x
y = point |> Point.y
concat ["#Vivid.Point<", to_doc({x, y}, opts), ">"]
x = point |> Point.x()
y = point |> Point.y()
concat(["#Vivid.Point<", to_doc({x, y}, opts), ">"])
end
end

View file

@ -5,12 +5,12 @@ defimpl Inspect, for: Vivid.Polygon do
@doc """
Defines the inspect protocol for `Polygon`.
"""
@spec inspect(Polygon.t, any) :: String.t
@spec inspect(Polygon.t(), any) :: String.t()
def inspect(%Polygon{vertices: points, fill: true}, opts) do
concat ["#Vivid.Polygon<", to_doc([:filled | points], opts), ">"]
concat(["#Vivid.Polygon<", to_doc([:filled | points], opts), ">"])
end
def inspect(%Polygon{vertices: points}, opts) do
concat ["#Vivid.Polygon<", to_doc(points, opts), ">"]
concat(["#Vivid.Polygon<", to_doc(points, opts), ">"])
end
end

View file

@ -5,12 +5,12 @@ defimpl Inspect, for: Vivid.RGBA do
@doc """
Defines the inspect protocol for `RGBA`.
"""
@spec inspect(RGBA.t, any) :: String.t
@spec inspect(RGBA.t(), any) :: String.t()
def inspect(colour, opts) do
red = RGBA.red(colour)
red = RGBA.red(colour)
green = RGBA.green(colour)
blue = RGBA.blue(colour)
blue = RGBA.blue(colour)
alpha = RGBA.alpha(colour)
concat ["#Vivid.RGBA<", to_doc({red, green, blue, alpha}, opts), ">"]
concat(["#Vivid.RGBA<", to_doc({red, green, blue, alpha}, opts), ">"])
end
end

View file

@ -6,11 +6,13 @@ defimpl Inspect, for: Vivid.Transform do
@doc """
Defines the inspect protocol for `Transform`.
"""
@spec inspect(Transform.t, any) :: String.t
@spec inspect(Transform.t(), any) :: String.t()
def inspect(%Transform{operations: operations, shape: shape}, opts) do
operations = operations
operations =
operations
|> Stream.map(fn %Operation{name: name} -> name end)
|> Enum.reverse
concat ["#Vivid.Transform<", to_doc([operations: operations, shape: shape], opts), ">"]
|> Enum.reverse()
concat(["#Vivid.Transform<", to_doc([operations: operations, shape: shape], opts), ">"])
end
end

View file

@ -4,18 +4,20 @@ defimpl String.Chars, for: Vivid.Buffer do
@doc """
Convert a `buffer` into a `string` for `IO.puts`, etc.
"""
@spec to_string(Buffer.t) :: String.t
@spec to_string(Buffer.t()) :: String.t()
def to_string(%Buffer{buffer: buffer, columns: columns} = _buffer) do
s = buffer
|> Enum.reverse
s =
buffer
|> Enum.reverse()
|> Enum.chunk(columns)
|> Enum.map(fn (row) ->
|> Enum.map(fn row ->
row
|> Enum.reverse
|> Enum.reverse()
|> Enum.map(&RGBA.to_ascii(&1))
|> Enum.join
|> Enum.join()
end)
|> Enum.join("\n")
s <> "\n"
end
end

View file

@ -4,10 +4,10 @@ defimpl String.Chars, for: Vivid.Frame do
@doc """
Convert a `frame` into a `string` for `IO.puts`, etc.
"""
@spec to_string(Frame.t) :: String.t
@spec to_string(Frame.t()) :: String.t()
def to_string(%Frame{} = frame) do
frame
|> Frame.buffer
|> Kernel.to_string
|> Frame.buffer()
|> Kernel.to_string()
end
end

View file

@ -8,25 +8,27 @@ defmodule Vivid.ShapeToString do
@doc """
Convert a `shape` into a `string` for `IO.puts`, etc.
"""
@spec to_string(Shape.t) :: String.t
@spec to_string(Shape.t()) :: String.t()
def to_string(shape) do
bounds = Bounds.bounds(shape)
width = round(Bounds.width(bounds) + 3)
width = round(Bounds.width(bounds) + 3)
height = round(Bounds.height(bounds) + 3)
frame = Frame.init(width, height, RGBA.white)
frame = Frame.init(width, height, RGBA.white())
shape = shape
shape =
shape
|> Transform.center(frame)
|> Transform.apply
|> Transform.apply()
frame
|> Frame.push(shape, RGBA.black)
|> Kernel.to_string
|> Frame.push(shape, RGBA.black())
|> Kernel.to_string()
end
end
Enum.each(~w(Arc Box Circle Group Line Path Polygon), fn type ->
mod = Module.concat(Vivid, type)
defimpl String.Chars, for: mod do
@moduledoc """
Convert a shape into a string.
@ -35,7 +37,7 @@ Enum.each(~w(Arc Box Circle Group Line Path Polygon), fn type ->
@doc """
Convert `shape` into a `string` for `IO.puts`, etc.
"""
@spec to_string(Shape.t) :: String.t
@spec to_string(Shape.t()) :: String.t()
def to_string(shape), do: Vivid.ShapeToString.to_string(shape)
end
end)

View file

@ -1,9 +1,26 @@
defmodule Vivid do
defmacro __using__(_opts) do
quote do
alias Vivid.{Arc, Bounds, Buffer, Circle, Font, Frame, Group, Font, Frame,
Group, Line, Path, Point, Polygon, Rasterize, RGBA,
Transform, Box}
alias Vivid.{
Arc,
Bounds,
Buffer,
Circle,
Font,
Frame,
Group,
Font,
Frame,
Group,
Line,
Path,
Point,
Polygon,
Rasterize,
RGBA,
Transform,
Box
}
end
end

View file

@ -24,7 +24,7 @@ defmodule Vivid.Arc do
"""
@opaque t :: %Arc{center: Point.t, radius: number, start_angle: number, steps: integer}
@opaque t :: %Arc{center: Point.t(), radius: number, start_angle: number, steps: integer}
@doc ~S"""
Creates an Arc.
@ -46,19 +46,15 @@ defmodule Vivid.Arc do
steps: 12
}
"""
@spec init(Point.t, number, number, number, integer) :: Arc.t
@spec init(Point.t(), number, number, number, integer) :: Arc.t()
def init(%Point{} = center, radius, start_angle, range, steps \\ 12)
when is_number(radius)
and is_number(start_angle)
and is_number(range)
and is_integer(steps)
do
when is_number(radius) and is_number(start_angle) and is_number(range) and is_integer(steps) do
%Arc{
center: center,
radius: radius,
center: center,
radius: radius,
start_angle: start_angle,
range: range,
steps: steps
range: range,
steps: steps
}
end
@ -71,7 +67,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.center
#Vivid.Point<{10, 10}>
"""
@spec center(Arc.t) :: Point.t
@spec center(Arc.t()) :: Point.t()
def center(%Arc{center: p} = _arc), do: p
@doc """
@ -84,7 +80,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.center
#Vivid.Point<{15, 15}>
"""
@spec center(Arc.t, Point.t) :: Arc.t
@spec center(Arc.t(), Point.t()) :: Arc.t()
def center(%Arc{} = arc, %Point{} = point), do: %{arc | center: point}
@doc """
@ -96,7 +92,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.radius
5
"""
@spec radius(Arc.t) :: number
@spec radius(Arc.t()) :: number
def radius(%Arc{radius: r} = _arc), do: r
@doc """
@ -109,7 +105,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.radius
10
"""
@spec radius(Arc.t, number) :: Arc.t
@spec radius(Arc.t(), number) :: Arc.t()
def radius(%Arc{} = arc, radius) when is_number(radius), do: %{arc | radius: radius}
@doc """
@ -121,7 +117,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.start_angle
0
"""
@spec start_angle(Arc.t) :: number
@spec start_angle(Arc.t()) :: number
def start_angle(%Arc{start_angle: a} = _arc), do: a
@doc """
@ -134,7 +130,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.start_angle
45
"""
@spec start_angle(Arc.t, number) :: Arc.t
@spec start_angle(Arc.t(), number) :: Arc.t()
def start_angle(%Arc{} = arc, theta), do: %{arc | start_angle: theta}
@doc """
@ -146,7 +142,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.range
90
"""
@spec range(Arc.t) :: number
@spec range(Arc.t()) :: number
def range(%Arc{range: r} = _arc), do: r
@doc """
@ -159,7 +155,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.range
270
"""
@spec range(Arc.t, number) :: Arc.t
@spec range(Arc.t(), number) :: Arc.t()
def range(%Arc{} = arc, theta) when is_number(theta), do: %{arc | range: theta}
@doc """
@ -171,7 +167,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.steps
12
"""
@spec steps(Arc.t) :: integer
@spec steps(Arc.t()) :: integer
def steps(%Arc{steps: s} = _arc), do: s
@doc """
@ -184,7 +180,7 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.steps
19
"""
@spec steps(Arc.t, integer) :: Arc.t
@spec steps(Arc.t(), integer) :: Arc.t()
def steps(%Arc{} = arc, steps) when is_integer(steps), do: %{arc | steps: steps}
@doc """
@ -197,25 +193,29 @@ defmodule Vivid.Arc do
...> |> Vivid.Arc.to_path
#Vivid.Path<[#Vivid.Point<{5, 10}>, #Vivid.Point<{6, 13}>, #Vivid.Point<{8, 14}>, #Vivid.Point<{10, 15}>]>
"""
@spec to_path(Arc.t) :: Path.t
def to_path(%Arc{center: center, radius: radius, start_angle: start_angle, range: range, steps: steps} = _arc) do
h = center |> Point.x
k = center |> Point.y
@spec to_path(Arc.t()) :: Path.t()
def to_path(
%Arc{center: center, radius: radius, start_angle: start_angle, range: range, steps: steps} =
_arc
) do
h = center |> Point.x()
k = center |> Point.y()
step_degree = range / steps
start_angle = start_angle - 180
points = Enum.map(0..steps, fn(step) ->
theta = (step_degree * step) + start_angle
theta = degrees_to_radians(theta)
points =
Enum.map(0..steps, fn step ->
theta = step_degree * step + start_angle
theta = degrees_to_radians(theta)
x = round(h + radius * cos(theta))
y = round(k - radius * sin(theta))
x = round(h + radius * cos(theta))
y = round(k - radius * sin(theta))
Point.init(x, y)
end)
Point.init(x, y)
end)
points
|> Path.init
|> Path.init()
end
end

View file

@ -13,7 +13,7 @@ defmodule Vivid.Bounds do
#Vivid.Bounds<[min: #Vivid.Point<{5, 10}>, max: #Vivid.Point<{15, 20}>]>
"""
@opaque t :: %Bounds{min: Point.t, max: Point.t}
@opaque t :: %Bounds{min: Point.t(), max: Point.t()}
@doc """
Initialise arbitrary bounds.
@ -28,7 +28,7 @@ defmodule Vivid.Bounds do
iex> Vivid.Bounds.init(0, 0, 5, 5)
#Vivid.Bounds<[min: #Vivid.Point<{0, 0}>, max: #Vivid.Point<{5, 5}>]>
"""
@spec init(number, number, number, number) :: Bounds.t
@spec init(number, number, number, number) :: Bounds.t()
def init(x0, y0, x1, y1), do: %Bounds{min: Point.init(x0, y0), max: Point.init(x1, y1)}
@doc """
@ -42,8 +42,9 @@ defmodule Vivid.Bounds do
...> |> Vivid.Bounds.bounds
#Vivid.Bounds<[min: #Vivid.Point<{0.0, 0.0}>, max: #Vivid.Point<{20.0, 20.0}>]>
"""
@spec bounds(Shape.t) :: Bounds.t
@spec bounds(Shape.t()) :: Bounds.t()
def bounds(%Bounds{} = shape), do: shape
def bounds(shape) do
{min, max} = Of.bounds(shape)
%Bounds{min: min, max: max}
@ -60,7 +61,7 @@ defmodule Vivid.Bounds do
...> |> Vivid.Bounds.width
20.0
"""
@spec width(Shape.t) :: number
@spec width(Shape.t()) :: number
def width(%Bounds{min: %Point{x: x0}, max: %Point{x: x1}} = _shape), do: abs(x1 - x0)
def width(shape), do: shape |> bounds |> width
@ -75,7 +76,7 @@ defmodule Vivid.Bounds do
...> |> Vivid.Bounds.height
20.0
"""
@spec height(Shape.t) :: number
@spec height(Shape.t()) :: number
def height(%Bounds{min: %Point{y: y0}, max: %Point{y: y1}}), do: abs(y1 - y0)
def height(shape), do: shape |> bounds |> height
@ -90,7 +91,7 @@ defmodule Vivid.Bounds do
...> |> Vivid.Bounds.min
#Vivid.Point<{0.0, 0.0}>
"""
@spec min(Shape.t) :: Point.t
@spec min(Shape.t()) :: Point.t()
def min(%Bounds{min: min} = _shape), do: min
def min(shape), do: shape |> bounds |> min
@ -105,7 +106,7 @@ defmodule Vivid.Bounds do
...> |> Vivid.Bounds.max
#Vivid.Point<{20.0, 20.0}>
"""
@spec max(Shape.t) :: Point.t
@spec max(Shape.t()) :: Point.t()
def max(%Bounds{max: max}), do: max
def max(shape), do: shape |> bounds |> max
@ -121,12 +122,13 @@ defmodule Vivid.Bounds do
...> |> Vivid.Bounds.center_of
#Vivid.Point<{10.0, 10.0}>
"""
@spec center_of(Shape.t) :: Point.t
@spec center_of(Shape.t()) :: Point.t()
def center_of(%Bounds{min: %Point{x: x0, y: y0}, max: %Point{x: x1, y: y1}}) do
x = x0 + (x1 - x0) / 2
y = y0 + (y1 - y0) / 2
Point.init(x, y)
end
def center_of(shape), do: shape |> bounds |> center_of
@doc """
@ -161,7 +163,13 @@ defmodule Vivid.Bounds do
...> |> Vivid.Bounds.contains?(Vivid.Point.init(11, 11))
false
"""
@spec contains?(Shape.t, Point.t) :: boolean
def contains?(%Bounds{min: %Point{x: x0, y: y0}, max: %Point{x: x1, y: y1}} = _shape, %Point{x: x, y: y} = _point) when x0 <= x and x <= x1 and y0 <= y and y <= y1, do: true
@spec contains?(Shape.t(), Point.t()) :: boolean
def contains?(
%Bounds{min: %Point{x: x0, y: y0}, max: %Point{x: x1, y: y1}} = _shape,
%Point{x: x, y: y} = _point
)
when x0 <= x and x <= x1 and y0 <= y and y <= y1,
do: true
def contains?(_shape, _point), do: false
end

View file

@ -1,5 +1,6 @@
defprotocol Vivid.Bounds.Of do
alias Vivid.{Shape, Point}
@moduledoc """
This protocol is used to calculate the bounds of a given shape.
@ -10,6 +11,6 @@ defprotocol Vivid.Bounds.Of do
Return the bounds of a Shape as a two element tuple of bottom-left and
top-right points.
"""
@spec bounds(Shape.t) :: {Point.t, Point.t}
@spec bounds(Shape.t()) :: {Point.t(), Point.t()}
def bounds(shape)
end

View file

@ -6,10 +6,10 @@ defimpl Vivid.Bounds.Of, for: Vivid.Arc do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Arc.t) :: {Point.t, Point.t}
@spec bounds(Arc.t()) :: {Point.t(), Point.t()}
def bounds(arc) do
arc
|> Arc.to_path
|> Bounds.Of.bounds
|> Arc.to_path()
|> Bounds.Of.bounds()
end
end

View file

@ -6,6 +6,6 @@ defimpl Vivid.Bounds.Of, for: Vivid.Bounds do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Bounds.t) :: {Point.t, Point.t}
@spec bounds(Bounds.t()) :: {Point.t(), Point.t()}
def bounds(%Bounds{min: min, max: max} = _bounds), do: {min, max}
end

View file

@ -6,6 +6,6 @@ defimpl Vivid.Bounds.Of, for: Vivid.Box do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Box.t) :: {Point.t, Point.t}
@spec bounds(Box.t()) :: {Point.t(), Point.t()}
def bounds(%Box{bottom_left: bl, top_right: tr} = _box), do: {bl, tr}
end

View file

@ -6,10 +6,10 @@ defimpl Vivid.Bounds.Of, for: Vivid.Circle do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Circle.t) :: {Point.t, Point.t}
@spec bounds(Circle.t()) :: {Point.t(), Point.t()}
def bounds(circle) do
circle
|> Circle.to_polygon
|> Bounds.Of.bounds
|> Circle.to_polygon()
|> Bounds.Of.bounds()
end
end

View file

@ -6,7 +6,7 @@ defimpl Vivid.Bounds.Of, for: Vivid.Frame do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Frame.t) :: {Point.t, Point.t}
@spec bounds(Frame.t()) :: {Point.t(), Point.t()}
def bounds(%Frame{width: w, height: h} = _frame) do
{Point.init(0, 0), Point.init(w - 1, h - 1)}
end

View file

@ -6,12 +6,14 @@ defimpl Vivid.Bounds.Of, for: Vivid.Group do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Group.t) :: {Point.t, Point.t}
@spec bounds(Group.t()) :: {Point.t(), Point.t()}
def bounds(%Vivid.Group{shapes: shapes} = _group) do
shapes
|> Enum.map(&Vivid.Bounds.Of.bounds(&1))
|> Enum.reduce(fn
{min, max}, nil -> {min, max}
{min, max}, nil ->
{min, max}
{pmin, pmax}, {min, max} ->
min = if pmin.x < min.x, do: Point.init(pmin.x, min.y), else: min
min = if pmin.y < min.y, do: Point.init(min.x, pmin.y), else: min

View file

@ -6,6 +6,6 @@ defimpl Vivid.Bounds.Of, for: Vivid.Line do
Returns a two-element tuple of the line-ends, left to right.
"""
@spec bounds(Line.t) :: {Point.t, Point.t}
@spec bounds(Line.t()) :: {Point.t(), Point.t()}
def bounds(%Line{origin: p0, termination: p1} = _line), do: Vivid.Bounds.Of.bounds([p0, p1])
end

View file

@ -6,10 +6,12 @@ defimpl Vivid.Bounds.Of, for: List do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds([Point.t, ...]) :: {Point.t, Point.t}
@spec bounds([Point.t(), ...]) :: {Point.t(), Point.t()}
def bounds(points) do
Enum.reduce(points, nil, fn
point, nil -> {point, point}
point, nil ->
{point, point}
point, {min, max} ->
x = Point.x(point)
y = Point.y(point)

View file

@ -6,6 +6,6 @@ defimpl Vivid.Bounds.Of, for: Vivid.Path do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Path.t) :: {Point.t, Point.t}
@spec bounds(Path.t()) :: {Point.t(), Point.t()}
def bounds(%Path{vertices: points} = _path), do: Vivid.Bounds.Of.bounds(points)
end

View file

@ -7,6 +7,6 @@ defimpl Vivid.Bounds.Of, for: Vivid.Point do
Since the bounds of a point are simply the point, it returns the point
twice.
"""
@spec bounds(Point.t) :: {Point.t, Point.t}
@spec bounds(Point.t()) :: {Point.t(), Point.t()}
def bounds(point), do: {point, point}
end

View file

@ -6,6 +6,6 @@ defimpl Vivid.Bounds.Of, for: Vivid.Polygon do
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Polygon.t) :: {Point.t, Point.t}
@spec bounds(Polygon.t()) :: {Point.t(), Point.t()}
def bounds(%Polygon{vertices: points} = _polygon), do: Vivid.Bounds.Of.bounds(points)
end

View file

@ -28,7 +28,7 @@ defmodule Vivid.Box do
"@@@@@@@@@@@@@\n"
"""
@opaque t :: Box.t
@opaque t :: Box.t()
@doc """
Initialize an unfilled Box from it's bottom left and top right points.
@ -42,7 +42,7 @@ defmodule Vivid.Box do
...> Box.init(Point.init(1,1), Point.init(4,4))
#Vivid.Box<[bottom_left: #Vivid.Point<{1, 1}>, top_right: #Vivid.Point<{4, 4}>]>
"""
@spec init(Point.t, Point.t) :: Box.t
@spec init(Point.t(), Point.t()) :: Box.t()
def init(%Point{} = bottom_left, %Point{} = top_right), do: init(bottom_left, top_right, false)
@doc """
@ -58,8 +58,9 @@ defmodule Vivid.Box do
...> Box.init(Point.init(1,1), Point.init(4,4))
#Vivid.Box<[bottom_left: #Vivid.Point<{1, 1}>, top_right: #Vivid.Point<{4, 4}>]>
"""
@spec init(Point.t, Point.t, boolean) :: Box.t
def init(%Point{} = bottom_left, %Point{} = top_right, fill) when is_boolean(fill), do: %Box{bottom_left: bottom_left, top_right: top_right, fill: fill}
@spec init(Point.t(), Point.t(), boolean) :: Box.t()
def init(%Point{} = bottom_left, %Point{} = top_right, fill) when is_boolean(fill),
do: %Box{bottom_left: bottom_left, top_right: top_right, fill: fill}
@doc """
Initialize a box from the bounds of an arbitrary shape.
@ -71,11 +72,11 @@ defmodule Vivid.Box do
...> |> Box.init_from_bounds
#Vivid.Box<[bottom_left: #Vivid.Point<{0.0, 0.2447174185242318}>, top_right: #Vivid.Point<{10.0, 9.755282581475768}>]>
"""
@spec init_from_bounds(Shape.t, boolean) :: Box.t
@spec init_from_bounds(Shape.t(), boolean) :: Box.t()
def init_from_bounds(shape, fill \\ false) do
bounds = shape |> Bounds.bounds
min = bounds |> Bounds.min
max = bounds |> Bounds.max
bounds = shape |> Bounds.bounds()
min = bounds |> Bounds.min()
max = bounds |> Bounds.max()
init(min, max, fill)
end
@ -89,7 +90,7 @@ defmodule Vivid.Box do
...> |> Box.bottom_left
#Vivid.Point<{1, 1}>
"""
@spec bottom_left(Box.t) :: Point.t
@spec bottom_left(Box.t()) :: Point.t()
def bottom_left(%Box{bottom_left: bl}), do: bl
@doc """
@ -102,7 +103,7 @@ defmodule Vivid.Box do
...> |> Box.top_left
#Vivid.Point<{1, 4}>
"""
@spec top_left(Box.t) :: Point.t
@spec top_left(Box.t()) :: Point.t()
def top_left(%Box{bottom_left: bl, top_right: tr}), do: Point.init(bl.x, tr.y)
@doc """
@ -115,7 +116,7 @@ defmodule Vivid.Box do
...> |> Box.top_right
#Vivid.Point<{4, 4}>
"""
@spec top_right(Box.t) :: Point.t
@spec top_right(Box.t()) :: Point.t()
def top_right(%Box{top_right: tr}), do: tr
@doc """
@ -128,7 +129,7 @@ defmodule Vivid.Box do
...> |> Box.bottom_right
#Vivid.Point<{4, 1}>
"""
@spec bottom_right(Box.t) :: Point.t
@spec bottom_right(Box.t()) :: Point.t()
def bottom_right(%Box{bottom_left: bl, top_right: tr}), do: Point.init(tr.x, bl.y)
@doc """
@ -141,13 +142,16 @@ defmodule Vivid.Box do
...> |> Box.to_polygon
#Vivid.Polygon<[#Vivid.Point<{1, 1}>, #Vivid.Point<{1, 4}>, #Vivid.Point<{4, 4}>, #Vivid.Point<{4, 1}>]>
"""
@spec to_polygon(Box.t) :: Polygon.t
@spec to_polygon(Box.t()) :: Polygon.t()
def to_polygon(%Box{fill: fill} = box) do
Polygon.init([
bottom_left(box),
top_left(box),
top_right(box),
bottom_right(box)
], fill)
Polygon.init(
[
bottom_left(box),
top_left(box),
top_right(box),
bottom_right(box)
],
fill
)
end
end

View file

@ -35,7 +35,7 @@ defmodule Vivid.Buffer do
"@@@@@@@@@@@@@@@@@@@@"
"""
@opaque t :: %Buffer{buffer: [RGBA.t], rows: integer, columns: integer}
@opaque t :: %Buffer{buffer: [RGBA.t()], rows: integer, columns: integer}
@doc ~S"""
Render the buffer horizontally, ie across rows then up columns.
@ -53,11 +53,11 @@ defmodule Vivid.Buffer do
"@@@@@\n" <>
"@@@@@\n"
"""
@spec horizontal(Frame.t) :: [RGBA.t]
@spec horizontal(Frame.t()) :: [RGBA.t()]
def horizontal(%Frame{shapes: shapes, width: w, height: h} = frame) do
empty_buffer = allocate(frame)
bounds = Bounds.bounds(frame)
buffer = Enum.reduce(shapes, empty_buffer, &horizontal_reducer(&1, &2, bounds, w))
bounds = Bounds.bounds(frame)
buffer = Enum.reduce(shapes, empty_buffer, &horizontal_reducer(&1, &2, bounds, w))
%Buffer{buffer: buffer, rows: h, columns: w}
end
@ -77,11 +77,11 @@ defmodule Vivid.Buffer do
"@@ @@\n" <>
"@@ @@\n"
"""
@spec vertical(Frame.t) :: [RGBA.t]
@spec vertical(Frame.t()) :: [RGBA.t()]
def vertical(%Frame{shapes: shapes, width: w, height: h} = frame) do
bounds = Bounds.bounds(frame)
bounds = Bounds.bounds(frame)
empty_buffer = allocate(frame)
buffer = Enum.reduce(shapes, empty_buffer, &vertical_reducer(&1, &2, bounds, h))
buffer = Enum.reduce(shapes, empty_buffer, &vertical_reducer(&1, &2, bounds, h))
%Buffer{buffer: buffer, rows: w, columns: h}
end
@ -100,8 +100,9 @@ defmodule Vivid.Buffer do
defp horizontal_reducer({shape, colour}, buffer, bounds, width) do
points = Rasterize.rasterize(shape, bounds)
Enum.reduce(points, buffer, fn(%Point{x: x, y: y}, buf) ->
pos = (y * width) + x
Enum.reduce(points, buffer, fn %Point{x: x, y: y}, buf ->
pos = y * width + x
existing = Enum.at(buf, pos)
List.replace_at(buf, pos, RGBA.over(existing, colour))
end)
@ -109,15 +110,17 @@ defmodule Vivid.Buffer do
defp vertical_reducer({shape, colour}, buffer, bounds, width) do
points = Rasterize.rasterize(shape, bounds)
Enum.reduce(points, buffer, fn(point, buf) ->
Enum.reduce(points, buffer, fn point, buf ->
point = Point.swap_xy(point)
pos = (point.y * width) + point.x
pos = point.y * width + point.x
existing = Enum.at(buf, pos)
List.replace_at(buf, pos, RGBA.over(existing, colour))
end)
end
defp allocate(%Frame{width: w, height: h, background_colour: bg}), do: generate_buffer([], w * h, bg)
defp allocate(%Frame{width: w, height: h, background_colour: bg}),
do: generate_buffer([], w * h, bg)
defp generate_buffer(buffer, 0, _colour), do: buffer
defp generate_buffer(buffer, i, colour), do: generate_buffer([colour | buffer], i - 1, colour)

View file

@ -35,7 +35,7 @@ defmodule Vivid.Circle do
"@@@@@@@@ @@@@@@@@\n" <>
"@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@opaque t :: %Circle{center: Point.t, radius: number, fill: boolean}
@opaque t :: %Circle{center: Point.t(), radius: number, fill: boolean}
@doc """
Creates a circle from a point in 2D space and a radius.
@ -45,20 +45,19 @@ defmodule Vivid.Circle do
iex> Vivid.Circle.init(Vivid.Point.init(5,5), 4)
#Vivid.Circle<[center: #Vivid.Point<{5, 5}>, radius: 4]>
"""
@spec init(Point.t, number) :: Circle.t
@spec init(Point.t(), number) :: Circle.t()
def init(%Point{} = point, radius)
when is_number(radius) and radius > 0,
do: init(point, radius, false)
when is_number(radius) and radius > 0,
do: init(point, radius, false)
@doc false
@spec init(Point.t, number, boolean) :: Circle.t
@spec init(Point.t(), number, boolean) :: Circle.t()
def init(%Point{} = point, radius, fill)
when is_number(radius) and is_boolean(fill) and radius > 0
do
when is_number(radius) and is_boolean(fill) and radius > 0 do
%Circle{
center: point,
radius: radius,
fill: fill
fill: fill
}
end
@ -71,7 +70,7 @@ defmodule Vivid.Circle do
...> |> Vivid.Circle.radius
4
"""
@spec radius(Cricle.t) :: number
@spec radius(Cricle.t()) :: number
def radius(%Circle{radius: r}), do: r
@doc """
@ -82,7 +81,7 @@ defmodule Vivid.Circle do
...> |> Vivid.Circle.center
%Vivid.Point{x: 5, y: 5}
"""
@spec center(Circle.t) :: Point.t
@spec center(Circle.t()) :: Point.t()
def center(%Circle{center: point}), do: point
@doc """
@ -94,8 +93,8 @@ defmodule Vivid.Circle do
...> |> Vivid.Circle.circumference
25.132741228718345
"""
@spec circumference(Circle.t) :: number
def circumference(%Circle{radius: radius}), do: 2 * :math.pi * radius
@spec circumference(Circle.t()) :: number
def circumference(%Circle{radius: radius}), do: 2 * :math.pi() * radius
@doc ~S"""
Convert the `circle` into a Polygon.
@ -127,7 +126,7 @@ defmodule Vivid.Circle do
"@@@@ @@@@\n" <>
"@@@@@@@@@@@@@\n"
"""
@spec to_polygon(Circle.t) :: Polygon.t
@spec to_polygon(Circle.t()) :: Polygon.t()
def to_polygon(%Circle{radius: radius} = circle), do: to_polygon(circle, round(radius * 2))
@doc ~S"""
@ -156,21 +155,22 @@ defmodule Vivid.Circle do
"@ @@@@@@@@@\n" <>
"@@@@@@@@@@@\n"
"""
@spec to_polygon(Circle.t, number) :: Polygon.t
@spec to_polygon(Circle.t(), number) :: Polygon.t()
def to_polygon(%Circle{center: center, radius: radius, fill: fill}, steps) do
h = center |> Point.x
k = center |> Point.y
h = center |> Point.x()
k = center |> Point.y()
step_degree = 360 / steps
points = Enum.map(0..steps - 1, fn(step) ->
theta = step_degree * step
theta = degrees_to_radians(theta)
points =
Enum.map(0..(steps - 1), fn step ->
theta = step_degree * step
theta = degrees_to_radians(theta)
x = h + radius * cos(theta)
y = k - radius * sin(theta)
x = h + radius * cos(theta)
y = k - radius * sin(theta)
Point.init(x, y)
end)
Point.init(x, y)
end)
points
|> Polygon.init(fill)

View file

@ -44,27 +44,28 @@ defmodule Vivid.Font do
"@ @@@@@@@@ @@@@@@@@ @@@@@@@ @@@@@ @@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ @@@@@ @@@@@@@@@@ @@@@@@@@ @@@@@@@@@ @@@@@@@@ @@ @\n" <>
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@spec line(String.t, number) :: Shape.t
@spec line(String.t(), number) :: Shape.t()
def line(str, scale \\ 1.0) do
font = rowmans()
str
|> String.split("")
|> Stream.reject(fn c -> c == "" end)
|> Enum.reduce([], fn
letter, [] ->
char = Map.get(font, letter)
lpad = Char.left_pad(char, scale)
[{char, lpad}]
letter, [{lchar, lpad} | _] = letters ->
char = Map.get(font, letter)
lpad =
Char.left_pad(char, scale) +
Char.right_pad(lchar, scale) +
lpad
[{char, lpad} | letters]
end)
|> Enum.map(fn {char, lpad} -> Char.to_shape(char, Point.init(lpad, @font_vertical_offset), scale) end)
|> Enum.into(Group.init)
|> String.split("")
|> Stream.reject(fn c -> c == "" end)
|> Enum.reduce([], fn
letter, [] ->
char = Map.get(font, letter)
lpad = Char.left_pad(char, scale)
[{char, lpad}]
letter, [{lchar, lpad} | _] = letters ->
char = Map.get(font, letter)
lpad = Char.left_pad(char, scale) + Char.right_pad(lchar, scale) + lpad
[{char, lpad} | letters]
end)
|> Enum.map(fn {char, lpad} ->
Char.to_shape(char, Point.init(lpad, @font_vertical_offset), scale)
end)
|> Enum.into(Group.init())
end
@doc """
@ -74,12 +75,101 @@ defmodule Vivid.Font do
@spec rowmans() :: map
def rowmans do
[
" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "", ".",
"/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">",
"?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "",
"-", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~"
" ",
"!",
"\"",
"#",
"$",
"%",
"&",
"'",
"(",
")",
"*",
"+",
",",
"",
".",
"/",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
":",
";",
"<",
"=",
">",
"?",
"@",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"[",
"\\",
"]",
"",
"-",
"`",
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"{",
"|",
"}",
"~"
]
|> Enum.zip(font("rowmans"))
|> Enum.into(%{})
@ -87,7 +177,7 @@ defmodule Vivid.Font do
defp font(name) do
name
|> Hershey.definitions
|> Enum.to_list
|> Hershey.definitions()
|> Enum.to_list()
end
end

View file

@ -14,31 +14,31 @@ defmodule Vivid.Font.Char do
This is not the maximum width of the character, as some go beyond or don't reach their documented bounds.
I assume this is for kerning. I may be wrong.
"""
@spec width(Char.t, number) :: number
@spec width(Char.t(), number) :: number
def width(%Char{left_pos: l, right_pos: r}, scale \\ 1.0), do: round((abs(l) + abs(r)) * scale)
@doc """
Returns the left padding specified for this character.
"""
@spec left_pad(Char.t, number) :: number
@spec left_pad(Char.t(), number) :: number
def left_pad(%Char{left_pos: l}, scale \\ 1.0), do: round(abs(l) * scale)
@doc """
Returns the right padding specified for this character.
"""
@spec right_pad(Char.t, number) :: number
@spec right_pad(Char.t(), number) :: number
def right_pad(%Char{right_pos: r}, scale \\ 1.0), do: round(abs(r) * scale)
@doc """
Rendered width of a character.
"""
@spec rendered_width(Char.t, number) :: number
@spec rendered_width(Char.t(), number) :: number
def rendered_width(%Char{} = char, scale \\ 1.0), do: rendered_dimension(char, scale, 0)
@doc """
Rendered height of a character.
"""
@spec rendered_height(Char.t, number) :: number
@spec rendered_height(Char.t(), number) :: number
def rendered_height(%Char{} = char, scale \\ 1.0), do: rendered_dimension(char, scale, 1)
@doc """
@ -48,32 +48,36 @@ defmodule Vivid.Font.Char do
* `center` the center `%Point{}` around which to render the character.
* `scale` how much to scale the character by.
"""
@spec to_shape(Char.t, Point.t, number) :: Shape.t
@spec to_shape(Char.t(), Point.t(), number) :: Shape.t()
def to_shape(%Char{coordinates: coords}, %Point{} = center, scale \\ 1.0) do
x_center = center |> Point.x
y_center = center |> Point.y
x_center = center |> Point.x()
y_center = center |> Point.y()
coords
|> Enum.reduce([[]], fn
:pen_up, acc -> [[] | acc]
:pen_up, acc ->
[[] | acc]
{x, y}, [last | rest] ->
x = round(x_center + (x * scale))
y = round(y_center + (y * scale))
x = round(x_center + x * scale)
y = round(y_center + y * scale)
[[Point.init(x, y) | last] | rest]
end)
end)
|> Enum.map(&Path.init(&1))
|> Group.init
|> Group.init()
end
defp rendered_dimension(%Char{coordinates: coords}, scale, i) do
coords = coords
coords =
coords
|> Enum.reject(fn c -> c == :pen_up end)
|> Enum.map(&elem(&1, i))
if coords == [] do
0
else
max = coords |> Enum.max
min = coords |> Enum.min
max = coords |> Enum.max()
min = coords |> Enum.min()
round((max - min) * scale)
end
end

View file

@ -39,8 +39,7 @@ defmodule Vivid.Frame do
"@@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@opaque t :: %Frame{width: integer, height: integer,
background_colour: RGBA.t, shapes: []}
@opaque t :: %Frame{width: integer, height: integer, background_colour: RGBA.t(), shapes: []}
@doc """
Initialize a frame buffer.
@ -54,10 +53,9 @@ defmodule Vivid.Frame do
iex> Vivid.Frame.init(4, 4)
#Vivid.Frame<[width: 4, height: 4, background_colour: #Vivid.RGBA<{0, 0, 0, 0}>]>
"""
@spec init(integer(), integer(), Range.t) :: Frame.t
@spec init(integer(), integer(), Range.t()) :: Frame.t()
def init(width \\ 128, height \\ 64, %RGBA{} = colour \\ RGBA.init(0, 0, 0, 0))
when is_integer(width) and is_integer(height) and width > 0 and height > 0
do
when is_integer(width) and is_integer(height) and width > 0 and height > 0 do
%Frame{width: width, height: height, background_colour: colour, shapes: []}
end
@ -136,7 +134,7 @@ defmodule Vivid.Frame do
" @ \n" <>
"@ \n"
"""
@spec push(Frame.t, Shape.t, RGBA.t) :: Frame.t
@spec push(Frame.t(), Shape.t(), RGBA.t()) :: Frame.t()
def push(%Frame{shapes: shapes} = frame, shape, colour) do
%{frame | shapes: [{shape, colour} | shapes]}
end
@ -144,7 +142,7 @@ defmodule Vivid.Frame do
@doc """
Clear the `frame` of any shapes.
"""
@spec clear(Frame.t) :: Frame.t
@spec clear(Frame.t()) :: Frame.t()
def clear(%Frame{} = frame) do
%{frame | shapes: []}
end
@ -157,7 +155,7 @@ defmodule Vivid.Frame do
iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.width
80
"""
@spec width(Frame.t) :: integer()
@spec width(Frame.t()) :: integer()
def width(%Frame{width: w}), do: w
@doc """
@ -168,7 +166,7 @@ defmodule Vivid.Frame do
iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.height
25
"""
@spec height(Frame.t) :: integer()
@spec height(Frame.t()) :: integer()
def height(%Frame{height: h}), do: h
@doc """
@ -179,7 +177,7 @@ defmodule Vivid.Frame do
iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.background_colour
#Vivid.RGBA<{0, 0, 0, 0}>
"""
@spec background_colour(Frame.t) :: RGBA.t
@spec background_colour(Frame.t()) :: RGBA.t()
def background_colour(%Frame{background_colour: c}), do: c
@doc """
@ -192,8 +190,9 @@ defmodule Vivid.Frame do
...> |> Vivid.Frame.background_colour
#Vivid.RGBA<{1, 1, 1, 1}>
"""
@spec background_colour(Frame.t, RGBA.t) :: Frame.t
def background_colour(%Frame{} = frame, %RGBA{} = colour), do: %{frame | background_colour: colour}
@spec background_colour(Frame.t(), RGBA.t()) :: Frame.t()
def background_colour(%Frame{} = frame, %RGBA{} = colour),
do: %{frame | background_colour: colour}
@doc """
Render a `frame` into a buffer for display horizontally.
@ -201,7 +200,7 @@ defmodule Vivid.Frame do
Returns a one-dimensional List of `RGBA` colours with alpha-compositing
completed.
"""
@spec buffer(Frame.t) :: [RGBA.t]
@spec buffer(Frame.t()) :: [RGBA.t()]
def buffer(%Frame{} = frame), do: Buffer.horizontal(frame)
@doc """
@ -215,7 +214,7 @@ defmodule Vivid.Frame do
Returns a one-dimensional List of `RGBA` colours with alpha-compositing
completed.
"""
@spec buffer(Frame.t, :horizontal | :vertical) :: [RGBA.t]
@spec buffer(Frame.t(), :horizontal | :vertical) :: [RGBA.t()]
def buffer(%Frame{} = frame, :horizontal), do: Buffer.horizontal(frame)
def buffer(%Frame{} = frame, :vertical), do: Buffer.vertical(frame)
def buffer(%Frame{} = frame, :vertical), do: Buffer.vertical(frame)
end

View file

@ -39,7 +39,7 @@ defmodule Vivid.Group do
"@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@opaque t :: %Group{shapes: [Shape.t]}
@opaque t :: %Group{shapes: [Shape.t()]}
@doc """
Initialize an empty group.
@ -49,7 +49,7 @@ defmodule Vivid.Group do
iex> Vivid.Group.init
#Vivid.Group<[]>
"""
@spec init() :: Group.t
@spec init() :: Group.t()
def init, do: %Group{shapes: MapSet.new()}
@doc """
@ -62,9 +62,9 @@ defmodule Vivid.Group do
...> Vivid.Group.init([circle, line])
#Vivid.Group<[#Vivid.Line<[origin: #Vivid.Point<{1, 1}>, termination: #Vivid.Point<{10, 10}>]>, #Vivid.Circle<[center: #Vivid.Point<{5, 5}>, radius: 5]>]>
"""
@spec init([Shape.t]) :: Group.t
@spec init([Shape.t()]) :: Group.t()
def init(shapes) do
%Group{shapes: Enum.into(shapes, MapSet.new)}
%Group{shapes: Enum.into(shapes, MapSet.new())}
end
@doc """
@ -77,7 +77,7 @@ defmodule Vivid.Group do
...> |> Vivid.Group.delete(line)
%Vivid.Group{shapes: MapSet.new()}
"""
@spec delete(Group.t, Shape.t) :: Group.t
@spec delete(Group.t(), Shape.t()) :: Group.t()
def delete(%Group{shapes: shapes}, shape), do: shapes |> MapSet.delete(shape) |> init
@doc """
@ -92,6 +92,6 @@ defmodule Vivid.Group do
%Vivid.Line{origin: %Vivid.Point{x: 1, y: 1}, termination: %Vivid.Point{x: 10, y: 10}}
])}
"""
@spec put(Group.t, Shape.t) :: Group.t
@spec put(Group.t(), Shape.t()) :: Group.t()
def put(%Group{shapes: shapes}, shape), do: shapes |> MapSet.put(shape) |> init
end

View file

@ -1,7 +1,7 @@
defmodule Vivid.Hershey do
require Logger
alias Vivid.Font.Char
@mid_point 'R' |> List.first
@mid_point 'R' |> List.first()
@moduledoc """
Supports reading the Hershey Vector Font format and converting them into paths.
@ -11,18 +11,20 @@ defmodule Vivid.Hershey do
Returns a map of "character IDs" to %Char{} structs, which can
be passed to `char_to_shape` to turn into renderable shapes.
"""
@spec definitions(String.t) :: Enumerable.t
@spec definitions(String.t()) :: Enumerable.t()
def definitions(file) do
file
|> get_path
|> File.stream!
|> File.stream!()
|> Stream.transform(nil, &fix_wrapped_lines(&1, &2))
|> Stream.map(&parse_line(&1))
end
defp fix_wrapped_lines(next, nil), do: {[], String.replace(next, ~r/[\r\n]+/, "")}
defp fix_wrapped_lines(next, last) do
next = next |> String.replace(~r/[\n\r]+/, "")
if Regex.match?(~r/^\ *[0-9]+/, next) do
{[last], next}
else
@ -31,26 +33,30 @@ defmodule Vivid.Hershey do
end
end
defp parse_line(<< char::binary-size(5), vertices::binary-size(3), l_pos::binary-size(1), r_pos::binary-size(1), coords::binary >>) do
char = char |> String.trim_leading |> String.to_integer
vertices = vertices |> String.trim_leading |> String.to_integer
l_pos = l_pos |> String.to_charlist |> List.first
r_pos = r_pos |> String.to_charlist |> List.first
coords = parse_coords([], coords)
defp parse_line(
<<char::binary-size(5), vertices::binary-size(3), l_pos::binary-size(1),
r_pos::binary-size(1), coords::binary>>
) do
char = char |> String.trim_leading() |> String.to_integer()
vertices = vertices |> String.trim_leading() |> String.to_integer()
l_pos = l_pos |> String.to_charlist() |> List.first()
r_pos = r_pos |> String.to_charlist() |> List.first()
coords = parse_coords([], coords)
%Char{
character: char,
vertices: vertices,
left_pos: l_pos - @mid_point,
right_pos: r_pos - @mid_point,
character: char,
vertices: vertices,
left_pos: l_pos - @mid_point,
right_pos: r_pos - @mid_point,
coordinates: coords
}
end
defp parse_coords(parsed, ""), do: parsed |> Enum.reverse
defp parse_coords(parsed, ""), do: parsed |> Enum.reverse()
defp parse_coords(parsed, " R" <> rest), do: parse_coords([:pen_up | parsed], rest)
defp parse_coords(parsed, << xy::binary-size(2), rest::binary >>) do
[y, x] = xy |> String.to_charlist
defp parse_coords(parsed, <<xy::binary-size(2), rest::binary>>) do
[y, x] = xy |> String.to_charlist()
normalized_x = @mid_point - x
normalized_y = y - @mid_point
parse_coords([{normalized_y, normalized_x} | parsed], rest)
@ -64,7 +70,7 @@ defmodule Vivid.Hershey do
defp priv_dir do
:vivid
|> :code.priv_dir
|> List.to_string
|> :code.priv_dir()
|> List.to_string()
end
end

View file

@ -21,7 +21,7 @@ defmodule Vivid.Line do
"@@@@@@@@\n"
"""
@opaque t :: %Line{origin: Point.t, termination: Point.t}
@opaque t :: %Line{origin: Point.t(), termination: Point.t()}
@doc ~S"""
Create a Line given an `origin` and `termination` point.
@ -32,7 +32,7 @@ defmodule Vivid.Line do
%Vivid.Line{origin: %Vivid.Point{x: 1, y: 1}, termination: %Vivid.Point{x: 4, y: 4}}
"""
@spec init(Point.t, Point.t) :: Line.t
@spec init(Point.t(), Point.t()) :: Line.t()
def init(%Point{} = origin, %Point{} = termination) do
%Line{origin: origin, termination: termination}
end
@ -40,7 +40,7 @@ defmodule Vivid.Line do
@doc """
Create a `Line` from a two-element list of points.
"""
@spec init([Point.t]) :: Line.t
@spec init([Point.t()]) :: Line.t()
def init([o, t]) do
init(o, t)
end
@ -53,7 +53,7 @@ defmodule Vivid.Line do
iex> Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(4,4)) |> Vivid.Line.origin
%Vivid.Point{x: 1, y: 1}
"""
@spec origin(Line.t) :: Point.t
@spec origin(Line.t()) :: Point.t()
def origin(%Line{origin: o}), do: o
@doc ~S"""
@ -66,7 +66,7 @@ defmodule Vivid.Line do
...> |> Line.termination
#Vivid.Point<{4, 4}>
"""
@spec termination(Line.t) :: Point.t
@spec termination(Line.t()) :: Point.t()
def termination(%Line{termination: t}), do: t
@doc ~S"""
@ -77,7 +77,7 @@ defmodule Vivid.Line do
iex> Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(14,4)) |> Vivid.Line.width
13
"""
@spec width(Line.t) :: number
@spec width(Line.t()) :: number
def width(%Line{} = line), do: abs(x_distance(line))
@doc ~S"""
@ -88,7 +88,7 @@ defmodule Vivid.Line do
iex> Vivid.Line.init(Vivid.Point.init(14,1), Vivid.Point.init(1,4)) |> Vivid.Line.x_distance
-13
"""
@spec x_distance(Line.t) :: number
@spec x_distance(Line.t()) :: number
def x_distance(%Line{origin: %Point{x: x0}, termination: %Point{x: x1}}), do: x1 - x0
@doc ~S"""
@ -99,7 +99,7 @@ defmodule Vivid.Line do
iex> Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(4,14)) |> Vivid.Line.height
13
"""
@spec height(Line.t) :: number
@spec height(Line.t()) :: number
def height(%Line{} = line), do: abs(y_distance(line))
@doc ~S"""
@ -110,7 +110,7 @@ defmodule Vivid.Line do
iex> Vivid.Line.init(Vivid.Point.init(1,14), Vivid.Point.init(4,1)) |> Vivid.Line.y_distance
-13
"""
@spec y_distance(Line.t) :: number
@spec y_distance(Line.t()) :: number
def y_distance(%Line{origin: %Point{y: y0}, termination: %Point{y: y1}}), do: y1 - y0
@doc ~S"""
@ -122,7 +122,7 @@ defmodule Vivid.Line do
iex> Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(4,5)) |> Vivid.Line.length
5.0
"""
@spec length(Line.t) :: number
@spec length(Line.t()) :: number
def length(%Line{} = line) do
dx2 = line |> width |> pow(2)
dy2 = line |> height |> pow(2)
@ -144,13 +144,13 @@ defmodule Vivid.Line do
...> |> Line.on?(Point.init(2,2))
false
"""
@spec on?(Line.t, Point.t) :: boolean
@spec on?(Line.t(), Point.t()) :: boolean
def on?(%Line{origin: origin, termination: termination}, %Point{} = point) do
x_distance_point = point.x - termination.x
y_distance_point = point.y - termination.y
x_distance_point = point.x - termination.x
y_distance_point = point.y - termination.y
x_distance_origin = origin.x - termination.x
y_distance_origin = origin.y - termination.y
cross_product = x_distance_point * y_distance_origin - x_distance_origin * y_distance_point
cross_product = x_distance_point * y_distance_origin - x_distance_origin * y_distance_point
cross_product == 0.0
end
@ -165,18 +165,26 @@ defmodule Vivid.Line do
...> |> Line.x_intersect(10)
#Vivid.Point<{10, 5.25}>
"""
@spec x_intersect(Line.t, integer) :: Point.t | nil
def x_intersect(%Line{origin: %Point{x: x0} = p, termination: %Point{x: x1}}, x) when x == x0 and x == x1, do: p
def x_intersect(%Line{origin: %Point{x: x0} = p0, termination: %Point{x: x1} = p1}, x) when x0 > x1 do
@spec x_intersect(Line.t(), integer) :: Point.t() | nil
def x_intersect(%Line{origin: %Point{x: x0} = p, termination: %Point{x: x1}}, x)
when x == x0 and x == x1,
do: p
def x_intersect(%Line{origin: %Point{x: x0} = p0, termination: %Point{x: x1} = p1}, x)
when x0 > x1 do
x_intersect(%Line{origin: p1, termination: p0}, x)
end
def x_intersect(%Line{origin: %Point{x: x0} = p}, x) when x0 == x, do: p
def x_intersect(%Line{termination: %Point{x: x0} = p}, x) when x0 == x, do: p
def x_intersect(%Line{origin: %Point{x: x0, y: y0}, termination: %Point{x: x1, y: y1}}, x) when x0 < x and x < x1 do
def x_intersect(%Line{origin: %Point{x: x0, y: y0}, termination: %Point{x: x1, y: y1}}, x)
when x0 < x and x < x1 do
rx = (x - x0) / (x1 - x0)
y = rx * (y1 - y0) + y0
y = rx * (y1 - y0) + y0
Point.init(x, y)
end
def x_intersect(_line, _x), do: nil
@doc """
@ -189,18 +197,26 @@ defmodule Vivid.Line do
...> |> Line.y_intersect(10)
#Vivid.Point<{17.307692307692307, 10}>
"""
@spec y_intersect(Line.t, integer) :: Point.t | nil
def y_intersect(%Line{origin: %Point{y: y0} = p, termination: %Point{y: y1}}, y) when y == y0 and y == y1, do: p
def y_intersect(%Line{origin: %Point{y: y0} = p0, termination: %Point{y: y1} = p1}, y) when y0 > y1 do
@spec y_intersect(Line.t(), integer) :: Point.t() | nil
def y_intersect(%Line{origin: %Point{y: y0} = p, termination: %Point{y: y1}}, y)
when y == y0 and y == y1,
do: p
def y_intersect(%Line{origin: %Point{y: y0} = p0, termination: %Point{y: y1} = p1}, y)
when y0 > y1 do
y_intersect(%Line{origin: p1, termination: p0}, y)
end
def y_intersect(%Line{origin: %Point{y: y0} = p}, y) when y0 == y, do: p
def y_intersect(%Line{termination: %Point{y: y0} = p}, y) when y0 == y, do: p
def y_intersect(%Line{origin: %Point{x: x0, y: y0}, termination: %Point{x: x1, y: y1}}, y) when y0 < y and y < y1 do
def y_intersect(%Line{origin: %Point{x: x0, y: y0}, termination: %Point{x: x1, y: y1}}, y)
when y0 < y and y < y1 do
ry = (y - y0) / (y1 - y0)
x = ry * (x1 - x0) + x0
x = ry * (x1 - x0) + x0
Point.init(x, y)
end
def y_intersect(_line, _y), do: nil
@doc """
@ -218,8 +234,10 @@ defmodule Vivid.Line do
...> |> Line.horizontal?
false
"""
@spec horizontal?(Line.t) :: boolean
def horizontal?(%Line{origin: %Point{y: y0}, termination: %Point{y: y1}}) when y0 == y1, do: true
@spec horizontal?(Line.t()) :: boolean
def horizontal?(%Line{origin: %Point{y: y0}, termination: %Point{y: y1}}) when y0 == y1,
do: true
def horizontal?(_line), do: false
@doc """
@ -237,7 +255,7 @@ defmodule Vivid.Line do
...> |> Line.vertical?
false
"""
@spec vertical?(Line.t) :: boolean
@spec vertical?(Line.t()) :: boolean
def vertical?(%Line{origin: %Point{x: x0}, termination: %Point{x: x1}}) when x0 == x1, do: true
def vertical?(_line), do: false
end

View file

@ -30,7 +30,7 @@ defmodule Vivid.Path do
"@@@@@@@@@@@@\n"
"""
@opaque t :: %Path{vertices: [Shape.t]}
@opaque t :: %Path{vertices: [Shape.t()]}
@doc """
Initialize an empty path.
@ -40,7 +40,7 @@ defmodule Vivid.Path do
iex> Vivid.Path.init
%Vivid.Path{vertices: []}
"""
@spec init() :: Path.t
@spec init() :: Path.t()
def init, do: %Path{vertices: []}
@doc """
@ -56,7 +56,7 @@ defmodule Vivid.Path do
%Vivid.Point{x: 2, y: 1}
]}
"""
@spec init([Point.t]) :: Path.t
@spec init([Point.t()]) :: Path.t()
def init(points) when is_list(points), do: %Path{vertices: points}
@doc """
@ -72,7 +72,7 @@ defmodule Vivid.Path do
%Vivid.Line{origin: %Vivid.Point{x: 2, y: 2},
termination: %Vivid.Point{x: 2, y: 1}}]
"""
@spec to_lines(Path.t) :: [Line.t]
@spec to_lines(Path.t()) :: [Line.t()]
def to_lines(%Path{vertices: points}) do
points_to_lines([], points)
end
@ -85,7 +85,7 @@ defmodule Vivid.Path do
iex> Vivid.Path.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Path.delete(Vivid.Point.init(2,2))
%Vivid.Path{vertices: [%Vivid.Point{x: 1, y: 1}]}
"""
@spec delete(Path.t, Point.t) :: Path.t
@spec delete(Path.t(), Point.t()) :: Path.t()
def delete(%Path{vertices: points}, %Point{} = point) do
points
|> List.delete(point)
@ -100,7 +100,7 @@ defmodule Vivid.Path do
iex> Vivid.Path.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Path.delete_at(1)
%Vivid.Path{vertices: [%Vivid.Point{x: 1, y: 1}]}
"""
@spec delete_at(Path.t, integer) :: Path.t
@spec delete_at(Path.t(), integer) :: Path.t()
def delete_at(%Path{vertices: points}, index) do
points
|> List.delete_at(index)
@ -115,10 +115,10 @@ defmodule Vivid.Path do
iex> Vivid.Path.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Path.first
%Vivid.Point{x: 1, y: 1}
"""
@spec first(Path.t) :: Point.t
@spec first(Path.t()) :: Point.t()
def first(%Path{vertices: points}) do
points
|> List.first
|> List.first()
end
@doc """
@ -133,7 +133,7 @@ defmodule Vivid.Path do
%Vivid.Point{x: 2, y: 2}
]}
"""
@spec insert_at(Path.t, integer, Point.t) :: Path.t
@spec insert_at(Path.t(), integer, Point.t()) :: Path.t()
def insert_at(%Path{vertices: points}, index, %Point{} = point) do
points
|> List.insert_at(index, point)
@ -148,10 +148,10 @@ defmodule Vivid.Path do
iex> Vivid.Path.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Path.last
%Vivid.Point{x: 2, y: 2}
"""
@spec last(Path.t) :: Point.t
@spec last(Path.t()) :: Point.t()
def last(%Path{vertices: points}) do
points
|> List.last
|> List.last()
end
@doc """
@ -166,7 +166,7 @@ defmodule Vivid.Path do
%Vivid.Point{x: 3, y: 3}
]}
"""
@spec replace_at(Path.t, integer, Point.t) :: Path.t
@spec replace_at(Path.t(), integer, Point.t()) :: Path.t()
def replace_at(%Path{vertices: points}, index, %Point{} = point) do
points
|> List.replace_at(index, point)
@ -181,9 +181,9 @@ defmodule Vivid.Path do
end
defp points_to_lines(lines, [point | rest]) do
origin = lines |> List.last |> Line.termination
term = point
lines = lines ++ [Line.init(origin, term)]
origin = lines |> List.last() |> Line.termination()
term = point
lines = lines ++ [Line.init(origin, term)]
points_to_lines(lines, rest)
end
end

View file

@ -29,10 +29,9 @@ defmodule Vivid.Point do
iex> Vivid.Point.init(13, 27)
%Vivid.Point{x: 13, y: 27}
"""
@spec init(number, number) :: Point.t
@spec init(number, number) :: Point.t()
def init(x, y)
when is_number(x) and is_number(y)
do
when is_number(x) and is_number(y) do
%Point{x: x, y: y}
end
@ -44,7 +43,7 @@ defmodule Vivid.Point do
iex> Vivid.Point.init(13, 27) |> Vivid.Point.x
13
"""
@spec x(Point.t) :: number
@spec x(Point.t()) :: number
def x(%Point{x: x}), do: x
@doc ~S"""
@ -55,7 +54,7 @@ defmodule Vivid.Point do
iex> Vivid.Point.init(13, 27) |> Vivid.Point.y
27
"""
@spec y(Point.t) :: number
@spec y(Point.t()) :: number
def y(%Point{y: y}), do: y
@doc """
@ -68,7 +67,7 @@ defmodule Vivid.Point do
...> |> Vivid.Point.swap_xy
#Vivid.Point<{27, 13}>
"""
@spec swap_xy(Point.t) :: Point.t
@spec swap_xy(Point.t()) :: Point.t()
def swap_xy(%Point{x: x, y: y}), do: Point.init(y, x)
@doc """
@ -82,7 +81,7 @@ defmodule Vivid.Point do
...> Point.vector(a, b)
{10, 10}
"""
@spec vector(Point.t, Point.t) :: {number, number}
@spec vector(Point.t(), Point.t()) :: {number, number}
def vector(%Point{x: x0, y: y0} = _a, %Point{x: x1, y: y1} = _b) do
{x1 - x0, y1 - y0}
end
@ -96,6 +95,6 @@ defmodule Vivid.Point do
...> |> Vivid.Point.round
#Vivid.Point<{1, 5}>
"""
@spec round(Point.t) :: Point.t
@spec round(Point.t()) :: Point.t()
def round(%Point{x: x, y: y}), do: Point.init(Kernel.round(x), Kernel.round(y))
end

View file

@ -31,7 +31,7 @@ defmodule Vivid.Polygon do
"@@@@@@@@@@@@\n"
"""
@opaque t :: %Polygon{vertices: [Point.t], fill: boolean}
@opaque t :: %Polygon{vertices: [Point.t()], fill: boolean}
@doc """
Initialize an empty Polygon.
@ -41,7 +41,7 @@ defmodule Vivid.Polygon do
iex> Vivid.Polygon.init
%Vivid.Polygon{vertices: []}
"""
@spec init() :: Polygon.t
@spec init() :: Polygon.t()
def init, do: %Polygon{vertices: [], fill: false}
@doc """
@ -57,12 +57,13 @@ defmodule Vivid.Polygon do
%Vivid.Point{x: 2, y: 1}
]}
"""
@spec init([Point.t]) :: Polygon.t
@spec init([Point.t()]) :: Polygon.t()
def init(points) when is_list(points), do: %Polygon{vertices: points, fill: false}
@doc false
@spec init([Point.t], boolean) :: Polygon.t
def init(points, fill) when is_list(points) and is_boolean(fill), do: %Polygon{vertices: points, fill: fill}
@spec init([Point.t()], boolean) :: Polygon.t()
def init(points, fill) when is_list(points) and is_boolean(fill),
do: %Polygon{vertices: points, fill: fill}
@doc """
Convert a Polygon into a list of lines joined by the vertices.
@ -79,7 +80,7 @@ defmodule Vivid.Polygon do
%Vivid.Line{origin: %Vivid.Point{x: 2, y: 1},
termination: %Vivid.Point{x: 1, y: 1}}]
"""
@spec to_lines(Polygon.t) :: [Line.t]
@spec to_lines(Polygon.t()) :: [Line.t()]
def to_lines(%Polygon{vertices: points}) do
points_to_lines([], points)
end
@ -92,7 +93,7 @@ defmodule Vivid.Polygon do
iex> Vivid.Polygon.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Polygon.delete(Vivid.Point.init(2,2))
%Vivid.Polygon{vertices: [%Vivid.Point{x: 1, y: 1}]}
"""
@spec delete(Polygon.t, Point.t) :: Polygon.t
@spec delete(Polygon.t(), Point.t()) :: Polygon.t()
def delete(%Polygon{vertices: points}, %Point{} = point) do
points
|> List.delete(point)
@ -107,7 +108,7 @@ defmodule Vivid.Polygon do
iex> Vivid.Polygon.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Polygon.delete_at(1)
%Vivid.Polygon{vertices: [%Vivid.Point{x: 1, y: 1}]}
"""
@spec delete_at(Polygon.t, integer) :: Polygon.t
@spec delete_at(Polygon.t(), integer) :: Polygon.t()
def delete_at(%Polygon{vertices: points}, index) do
points
|> List.delete_at(index)
@ -122,10 +123,10 @@ defmodule Vivid.Polygon do
iex> Vivid.Polygon.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Polygon.first
%Vivid.Point{x: 1, y: 1}
"""
@spec first(Polygon.t) :: Point.t
@spec first(Polygon.t()) :: Point.t()
def first(%Polygon{vertices: points}) do
points
|> List.first
|> List.first()
end
@doc """
@ -140,7 +141,7 @@ defmodule Vivid.Polygon do
%Vivid.Point{x: 2, y: 2}
]}
"""
@spec insert_at(Polygon.t, integer, Point.t) :: Polygon.t
@spec insert_at(Polygon.t(), integer, Point.t()) :: Polygon.t()
def insert_at(%Polygon{vertices: points}, index, %Point{} = point) do
points
|> List.insert_at(index, point)
@ -155,10 +156,10 @@ defmodule Vivid.Polygon do
iex> Vivid.Polygon.init([Vivid.Point.init(1,1), Vivid.Point.init(2,2)]) |> Vivid.Polygon.last
%Vivid.Point{x: 2, y: 2}
"""
@spec last(Polygon.t) :: Point.t
@spec last(Polygon.t()) :: Point.t()
def last(%Polygon{vertices: points}) do
points
|> List.last
|> List.last()
end
@doc """
@ -173,7 +174,7 @@ defmodule Vivid.Polygon do
%Vivid.Point{x: 3, y: 3}
]}
"""
@spec replace_at(Polygon.t, integer, Point.t) :: Polygon.t
@spec replace_at(Polygon.t(), integer, Point.t()) :: Polygon.t()
def replace_at(%Polygon{vertices: points}, index, %Point{} = point) do
points
|> List.replace_at(index, point)
@ -200,7 +201,7 @@ defmodule Vivid.Polygon do
...> |> Polygon.filled?
false
"""
@spec filled?(Polygon.t) :: boolean
@spec filled?(Polygon.t()) :: boolean
def filled?(%Polygon{fill: fill}), do: fill
@doc """
@ -214,12 +215,12 @@ defmodule Vivid.Polygon do
...> |> Polygon.filled?
true
"""
@spec fill(Polygon.t, boolean) :: Polygon.t
@spec fill(Polygon.t(), boolean) :: Polygon.t()
def fill(%Polygon{} = polygon, fill) when is_boolean(fill), do: %{polygon | fill: fill}
defp points_to_lines(lines, []) do
origin = lines |> List.last |> Line.termination
term = lines |> List.first |> Line.origin
origin = lines |> List.last() |> Line.termination()
term = lines |> List.first() |> Line.origin()
lines ++ [Line.init(origin, term)]
end
@ -229,9 +230,9 @@ defmodule Vivid.Polygon do
end
defp points_to_lines(lines, [point | rest]) do
origin = lines |> List.last |> Line.termination
term = point
lines = lines ++ [Line.init(origin, term)]
origin = lines |> List.last() |> Line.termination()
term = point
lines = lines ++ [Line.init(origin, term)]
points_to_lines(lines, rest)
end
end

View file

@ -1,5 +1,6 @@
defprotocol Vivid.Rasterize do
alias Vivid.Shape
@moduledoc ~S"""
The Rasterize protocol is responsible for converting shapes into bitmaps.
@ -18,6 +19,6 @@ defprotocol Vivid.Rasterize do
Takes a `shape` and returns a `MapSet` of points within `bounds`.
"""
@spec rasterize(Shape.t, Bounds.t) :: MapSet
@spec rasterize(Shape.t(), Bounds.t()) :: MapSet
def rasterize(shape, bounds)
end

View file

@ -15,10 +15,10 @@ defimpl Vivid.Rasterize, for: Vivid.Arc do
#MapSet<[#Vivid.Point<{0, 5}>, #Vivid.Point<{1, 3}>, #Vivid.Point<{1, 4}>, #Vivid.Point<{2, 2}>, #Vivid.Point<{3, 1}>, #Vivid.Point<{4, 1}>, #Vivid.Point<{5, 0}>]>
"""
@spec rasterize(Arc.t, Bounds.t) :: MapSet.t
@spec rasterize(Arc.t(), Bounds.t()) :: MapSet.t()
def rasterize(arc, bounds) do
arc
|> Arc.to_path
|> Arc.to_path()
|> Rasterize.rasterize(bounds)
end
end

View file

@ -15,10 +15,10 @@ defimpl Vivid.Rasterize, for: Vivid.Box do
...> Rasterize.rasterize(box, Bounds.bounds(box))
#MapSet<[#Vivid.Point<{2, 2}>, #Vivid.Point<{2, 3}>, #Vivid.Point<{2, 4}>, #Vivid.Point<{3, 2}>, #Vivid.Point<{3, 4}>, #Vivid.Point<{4, 2}>, #Vivid.Point<{4, 3}>, #Vivid.Point<{4, 4}>]>
"""
@spec rasterize(Box.t, Bounds.t) :: MapSet.t
@spec rasterize(Box.t(), Bounds.t()) :: MapSet.t()
def rasterize(box, bounds) do
box
|> Box.to_polygon
|> Box.to_polygon()
|> Rasterize.rasterize(bounds)
end
end

View file

@ -27,10 +27,10 @@ defimpl Vivid.Rasterize, for: Vivid.Circle do
%Vivid.Point{x: 9, y: 5}, %Vivid.Point{x: 9, y: 6}
])
"""
@spec rasterize(Circle.t, Bounds.t) :: MapSet.t
@spec rasterize(Circle.t(), Bounds.t()) :: MapSet.t()
def rasterize(circle, bounds) do
circle
|> Circle.to_polygon
|> Circle.to_polygon()
|> Rasterize.rasterize(bounds)
end
end

View file

@ -14,9 +14,9 @@ defimpl Vivid.Rasterize, for: Vivid.Group do
...> Vivid.Group.init([path]) |> Vivid.Rasterize.rasterize(Vivid.Bounds.init(0, 0, 3, 3))
#MapSet<[#Vivid.Point<{1, 1}>, #Vivid.Point<{1, 2}>, #Vivid.Point<{1, 3}>, #Vivid.Point<{2, 3}>, #Vivid.Point<{3, 1}>, #Vivid.Point<{3, 2}>, #Vivid.Point<{3, 3}>]>
"""
@spec rasterize(Group.t, Bounds.t) :: MapSet.t
@spec rasterize(Group.t(), Bounds.t()) :: MapSet.t()
def rasterize(%Group{shapes: shapes} = _group, bounds) do
Enum.reduce(shapes, MapSet.new, fn(shape, acc) ->
Enum.reduce(shapes, MapSet.new(), fn shape, acc ->
MapSet.union(acc, Rasterize.rasterize(shape, bounds))
end)
end

View file

@ -34,36 +34,36 @@ defimpl Vivid.Rasterize, for: Vivid.Line do
])
"""
@spec rasterize(Line.t, Bounds.t) :: MapSet.t
@spec rasterize(Line.t(), Bounds.t()) :: MapSet.t()
def rasterize(%Line{} = line, bounds) do
# Convert the line into absolute coordinates.
origin = line |> Line.origin |> Point.round
term = line |> Line.termination |> Point.round
line = Line.init(origin, term)
origin = line |> Line.origin() |> Point.round()
term = line |> Line.termination() |> Point.round()
line = Line.init(origin, term)
dx = line |> Line.x_distance
dy = line |> Line.y_distance
dx = line |> Line.x_distance()
dy = line |> Line.y_distance()
steps = choose_largest_of(abs(dx), abs(dy))
points = if steps == 0 do
MapSet.new([origin])
else
x_increment = dx / steps
y_increment = dy / steps
points =
if steps == 0 do
MapSet.new([origin])
else
x_increment = dx / steps
y_increment = dy / steps
points = MapSet.new([origin])
current_x = origin |> Point.x
current_y = origin |> Point.y
points = MapSet.new([origin])
current_x = origin |> Point.x()
current_y = origin |> Point.y()
reduce_points({points, steps, current_x,
current_y, x_increment, y_increment})
end
reduce_points({points, steps, current_x, current_y, x_increment, y_increment})
end
points
|> Stream.map(&Point.round(&1))
|> Stream.filter(&Bounds.contains?(bounds, &1))
|> Enum.into(MapSet.new)
|> Enum.into(MapSet.new())
end
defp reduce_points({points, 0, _, _, _, _}), do: points
@ -71,7 +71,7 @@ defimpl Vivid.Rasterize, for: Vivid.Line do
defp reduce_points({points, steps, current_x, current_y, x_increment, y_increment}) do
next_x = current_x + x_increment
next_y = current_y + y_increment
steps = steps - 1
steps = steps - 1
points = MapSet.put(points, Point.init(next_x, next_y))
reduce_points({points, steps, next_x, next_y, x_increment, y_increment})
end

View file

@ -22,11 +22,11 @@ defimpl Vivid.Rasterize, for: Vivid.Path do
%Vivid.Point{x: 3, y: 3}
])
"""
@spec rasterize(Path.t, Bounds.t) :: MapSet.t
@spec rasterize(Path.t(), Bounds.t()) :: MapSet.t()
def rasterize(%Path{} = path, bounds) do
lines = path |> Path.to_lines
lines = path |> Path.to_lines()
Enum.reduce(lines, MapSet.new, fn(line, acc) ->
Enum.reduce(lines, MapSet.new(), fn line, acc ->
MapSet.union(acc, Rasterize.rasterize(line, bounds))
end)
end

View file

@ -13,14 +13,14 @@ defimpl Vivid.Rasterize, for: Vivid.Point do
iex> Vivid.Rasterize.rasterize(Vivid.Point.init(3,3), Vivid.Bounds.init(0, 0, 3, 3)) |> Enum.to_list
[%Vivid.Point{x: 3, y: 3}]
"""
@spec rasterize(Point.t, Bounds.t) :: MapSet.t
@spec rasterize(Point.t(), Bounds.t()) :: MapSet.t()
def rasterize(point, bounds) do
point = point |> Point.round
point = point |> Point.round()
if Bounds.contains?(bounds, point) do
MapSet.new([point])
else
MapSet.new
MapSet.new()
end
end
end

View file

@ -29,7 +29,7 @@ defimpl Vivid.Rasterize, for: Vivid.Polygon do
%Vivid.Point{x: 3, y: 3}
])
"""
@spec rasterize(Polygon.t, Bounds.t) :: MapSet.t
@spec rasterize(Polygon.t(), Bounds.t()) :: MapSet.t()
def rasterize(%Polygon{vertices: v}, _bounds) when length(v) < 3 do
raise InvalidPolygonError, "Polygon does not contain enough edges."
end
@ -46,15 +46,15 @@ defimpl Vivid.Rasterize, for: Vivid.Polygon do
defp filled_polygon_inside_area(polygon, bounds) do
polygon
|> SLPFA.fill
|> SLPFA.fill()
|> Enum.filter(&Bounds.contains?(bounds, &1))
|> Enum.into(MapSet.new)
|> Enum.into(MapSet.new())
end
defp polygon_border(polygon, bounds) do
lines = polygon |> Polygon.to_lines
lines = polygon |> Polygon.to_lines()
Enum.reduce(lines, MapSet.new, fn(line, acc) ->
Enum.reduce(lines, MapSet.new(), fn line, acc ->
MapSet.union(acc, Rasterize.rasterize(line, bounds))
end)
end

View file

@ -23,13 +23,15 @@ defmodule Vivid.RGBA do
"""
@type zero_to_one :: number
@opaque t :: %RGBA{red: zero_to_one,
green: zero_to_one,
blue: zero_to_one,
alpha: zero_to_one,
a_red: zero_to_one,
a_green: zero_to_one,
a_blue: zero_to_one}
@opaque t :: %RGBA{
red: zero_to_one,
green: zero_to_one,
blue: zero_to_one,
alpha: zero_to_one,
a_red: zero_to_one,
a_green: zero_to_one,
a_blue: zero_to_one
}
# I would put this at the bottom, but it has to be defined *before* it's
# used in the guard.
@ -54,7 +56,7 @@ defmodule Vivid.RGBA do
iex> Vivid.RGBA.init(0.1, 0.2, 0.3, 0.4)
#Vivid.RGBA<{0.1, 0.2, 0.3, 0.4}>
"""
@spec init(zero_to_one, zero_to_one, zero_to_one) :: RGBA.t
@spec init(zero_to_one, zero_to_one, zero_to_one) :: RGBA.t()
def init(red, green, blue), do: init(red, green, blue, 1)
@doc """
@ -72,46 +74,44 @@ defmodule Vivid.RGBA do
iex> Vivid.RGBA.init(0.1, 0.2, 0.3, 0.4)
#Vivid.RGBA<{0.1, 0.2, 0.3, 0.4}>
"""
@spec init(zero_to_one, zero_to_one, zero_to_one, zero_to_one) :: RGBA.t
@spec init(zero_to_one, zero_to_one, zero_to_one, zero_to_one) :: RGBA.t()
def init(red, green, blue, 1)
when zero_to_one?(red) and zero_to_one?(green) and zero_to_one?(blue)
do
when zero_to_one?(red) and zero_to_one?(green) and zero_to_one?(blue) do
%RGBA{
red: red,
green: green,
blue: blue,
alpha: 1,
a_red: red,
red: red,
green: green,
blue: blue,
alpha: 1,
a_red: red,
a_green: green,
a_blue: blue
a_blue: blue
}
end
def init(red, green, blue, 0)
when zero_to_one?(red) and zero_to_one?(green) and zero_to_one?(blue)
do
when zero_to_one?(red) and zero_to_one?(green) and zero_to_one?(blue) do
%RGBA{
red: red,
green: green,
blue: blue,
alpha: 0,
a_red: 0,
red: red,
green: green,
blue: blue,
alpha: 0,
a_red: 0,
a_green: 0,
a_blue: 0
a_blue: 0
}
end
def init(red, green, blue, alpha)
when zero_to_one?(red) and zero_to_one?(green) and zero_to_one?(blue) and zero_to_one?(alpha)
do
when zero_to_one?(red) and zero_to_one?(green) and zero_to_one?(blue) and
zero_to_one?(alpha) do
%RGBA{
red: red,
green: green,
blue: blue,
alpha: alpha,
a_red: red * alpha,
red: red,
green: green,
blue: blue,
alpha: alpha,
a_red: red * alpha,
a_green: green * alpha,
a_blue: blue * alpha
a_blue: blue * alpha
}
end
@ -123,7 +123,7 @@ defmodule Vivid.RGBA do
iex> Vivid.RGBA.white
#Vivid.RGBA<{1, 1, 1, 1}>
"""
@spec white() :: RGBA.t
@spec white() :: RGBA.t()
def white, do: RGBA.init(1, 1, 1)
@doc """
@ -134,7 +134,7 @@ defmodule Vivid.RGBA do
iex> Vivid.RGBA.black
#Vivid.RGBA<{0, 0, 0, 1}>
"""
@spec black() :: RGBA.t
@spec black() :: RGBA.t()
def black, do: RGBA.init(0, 0, 0)
@doc """
@ -146,7 +146,7 @@ defmodule Vivid.RGBA do
...> |> Vivid.RGBA.red
0.7
"""
@spec red(RGBA.t) :: zero_to_one
@spec red(RGBA.t()) :: zero_to_one
def red(%RGBA{red: r}), do: r
@doc """
@ -158,7 +158,7 @@ defmodule Vivid.RGBA do
...> |> Vivid.RGBA.green
0.6
"""
@spec green(RGBA.t) :: zero_to_one
@spec green(RGBA.t()) :: zero_to_one
def green(%RGBA{green: g}), do: g
@doc """
@ -170,7 +170,7 @@ defmodule Vivid.RGBA do
...> |> Vivid.RGBA.blue
0.5
"""
@spec blue(RGBA.t) :: zero_to_one
@spec blue(RGBA.t()) :: zero_to_one
def blue(%RGBA{blue: b}), do: b
@doc """
@ -182,7 +182,7 @@ defmodule Vivid.RGBA do
...> |> Vivid.RGBA.alpha
0.4
"""
@spec alpha(RGBA.t) :: zero_to_one
@spec alpha(RGBA.t()) :: zero_to_one
def alpha(%RGBA{alpha: a}), do: a
@doc """
@ -194,7 +194,7 @@ defmodule Vivid.RGBA do
...> |> Vivid.RGBA.to_hex
"#B39980"
"""
@spec to_hex(RGBA.t) :: String.t
@spec to_hex(RGBA.t()) :: String.t()
def to_hex(%RGBA{red: r, green: g, blue: b, alpha: 1}) do
r = r |> int_to_hex
g = g |> int_to_hex
@ -218,14 +218,21 @@ defmodule Vivid.RGBA do
iex> Vivid.RGBA.over(Vivid.RGBA.black, Vivid.RGBA.init(1,1,1, 0.5))
#Vivid.RGBA<{0.5, 0.5, 0.5, 1.0}>
"""
@spec over(RGBA.t, RGBA.t) :: RGBA.t
@spec over(RGBA.t(), RGBA.t()) :: RGBA.t()
def over(nil, %RGBA{} = colour), do: colour
def over(%RGBA{}, %RGBA{alpha: 1} = visible), do: visible
def over(%RGBA{} = visible, %RGBA{alpha: 0}), do: visible
def over(%RGBA{a_red: r0, a_green: g0, a_blue: b0, alpha: a0}, %RGBA{a_red: r1, a_green: g1, a_blue: b1, alpha: a1}) do
def over(%RGBA{a_red: r0, a_green: g0, a_blue: b0, alpha: a0}, %RGBA{
a_red: r1,
a_green: g1,
a_blue: b1,
alpha: a1
}) do
a = a0 + a1 * (1 - a0)
[r, g, b] = [{r0, r1}, {g0, g1}, {b0, b1}]
[r, g, b] =
[{r0, r1}, {g0, g1}, {b0, b1}]
|> Enum.map(fn {c0, c1} -> c1 + c0 * (1 - a1) end)
RGBA.init(r, g, b, a)
@ -246,7 +253,7 @@ defmodule Vivid.RGBA do
iex> Vivid.RGBA.black |> Vivid.RGBA.luminance
0.0
"""
@spec luminance(RGBA.t) :: zero_to_one
@spec luminance(RGBA.t()) :: zero_to_one
def luminance(%RGBA{a_red: r, a_green: g, a_blue: b}) do
[rl, gl, bl] = [r, g, b] |> Enum.map(&pow(&1, 2.2))
0.2128 * rl + 0.7150 * gl + 0.0722 * bl
@ -261,7 +268,7 @@ defmodule Vivid.RGBA do
The chacaters used (from black to white) are `" .:-=+*#%@"`. These are
chosen based on the `luminance/1` value of the colour.
"""
@spec to_ascii(RGBA.t) :: String.t
@spec to_ascii(RGBA.t()) :: String.t()
def to_ascii(%RGBA{} = colour) do
l = luminance(colour)
c = round(l * (@ascii_luminance_map_length - 1))
@ -269,9 +276,9 @@ defmodule Vivid.RGBA do
end
defp int_to_hex(n) when zero_to_one?(n) do
h = Integer.to_string(round(n * 0xff), 16)
h = Integer.to_string(round(n * 0xFF), 16)
case h |> String.length do
case h |> String.length() do
1 -> "0" <> h
2 -> h
end

View file

@ -1,9 +1,18 @@
defmodule Vivid.Shape do
alias Vivid.{Arc, Bounds, Box, Circle, Group, Line, Path, Point, Polygon}
@moduledoc """
Doesn't do anything - is merely a type to represent an arbitrary shape in typespecs.
"""
@type t :: Arc.t | Bounds.t | Box.t | Circle.t | Group.t |
Line.t | Path.t | Point.t | Polygon.t
@type t ::
Arc.t()
| Bounds.t()
| Box.t()
| Circle.t()
| Group.t()
| Line.t()
| Path.t()
| Point.t()
| Polygon.t()
end

View file

@ -95,7 +95,7 @@ defmodule Vivid.SLPFA do
" @@@@ @@@@ \n" <>
" \n"
"""
@spec fill(Polygon.t) :: MapSet.t
@spec fill(Polygon.t()) :: MapSet.t()
def fill(%Polygon{vertices: vertices}) do
vertices
|> create_edge_table
@ -105,7 +105,7 @@ defmodule Vivid.SLPFA do
defp process_edge_table([a0 | _] = edge_table) do
scan_line = a0.y_min
{active, edge_table} = update_active_list(scan_line, [], edge_table)
points = pixels_for_active_list(MapSet.new, active, scan_line)
points = pixels_for_active_list(MapSet.new(), active, scan_line)
process_edge_table(points, active, edge_table, scan_line + 1)
end
@ -122,14 +122,16 @@ defmodule Vivid.SLPFA do
Enum.map(active, fn
%EdgeBucket{distance_x: 0} = edge_bucket ->
edge_bucket
%EdgeBucket{distance_x: dx, sum: s} = edge_bucket ->
edge_bucket
|> Map.put(:sum, s + dx)
|> increment_edge
|> Map.put(:sum, s + dx)
|> increment_edge
end)
end
defp increment_edge(%EdgeBucket{sum: sum, distance_y: dy, sign: sign, x: x} = edge_bucket) when sum >= dy do
defp increment_edge(%EdgeBucket{sum: sum, distance_y: dy, sign: sign, x: x} = edge_bucket)
when sum >= dy do
edge_bucket
|> Map.put(:x, x + sign)
|> Map.put(:sum, sum - dy)
@ -142,45 +144,54 @@ defmodule Vivid.SLPFA do
active
|> Stream.chunk(2)
|> Enum.reduce(points, fn [a0, a1], points ->
Enum.reduce(a0.x + 1..a1.x - 1, points, fn x, points ->
Enum.reduce((a0.x + 1)..(a1.x - 1), points, fn x, points ->
MapSet.put(points, Point.init(x, y))
end)
end)
end
defp update_active_list(scan_line, active, edge_table) do
{active, edge_table} = active
{active, edge_table} =
active
|> Stream.concat(edge_table)
|> Enum.reduce({[], []}, fn
%EdgeBucket{y_min: y_min, y_max: y_max} = edge, {active, edge_table} when y_min <= scan_line and y_max > scan_line ->
%EdgeBucket{y_min: y_min, y_max: y_max} = edge, {active, edge_table}
when y_min <= scan_line and y_max > scan_line ->
active = [edge | active]
{active, edge_table}
%EdgeBucket{y_min: y_min} = edge, {active, edge_table} when y_min >= scan_line ->
edge_table = [edge | edge_table]
{active, edge_table}
_edge_bucket, {active, edge_table} ->
{active, edge_table}
end)
new_active = active
new_active =
active
|> Enum.sort(&sort_by_x_and_slope(&1, &2))
{new_active, edge_table}
end
defp sort_by_x_and_slope(%EdgeBucket{x: x0},
%EdgeBucket{x: x1})
when x0 < x1, do: true
defp sort_by_x_and_slope(%EdgeBucket{x: x0},
%EdgeBucket{x: x1})
when x0 > x1, do: false
defp sort_by_x_and_slope(%EdgeBucket{distance_x: dx0, distance_y: dy0},
%EdgeBucket{distance_x: dx1, distance_y: dy1}),
do: (dx0 / dy0) < (dx1 / dy1)
defp sort_by_x_and_slope(%EdgeBucket{x: x0}, %EdgeBucket{x: x1})
when x0 < x1,
do: true
defp sort_by_x_and_slope(%EdgeBucket{x: x0}, %EdgeBucket{x: x1})
when x0 > x1,
do: false
defp sort_by_x_and_slope(%EdgeBucket{distance_x: dx0, distance_y: dy0}, %EdgeBucket{
distance_x: dx1,
distance_y: dy1
}),
do: dx0 / dy0 < dx1 / dy1
defp create_edge_table(vertices) do
vertices
|> Stream.with_index
|> Stream.with_index()
|> Stream.map(fn {p0, idx} ->
p1 = Enum.at(vertices, idx - 1)
Line.init(p0, p1)
@ -191,25 +202,28 @@ defmodule Vivid.SLPFA do
|> Enum.sort(&sort_by_min_y(&1, &2))
end
defp line_left_to_right(%Line{origin: %Point{x: x0} = p0, termination: %Point{x: x1} = p1}) when x0 > x1, do: Line.init(p1, p0)
defp line_left_to_right(%Line{origin: %Point{x: x0} = p0, termination: %Point{x: x1} = p1})
when x0 > x1,
do: Line.init(p1, p0)
defp line_left_to_right(line), do: line
defp line_to_edge_bucket(%Line{origin: p0, termination: p1}) do
y_max = if p0.y > p1.y, do: p0.y, else: p1.y
y_min = if p0.y < p1.y, do: p0.y, else: p1.y
y_max = if p0.y > p1.y, do: p0.y, else: p1.y
y_min = if p0.y < p1.y, do: p0.y, else: p1.y
init_x = if p0.y < p1.y, do: p0.x, else: p1.x
sign = if p1.y - p0.y < 0, do: -1, else: 1
dx = abs(p1.x - p0.x)
dy = abs(p1.y - p0.y)
sign = if p1.y - p0.y < 0, do: -1, else: 1
dx = abs(p1.x - p0.x)
dy = abs(p1.y - p0.y)
%EdgeBucket{
y_min: round(y_min),
y_max: round(y_max),
x: round(init_x),
sign: sign,
y_min: round(y_min),
y_max: round(y_max),
x: round(init_x),
sign: sign,
distance_x: round(dx),
distance_y: round(dy),
sum: 0
sum: 0
}
end

View file

@ -2,7 +2,7 @@ defmodule Vivid.Transform do
alias Vivid.{Point, Transform, Bounds, Shape}
alias Vivid.Transformable
import Vivid.Math
defstruct [operations: [], shape: nil]
defstruct operations: [], shape: nil
defmodule Operation do
alias __MODULE__
@ -10,7 +10,7 @@ defmodule Vivid.Transform do
@moduledoc false
@opaque t :: %Operation{function: function, name: String.t}
@opaque t :: %Operation{function: function, name: String.t()}
end
@moduledoc ~S"""
@ -72,8 +72,8 @@ defmodule Vivid.Transform do
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@opaque t :: %Transform{shape: Shape.t, operations: [Operation.t]}
@type shape_or_transform :: Transform.t | Shape.t
@opaque t :: %Transform{shape: Shape.t(), operations: [Operation.t()]}
@type shape_or_transform :: Transform.t() | Shape.t()
@type degrees :: number
@doc """
@ -86,7 +86,7 @@ defmodule Vivid.Transform do
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{15, 10}>, #Vivid.Point<{15, 15}>, #Vivid.Point<{10, 15}>, #Vivid.Point<{10, 10}>]>
"""
@spec translate(shape_or_transform, number, number) :: Transform.t
@spec translate(shape_or_transform, number, number) :: Transform.t()
def translate(shape, x, y) do
fun = fn _shape ->
&Transform.Point.translate(&1, x, y)
@ -105,7 +105,7 @@ defmodule Vivid.Transform do
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{12.5, 2.5}>, #Vivid.Point<{12.5, 12.5}>, #Vivid.Point<{2.5, 12.5}>, #Vivid.Point<{2.5, 2.5}>]>
"""
@spec scale(shape_or_transform, number) :: Transform.t
@spec scale(shape_or_transform, number) :: Transform.t()
def scale(shape, uniform) do
fun = fn shape ->
origin = Bounds.center_of(shape)
@ -126,7 +126,7 @@ defmodule Vivid.Transform do
#Vivid.Polygon<[#Vivid.Point<{12.5, -2.5}>, #Vivid.Point<{12.5, 17.5}>, #Vivid.Point<{2.5, 17.5}>, #Vivid.Point<{2.5, -2.5}>]>
"""
@spec scale(shape_or_transform, number, number) :: Transform.t
@spec scale(shape_or_transform, number, number) :: Transform.t()
def scale(shape, x, y) do
fun = fn shape ->
origin = Bounds.center_of(shape)
@ -163,9 +163,10 @@ defmodule Vivid.Transform do
"@@@@@@@@ @@@@@@@@\n" <>
"@@@@@@@@@@@@@@@@@\n"
"""
@spec rotate(shape_or_transform, degrees) :: Transform.t
@spec rotate(shape_or_transform, degrees) :: Transform.t()
def rotate(shape, degrees) do
radians = degrees_to_radians(degrees)
fun = fn shape ->
origin = Bounds.center_of(shape)
&Transform.Point.rotate_radians(&1, origin, radians)
@ -201,9 +202,10 @@ defmodule Vivid.Transform do
"@@@@@@@@ @@@@@@@@\n" <>
"@@@@@@@@@@@@@@@@@\n"
"""
@spec rotate(shape_or_transform, degrees, Point.t) :: Transform.t
@spec rotate(shape_or_transform, degrees, Point.t()) :: Transform.t()
def rotate(shape, degrees, %Point{x: x, y: y} = origin) do
radians = degrees_to_radians(degrees)
fun = fn _shape ->
&Transform.Point.rotate_radians(&1, origin, radians)
end
@ -237,18 +239,20 @@ defmodule Vivid.Transform do
"@ @\n" <>
"@@@@@@@@@@@@@\n"
"""
@spec center(shape_or_transform, Shape.t) :: Transform.t
@spec center(shape_or_transform, Shape.t()) :: Transform.t()
def center(shape, bounds) do
bounds = Bounds.bounds(bounds)
bounds_width = Bounds.width(bounds)
bounds = Bounds.bounds(bounds)
bounds_width = Bounds.width(bounds)
bounds_height = Bounds.height(bounds)
bounds_center = Bounds.center_of(bounds)
fun = fn shape ->
shape_center = Bounds.center_of(shape)
{tr_x, tr_y} = Point.vector(shape_center, bounds_center)
&Transform.Point.translate(&1, tr_x, tr_y)
end
apply_transform(shape, fun, "center-within-#{bounds_width}-#{bounds_height}")
end
@ -262,22 +266,23 @@ defmodule Vivid.Transform do
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{40.0, 0.0}>, #Vivid.Point<{40.0, 80.0}>, #Vivid.Point<{0.0, 80.0}>, #Vivid.Point<{0.0, 0.0}>]>
"""
@spec stretch(shape_or_transform, Shape.t) :: Transform.t
@spec stretch(shape_or_transform, Shape.t()) :: Transform.t()
def stretch(shape, bounds) do
bounds = Bounds.bounds(bounds)
bounds_min = Bounds.min(bounds)
bounds_width = Bounds.width(bounds)
bounds = Bounds.bounds(bounds)
bounds_min = Bounds.min(bounds)
bounds_width = Bounds.width(bounds)
bounds_height = Bounds.height(bounds)
fun = fn shape ->
shape_bounds = Bounds.bounds(shape)
shape_width = Bounds.width(shape_bounds)
shape_height = Bounds.height(shape_bounds)
shape_min = Bounds.min(shape_bounds)
shape_bounds = Bounds.bounds(shape)
shape_width = Bounds.width(shape_bounds)
shape_height = Bounds.height(shape_bounds)
shape_min = Bounds.min(shape_bounds)
{tr_x, tr_y} = Point.vector(shape_min, bounds_min)
{tr_x, tr_y} = Point.vector(shape_min, bounds_min)
scale_x = bounds_width / shape_width
scale_y = bounds_height / shape_height
scale_x = bounds_width / shape_width
scale_y = bounds_height / shape_height
fn point ->
point
@ -285,6 +290,7 @@ defmodule Vivid.Transform do
|> Transform.Point.scale(scale_x, scale_y)
end
end
apply_transform(shape, fun, "stretch-to-#{bounds_width}-#{bounds_height}")
end
@ -298,23 +304,24 @@ defmodule Vivid.Transform do
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{40.0, 0.0}>, #Vivid.Point<{40.0, 40.0}>, #Vivid.Point<{0.0, 40.0}>, #Vivid.Point<{0.0, 0.0}>]>
"""
@spec fill(shape_or_transform, Shape.t) :: Transform.t
@spec fill(shape_or_transform, Shape.t()) :: Transform.t()
def fill(shape, bounds) do
bounds = Bounds.bounds(bounds)
bounds_min = Bounds.min(bounds)
bounds_width = Bounds.width(bounds)
bounds = Bounds.bounds(bounds)
bounds_min = Bounds.min(bounds)
bounds_width = Bounds.width(bounds)
bounds_height = Bounds.height(bounds)
fun = fn shape ->
shape_bounds = Bounds.bounds(shape)
shape_width = Bounds.width(shape_bounds)
shape_height = Bounds.height(shape_bounds)
shape_min = Bounds.min(shape_bounds)
shape_bounds = Bounds.bounds(shape)
shape_width = Bounds.width(shape_bounds)
shape_height = Bounds.height(shape_bounds)
shape_min = Bounds.min(shape_bounds)
{tr_x, tr_y} = Point.vector(shape_min, bounds_min)
{tr_x, tr_y} = Point.vector(shape_min, bounds_min)
scale_x = bounds_width / shape_width
scale_y = bounds_height / shape_height
scale = if scale_x < scale_y, do: scale_x, else: scale_y
scale_x = bounds_width / shape_width
scale_y = bounds_height / shape_height
scale = if scale_x < scale_y, do: scale_x, else: scale_y
fn point ->
point
@ -322,6 +329,7 @@ defmodule Vivid.Transform do
|> Transform.Point.scale(scale, scale)
end
end
apply_transform(shape, fun, "fill-to-#{bounds_width}-#{bounds_height}")
end
@ -336,23 +344,24 @@ defmodule Vivid.Transform do
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{80.0, 0.0}>, #Vivid.Point<{80.0, 80.0}>, #Vivid.Point<{0.0, 80.0}>, #Vivid.Point<{0.0, 0.0}>]>
"""
@spec overflow(shape_or_transform, Shape.t) :: Transform.t
@spec overflow(shape_or_transform, Shape.t()) :: Transform.t()
def overflow(shape, bounds) do
bounds = Bounds.bounds(bounds)
bounds_min = Bounds.min(bounds)
bounds_width = Bounds.width(bounds)
bounds = Bounds.bounds(bounds)
bounds_min = Bounds.min(bounds)
bounds_width = Bounds.width(bounds)
bounds_height = Bounds.height(bounds)
fun = fn shape ->
shape_bounds = Bounds.bounds(shape)
shape_width = Bounds.width(shape_bounds)
shape_height = Bounds.height(shape_bounds)
shape_min = Bounds.min(shape_bounds)
shape_bounds = Bounds.bounds(shape)
shape_width = Bounds.width(shape_bounds)
shape_height = Bounds.height(shape_bounds)
shape_min = Bounds.min(shape_bounds)
{tr_x, tr_y} = Point.vector(shape_min, bounds_min)
{tr_x, tr_y} = Point.vector(shape_min, bounds_min)
scale_x = bounds_width / shape_width
scale_y = bounds_height / shape_height
scale = if scale_x > scale_y, do: scale_x, else: scale_y
scale_x = bounds_width / shape_width
scale_y = bounds_height / shape_height
scale = if scale_x > scale_y, do: scale_x, else: scale_y
fn point ->
point
@ -360,6 +369,7 @@ defmodule Vivid.Transform do
|> Transform.Point.scale(scale, scale)
end
end
apply_transform(shape, fun, "overflow-#{bounds_width}-#{bounds_height}")
end
@ -388,16 +398,16 @@ defmodule Vivid.Transform do
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{25, 10}>, #Vivid.Point<{25, 20}>, #Vivid.Point<{15, 20}>, #Vivid.Point<{15, 10}>]>
"""
@spec transform(shape_or_transform, function) :: Transform.t
@spec transform(shape_or_transform, function) :: Transform.t()
def transform(shape, fun), do: apply_transform(shape, fun, inspect(fun))
@doc """
Apply a transformation pipeline returning the modified shape.
"""
@spec apply(Transform.t) :: Shape.t
@spec apply(Transform.t()) :: Shape.t()
def apply(%Transform{operations: operations, shape: shape}) do
operations
|> Enum.reverse
|> Enum.reverse()
|> Enum.reduce(shape, fn %Operation{function: fun}, shape ->
transform = fun.(shape)
Transformable.transform(shape, transform)
@ -408,6 +418,7 @@ defmodule Vivid.Transform do
operations = [%Operation{function: fun, name: name} | operations]
%{transform | operations: operations}
end
defp apply_transform(shape, fun, name) do
%Transform{operations: [%Operation{function: fun, name: name}], shape: shape}
end

View file

@ -16,19 +16,20 @@ defmodule Vivid.Transform.Point do
@doc """
Translate `point` (ie move it) by adding `x` and `y` to it's coordinates.
"""
@spec translate(Point.t, number, number) :: Point.t
@spec translate(Point.t(), number, number) :: Point.t()
def translate(%Point{x: x0, y: y0} = _point, x, y), do: Point.init(x0 + x, y0 + y)
@doc """
Scale `point` (ie move it) by multiplying it's distance from the `0`, `0` point by `x_factor` and `y_factor`.
"""
@spec scale(Point, number, number) :: Point.t
def scale(%Point{} = point, x_factor, y_factor), do: scale(point, x_factor, y_factor, Point.init(0, 0))
@spec scale(Point, number, number) :: Point.t()
def scale(%Point{} = point, x_factor, y_factor),
do: scale(point, x_factor, y_factor, Point.init(0, 0))
@doc """
Scale `point` (ie move it) by multiplying it's distance from the origin point by `x_factor` and `y_factor`.
"""
@spec scale(Point.t, number, number, Point.t) :: Point.t
@spec scale(Point.t(), number, number, Point.t()) :: Point.t()
def scale(%Point{x: x, y: y} = _point, x_factor, y_factor, %Point{x: xo, y: yo} = _origin) do
x = (x - xo) * x_factor + xo
y = (y - yo) * y_factor + yo
@ -38,13 +39,14 @@ defmodule Vivid.Transform.Point do
@doc """
Rotate `point` `degrees` around an `origin` point.
"""
@spec rotate(Point.t, Point.t, degrees) :: Point.t
def rotate(point, origin, degrees), do: rotate_radians(point, origin, degrees_to_radians(degrees))
@spec rotate(Point.t(), Point.t(), degrees) :: Point.t()
def rotate(point, origin, degrees),
do: rotate_radians(point, origin, degrees_to_radians(degrees))
@doc """
Rotate `point` `radians` around an `origin` point.
"""
@spec rotate_radians(Point.t, Point.t, radians) :: Point.t
@spec rotate_radians(Point.t(), Point.t(), radians) :: Point.t()
def rotate_radians(%Point{x: x0, y: y0} = _point, %Point{x: x1, y: y1} = _origin, radians) do
x = cos(radians) * (x0 - x1) - sin(radians) * (y0 - y1) + x1
y = sin(radians) * (x0 - x1) + cos(radians) * (y0 - y1) + y1

View file

@ -1,5 +1,6 @@
defprotocol Vivid.Transformable do
alias Vivid.Shape
@moduledoc """
This protocol is used to apply *point* transformations to a shape.
"""
@ -7,6 +8,6 @@ defprotocol Vivid.Transformable do
@doc """
Transform all of a shape's points using `fun`.
"""
@spec transform(Shape.t, function) :: Shape.t
@spec transform(Shape.t(), function) :: Shape.t()
def transform(shape, fun)
end

View file

@ -10,11 +10,11 @@ defimpl Vivid.Transformable, for: Vivid.Arc do
Many of the transformations can't be applied to an Arc, but we
can convert it to a path and then use that to apply transformations.
"""
@spec transform(Arc.t, (Point.t -> Point.t)) :: Path.t
@spec transform(Arc.t(), (Point.t() -> Point.t())) :: Path.t()
def transform(arc, fun) do
arc
|> Arc.to_path
|> Arc.to_path()
|> Stream.map(&Transformable.transform(&1, fun))
|> Enum.into(Path.init)
|> Enum.into(Path.init())
end
end

View file

@ -7,10 +7,10 @@ defimpl Vivid.Transformable, for: Vivid.Box do
* `box` - the box to modify.
* `fun` - the transformation function to apply.
"""
@spec transform(Box.t, (Point.t -> Point.t)) :: Box.t
@spec transform(Box.t(), (Point.t() -> Point.t())) :: Box.t()
def transform(box, fun) do
box
|> Box.to_polygon
|> Box.to_polygon()
|> Transformable.transform(fun)
end
end

View file

@ -10,10 +10,10 @@ defimpl Vivid.Transformable, for: Vivid.Circle do
Many of the transformations can't be applied to a Circle, but we
can convert it to a polygon and then use that to apply transformations.
"""
@spec transform(Circle.t, (Point.t -> Point.t)) :: Polygon.t
@spec transform(Circle.t(), (Point.t() -> Point.t())) :: Polygon.t()
def transform(%Circle{fill: f} = circle, fun) do
circle
|> Circle.to_polygon
|> Circle.to_polygon()
|> Stream.map(&Transformable.transform(&1, fun))
|> Enum.into(Polygon.init([], f))
end

View file

@ -7,10 +7,10 @@ defimpl Vivid.Transformable, for: Vivid.Group do
* `group` - the group to modify.
* `fun` - the transformation function to apply.
"""
@spec transform(Group.t, (Point.t -> Point.t)) :: Group.t
@spec transform(Group.t(), (Point.t() -> Point.t())) :: Group.t()
def transform(group, fun) do
group
|> Stream.map(&Transformable.transform(&1, fun))
|> Enum.into(Group.init)
|> Enum.into(Group.init())
end
end

View file

@ -7,10 +7,10 @@ defimpl Vivid.Transformable, for: Vivid.Line do
* `line` - the line to modify.
* `fun` - the transformation function to apply.
"""
@spec transform(Line.t, (Point.t -> Point.t)) :: Line.t
@spec transform(Line.t(), (Point.t() -> Point.t())) :: Line.t()
def transform(line, fun) do
origin = line |> Line.origin |> Transformable.transform(fun)
term = line |> Line.termination |> Transformable.transform(fun)
origin = line |> Line.origin() |> Transformable.transform(fun)
term = line |> Line.termination() |> Transformable.transform(fun)
Line.init(origin, term)
end
end

View file

@ -7,10 +7,10 @@ defimpl Vivid.Transformable, for: Vivid.Path do
* `path` - the path to modify.
* `fun` - the transformation function to apply.
"""
@spec transform(Path.t, (Point.t -> Point.t)) :: Path.t
@spec transform(Path.t(), (Point.t() -> Point.t())) :: Path.t()
def transform(path, fun) do
path
|> Stream.map(&Transformable.transform(&1, fun))
|> Enum.into(Path.init)
|> Enum.into(Path.init())
end
end

View file

@ -7,6 +7,6 @@ defimpl Vivid.Transformable, for: Vivid.Point do
* `point` - the point to modify.
* `fun` - the transformation function to apply.
"""
@spec transform(Point.t, (Point.t -> Point.t)) :: Point.t
@spec transform(Point.t(), (Point.t() -> Point.t())) :: Point.t()
def transform(point, fun), do: fun.(point)
end

View file

@ -7,7 +7,7 @@ defimpl Vivid.Transformable, for: Vivid.Polygon do
* `polygon` - the polygon to modify.
* `fun` - the transformation function to apply.
"""
@spec transform(Polygon.t, (Point.t -> Point.t)) :: Polygon.t
@spec transform(Polygon.t(), (Point.t() -> Point.t())) :: Polygon.t()
def transform(%Polygon{fill: fill} = polygon, fun) do
polygon
|> Stream.map(&Transformable.transform(&1, fun))

34
mix.exs
View file

@ -4,19 +4,23 @@ defmodule Vivid.Mixfile do
@version "0.4.2"
def project do
[app: :vivid,
version: @version,
description: description(),
elixir: "~> 1.3",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
package: package(),
deps: deps(),
docs: [source_ref: "v#{@version}",
main: "Vivid",
canonical: "http://hexdocs.pm/vivid",
source_url: "https://github.com/jamesotron/vivid.ex",
extras: ["guides/getting-started.md"]]]
[
app: :vivid,
version: @version,
description: description(),
elixir: "~> 1.3",
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
package: package(),
deps: deps(),
docs: [
source_ref: "v#{@version}",
main: "Vivid",
canonical: "http://hexdocs.pm/vivid",
source_url: "https://github.com/jamesotron/vivid.ex",
extras: ["guides/getting-started.md"]
]
]
end
# Configuration for the OTP application
@ -34,8 +38,8 @@ defmodule Vivid.Mixfile do
def package do
[
maintainers: [ "James Harton <james@messagerocket.co>" ],
licenses: [ "MIT" ],
maintainers: ["James Harton <james@messagerocket.co>"],
licenses: ["MIT"],
links: %{
"Source" => "https://github.com/jamesotron/vivid.ex"
}

View file

@ -1,4 +1,4 @@
defmodule Collectable.Vivid.GroupTest do
use ExUnit.Case
doctest Collectable.Vivid.Group
end
end

View file

@ -1,4 +1,4 @@
defmodule Collectable.Vivid.PathTest do
use ExUnit.Case
doctest Collectable.Vivid.Path
end
end

View file

@ -1,4 +1,4 @@
defmodule Collectable.Vivid.PolygonTest do
use ExUnit.Case
doctest Collectable.Vivid.Polygon
end
end

View file

@ -1,4 +1,4 @@
defmodule Enumerable.Vivid.BufferTest do
use ExUnit.Case
doctest Enumerable.Vivid.Buffer
end
end

View file

@ -1,4 +1,4 @@
defmodule Enumerable.Vivid.GroupTest do
use ExUnit.Case
doctest Enumerable.Vivid.Group
end
end

View file

@ -1,4 +1,4 @@
defmodule Enumerable.Vivid.LineTest do
use ExUnit.Case
doctest Enumerable.Vivid.Line
end
end

View file

@ -1,4 +1,4 @@
defmodule Enumerable.Vivid.PathTest do
use ExUnit.Case
doctest Enumerable.Vivid.Path
end
end

View file

@ -1,4 +1,4 @@
defmodule Enumerable.Vivid.PolygonTest do
use ExUnit.Case
doctest Enumerable.Vivid.Polygon
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.ArcTest do
use ExUnit.Case
doctest Vivid.Arc
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.BoundsTest do
use ExUnit.Case
doctest Vivid.Bounds
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.BoxTest do
use ExUnit.Case
doctest Vivid.Box
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.BufferTest do
use ExUnit.Case
doctest Vivid.Buffer
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.CircleTest do
use ExUnit.Case
doctest Vivid.Circle
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.FontTest do
use ExUnit.Case
doctest Vivid.Font
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.FrameTest do
use ExUnit.Case
doctest Vivid.Frame
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.GroupTest do
use ExUnit.Case
doctest Vivid.Group
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.LineTest do
use ExUnit.Case
doctest Vivid.Line
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.MathTest do
use ExUnit.Case
doctest Vivid.Math
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.PathTest do
use ExUnit.Case
doctest Vivid.Path
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.PointTest do
use ExUnit.Case
doctest Vivid.Point
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.Rasterize.CircleTest do
use ExUnit.Case
doctest Vivid.Rasterize.Vivid.Circle
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.Rasterize.GroupTest do
use ExUnit.Case
doctest Vivid.Rasterize.Vivid.Group
end
end

View file

@ -1,4 +1,4 @@
defmodule Vivid.Rasterize.LineTest do
use ExUnit.Case
doctest Vivid.Rasterize.Vivid.Line
end
end

Some files were not shown because too many files have changed in this diff Show more