improvement: bring code up to date and add linters, etc.

This commit is contained in:
James Harton 2023-07-25 20:29:42 +12:00
parent 01a0089384
commit 2993639ae2
Signed by: james
GPG key ID: 90E82DAA13F624F4
78 changed files with 835 additions and 439 deletions

33
.check.exs Normal file
View file

@ -0,0 +1,33 @@
[
## don't run tools concurrently
# parallel: false,
## don't print info about skipped tools
skipped: false,
## always run tools in fix mode (put it in ~/.check.exs locally, not in project config)
# fix: true,
## don't retry automatically even if last run resulted in failures
# retry: false,
## list of tools (see `mix check` docs for a list of default curated tools)
tools: [
## curated tools may be disabled (e.g. the check for compilation warnings)
# {:compiler, false},
## ...or have command & args adjusted (e.g. enable skip comments for sobelow)
{:sobelow, false},
## ...or reordered (e.g. to see output from dialyzer before others)
# {:dialyzer, order: -1},
## ...or reconfigured (e.g. disable parallel execution of ex_unit in umbrella)
# {:ex_unit, umbrella: [parallel: false]},
## custom new tools may be added (Mix tasks or arbitrary commands)
# {:my_task, "mix my_task", env: %{"MIX_ENV" => "prod"}},
# {:my_tool, ["my_tool", "arg with spaces"]}
# {:esbuild, "mix esbuild prod"}
]
]

View file

@ -1,145 +0,0 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any exec using `mix credo -C <name>`. If no exec name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: ["lib/", "src/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
# For some checks, like AliasUsage, you can only customize the priority
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage, priority: :low},
# For others you can set parameters
# If you don't want the `setup` and `test` macro calls in ExUnit tests
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
#
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
{Credo.Check.Design.TagTODO, exit_status: 2},
{Credo.Check.Design.TagFIXME},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 140},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SpaceAfterCommas},
{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting},
{Credo.Check.Refactor.PipeChainStart},
{Credo.Check.Refactor.UnlessWithElse},
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},
# Controversial and experimental checks (opt-in, just remove `, false`)
#
{Credo.Check.Refactor.ABCSize},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.VariableRebinding},
{Credo.Check.Warning.MapGetUnsafePass},
{Credo.Check.Consistency.MultiAliasImportRequireUse},
# Deprecated checks (these will be deleted after a grace period)
#
{Credo.Check.Readability.Specs, false},
{Credo.Check.Warning.NameRedeclarationByAssignment, false},
{Credo.Check.Warning.NameRedeclarationByCase, false},
{Credo.Check.Warning.NameRedeclarationByDef, false},
{Credo.Check.Warning.NameRedeclarationByFn, false},
# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
}

504
.drone.yml Normal file
View file

@ -0,0 +1,504 @@
kind: pipeline
type: docker
name: build
steps:
- name: restore ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
- name: install dependencies
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
ASDF_DIR: /root/.asdf
depends_on:
- restore ASDF cache
- restore build cache
commands:
- asdf_install
- rm -rf .asdf/downloads
- . $ASDF_DIR/asdf.sh
- mix local.hex --if-missing --force
- mix local.rebar --if-missing --force
- mix deps.get
- mix deps.compile
- name: store ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
depends_on:
- install dependencies
settings:
rebuild: true
override: false
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: store build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
depends_on:
- install dependencies
settings:
rebuild: true
override: false
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
---
kind: pipeline
type: docker
name: test
depends_on:
- build
steps:
- name: restore ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
- name: mix compile
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- restore ASDF cache
- restore build cache
commands:
- asdf mix compile --warnings-as-errors
- name: mix test
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- mix compile
commands:
- asdf mix test
- name: mix credo
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- mix compile
commands:
- asdf mix credo --strict
- name: mix hex.audit
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- mix compile
commands:
- asdf mix hex.audit
- name: mix format
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- mix compile
commands:
- asdf mix format --check-formatted
- name: mix deps.unlock
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- mix compile
commands:
- asdf mix deps.unlock --check-unused
- name: mix doctor
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- mix compile
commands:
- asdf mix doctor --full
- name: mix git_ops.check_message
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- mix compile
commands:
- git log -1 --format=%s > .last_commit_message
- asdf mix git_ops.check_message .last_commit_message
---
kind: pipeline
type: docker
name: git ops
trigger:
branch:
- main
event:
- push
depends_on:
- test
steps:
- name: restore ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
- name: mix git_ops.release
image: code.harton.nz/james/asdf_container:latest
pull: "always"
depends_on:
- restore ASDF cache
- restore build cache
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
ASDF_DIR: /root/.asdf
DRONE_TOKEN:
from_secret: DRONE_TOKEN
commands:
- git fetch --tags
- . $ASDF_DIR/asdf.sh
- mix git_ops.project_info --format=shell > before.env
- mix git_ops.release --yes --no-major || true
- mix git_ops.project_info --format=shell > after.env
- . ./before.env
- export OLD_APP_VERSION=$${APP_VERSION}
- . ./after.env
- export NEW_APP_VERSION=$${APP_VERSION}
- if [ "v$${OLD_APP_VERSION}" != "v$${NEW_APP_VERSION}" ]; then
- export GIT_URL=$(echo $DRONE_GIT_HTTP_URL | sed -e "s/:\\/\\//:\\/\\/$DRONE_REPO_OWNER:$DRONE_TOKEN@/")
- git push $${GIT_URL} "HEAD:${DRONE_COMMIT_REF}" "refs/tags/v$${NEW_APP_VERSION}"
- fi
---
kind: pipeline
type: docker
name: release
trigger:
ref:
include:
- refs/tags/v**
depends_on:
- test
steps:
- name: restore ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
- name: build artifacts
image: code.harton.nz/james/asdf_container:latest
pull: "always"
depends_on:
- restore ASDF cache
- restore build cache
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
ASDF_DIR: /root/.asdf
commands:
- . $ASDF_DIR/asdf.sh
- mix git_ops.project_info --format=shell > app.env
- . ./app.env
- mkdir artifacts
- mix esbuild prod
- mix hex.build -o "artifacts/$${APP_NAME}-$${APP_VERSION}-pkg.tar"
- gzip "artifacts/$${APP_NAME}-$${APP_VERSION}-pkg.tar"
- mix docs
- tar zcvf "artifacts/$${APP_NAME}-$${APP_VERSION}-docs.tar.gz" doc/
- git tag -l --format='%(contents:subject)' v$${APP_VERSION} > tag_subject
- git tag -l --format='%(contents:body)' v$${APP_VERSION} > tag_body
- name: gitea release
image: plugins/gitea-release
depends_on:
- build artifacts
settings:
api_key:
from_secret: DRONE_TOKEN
base_url: https://code.harton.nz
files: artifacts/*.tar.gz
checksum: sha256
title: tag_subject
note: tag_body
- name: hex release
image: code.harton.nz/james/asdf_container:latest
pull: "always"
depends_on:
- restore ASDF cache
- restore build cache
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
MIX_HOME: /drone/src/.mix
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
ASDF_DIR: /root/.asdf
HEX_API_KEY:
from_secret: HEX_API_KEY
commands:
- . $ASDF_DIR/asdf.sh
- mix hex.publish --yes

4
.formatter.exs Normal file
View file

@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

2
.tool-versions Normal file
View file

@ -0,0 +1,2 @@
erlang 26.0.2
elixir 1.15.4

View file

@ -1,18 +1,17 @@
# Vivid
[![Build Status](https://travis-ci.org/jamesotron/vivid.ex.svg?branch=master)](https://travis-ci.org/jamesotron/vivid.ex)
[![Inline docs](https://inch-ci.org/github/jamesotron/vivid.ex.svg)](https://inch-ci.org/github/jamesotron/vivid.ex)
[![Build Status](https://drone.harton.nz/api/badges/james/vivid/status.svg?ref=refs/heads/main)](https://drone.harton.nz/james/vivid)
Vivid is a simple 2D rendering library.
## Features
* Supports drawing and manipulating a number of basic 2D primitives.
* Supports filling arbitrary polygons.
* Supports arbitrary transformations shape transformations.
* Renders shapes onto a buffer.
* 100% pure Elixir with no dependencies.
* Render to PNG using [vivid_png](https://github.com/jamesotron/vivid_png.ex).
- Supports drawing and manipulating a number of basic 2D primitives.
- Supports filling arbitrary polygons.
- Supports arbitrary transformations shape transformations.
- Renders shapes onto a buffer.
- 100% pure Elixir with no dependencies.
- Render to PNG using [vivid_png](https://code.harton.nz/james/vivid_png).
## Demo
@ -126,7 +125,7 @@ This library is now in use in several projects and seems to work well.
Future improvements include:
- Improve `Vivid.SLPFA`.
- Add transformations which can apply rotation matrices directly.
- Add ability to composit multiple frames together.
- Support bitmaps as a shape.
- Improve `Vivid.SLPFA`.
- Add transformations which can apply rotation matrices directly.
- Add ability to composit multiple frames together.
- Support bitmaps as a shape.

View file

@ -1,30 +1,9 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
import Config
# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.
# You can configure for your application as:
#
# config :vivid, key: :value
#
# And access this configuration in your application as:
#
# Application.get_env(:vivid, :key)
#
# Or configure a 3rd-party app:
#
# config :logger, level: :info
#
# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"
config :git_ops,
mix_project: Mix.Project.get!(),
changelog_file: "CHANGELOG.md",
repository_url: "https://code.harton.nz/james/vivid",
manage_mix_version?: true,
version_tag_prefix: "v",
manage_readme_version: false

View file

@ -8,27 +8,25 @@ defimpl Enumerable, for: Vivid.Buffer do
@doc """
Returns the number of pixels in a buffer.
"""
@spec count(Buffer.t()) :: {:ok, non_neg_integer}
@impl true
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}
@impl true
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()
@impl true
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()}
@impl true
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}
@impl true
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}
@impl true
def member?(%Frame{shapes: shapes}, shape), do: {:ok, Enum.member?(shapes, shape)}
@doc """
@ -41,14 +41,12 @@ 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()
@impl true
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()}
@impl true
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}
@impl true
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}
@impl true
def member?(%Group{shapes: shapes}, shape), do: {:ok, Enum.member?(shapes, shape)}
@doc """
@ -41,14 +41,12 @@ 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()
@impl true
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()}
@impl true
def slice(%Group{shapes: shapes}), do: Enumerable.MapSet.slice(shapes)
end

View file

@ -1,5 +1,5 @@
defimpl Enumerable, for: Vivid.Line do
alias Vivid.{Line, Point}
alias Vivid.Line
@moduledoc """
Implements the Enumerable protocol for %Line{}
@ -15,7 +15,7 @@ defimpl Enumerable, for: Vivid.Line do
...> |> Enum.count
2
"""
@spec count(Line.t()) :: non_neg_integer
@impl true
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
@impl true
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,15 +49,13 @@ 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()
@impl true
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()}
@impl true
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}
@impl true
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}
@impl true
def member?(%Path{vertices: points}, %Point{} = point), do: {:ok, Enum.member?(points, point)}
@doc """
@ -39,14 +39,12 @@ 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()
@impl true
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()}
@impl true
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}
@impl true
def count(%Polygon{vertices: points}), do: {:ok, Enum.count(points)}
@doc """
@ -28,7 +28,7 @@ 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}
@impl true
def member?(%Polygon{vertices: points}, %Point{} = point),
do: {:ok, Enum.member?(points, point)}
@ -40,14 +40,12 @@ 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()
@impl true
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()}
@impl true
def slice(%Polygon{vertices: points}), do: Enumerable.List.slice(points)
end

View file

@ -3,7 +3,7 @@ defimpl Inspect, for: Vivid.Arc do
import Inspect.Algebra
@doc false
@spec inspect(Arc.t(), any) :: String.t()
@impl true
def inspect(arc, opts) do
center = arc |> Arc.center()
radius = arc |> Arc.radius()
@ -19,6 +19,6 @@ defimpl Inspect, for: Vivid.Arc do
steps: steps
]
concat(["#Vivid.Arc<", to_doc(details, opts), ">"])
concat(["Vivid.Arc.new(", 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()
@impl true
def inspect(%Bounds{min: min, max: max}, opts) do
concat(["#Vivid.Bounds<", to_doc([min: min, max: max], opts), ">"])
concat(["Vivid.Bounds.new(", 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()
@impl true
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.new(", 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.new(", 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()
@impl true
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.new(", 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()
@impl true
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.new(", 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.new(", to_doc(details, opts), ")"])
end
end

View file

@ -5,16 +5,16 @@ defimpl Inspect, for: Vivid.Frame do
@doc """
Defines the inspect protocol for `Frame`.
"""
@spec inspect(Frame.t(), any) :: String.t()
@impl true
def inspect(frame, opts) do
width = Frame.width(frame)
height = Frame.height(frame)
colour = Frame.background_colour(frame)
concat([
"#Vivid.Frame<",
"Vivid.Frame.new(",
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()
@impl true
def inspect(%Group{shapes: shapes}, opts) do
shapes = shapes |> Enum.to_list()
concat(["#Vivid.Group<", to_doc(shapes, opts), ">"])
concat(["Vivid.Group.new(", to_doc(shapes, opts), ")"])
end
end

View file

@ -5,13 +5,13 @@ defimpl Inspect, for: Vivid.Line do
@doc """
Defines the inspect protocol for `Line`.
"""
@spec inspect(Line.t(), any) :: String.t()
@impl true
def inspect(line, opts) do
details = [
origin: Line.origin(line),
termination: Line.termination(line)
]
concat(["#Vivid.Line<", to_doc(details, opts), ">"])
concat(["Vivid.Line.new(", 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()
@impl true
def inspect(%Path{vertices: points}, opts) do
concat(["#Vivid.Path<", to_doc(points, opts), ">"])
concat(["Vivid.Path.new(", 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()
@impl true
def inspect(point, opts) do
x = point |> Point.x()
y = point |> Point.y()
concat(["#Vivid.Point<", to_doc({x, y}, opts), ">"])
concat(["Vivid.Point.new(", 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()
@impl true
def inspect(%Polygon{vertices: points, fill: true}, opts) do
concat(["#Vivid.Polygon<", to_doc([:filled | points], opts), ">"])
concat(["Vivid.Polygon.new(", to_doc([:filled | points], opts), ")"])
end
def inspect(%Polygon{vertices: points}, opts) do
concat(["#Vivid.Polygon<", to_doc(points, opts), ">"])
concat(["Vivid.Polygon.new(", 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()
@impl true
def inspect(colour, opts) do
red = RGBA.red(colour)
green = RGBA.green(colour)
blue = RGBA.blue(colour)
alpha = RGBA.alpha(colour)
concat(["#Vivid.RGBA<", to_doc({red, green, blue, alpha}, opts), ">"])
concat(["Vivid.RGBA.new(", to_doc({red, green, blue, alpha}, opts), ")"])
end
end

View file

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

View file

@ -4,19 +4,17 @@ 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()
@impl true
def to_string(%Buffer{buffer: buffer, columns: columns} = _buffer) do
s =
buffer
|> Enum.reverse()
|> Enum.chunk(columns)
|> Enum.map(fn row ->
|> Enum.chunk_every(columns)
|> Enum.map_join("\n", fn row ->
row
|> Enum.reverse()
|> Enum.map(&RGBA.to_ascii(&1))
|> Enum.join()
|> Enum.map_join(&RGBA.to_ascii(&1))
end)
|> Enum.join("\n")
s <> "\n"
end

View file

@ -4,7 +4,7 @@ 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()
@impl true
def to_string(%Frame{} = frame) do
frame
|> Frame.buffer()

View file

@ -1,5 +1,5 @@
defmodule Vivid.ShapeToString do
alias Vivid.{Bounds, Frame, Transform, RGBA, Shape}
alias Vivid.{Bounds, Frame, RGBA, Shape, Transform}
@moduledoc """
Handle conversions of arbitrary shapes into strings.
@ -30,6 +30,8 @@ Enum.each(~w(Arc Box Circle Group Line Path Polygon), fn type ->
mod = Module.concat(Vivid, type)
defimpl String.Chars, for: mod do
alias Vivid.ShapeToString
@moduledoc """
Convert a shape into a string.
"""
@ -37,7 +39,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()
def to_string(shape), do: Vivid.ShapeToString.to_string(shape)
@impl true
def to_string(shape), do: ShapeToString.to_string(shape)
end
end)

View file

@ -4,13 +4,14 @@ defmodule Vivid do
alias Vivid.{
Arc,
Bounds,
Box,
Buffer,
Circle,
Font,
Frame,
Group,
Font,
Frame,
Frame,
Group,
Group,
Line,
Path,
@ -18,8 +19,7 @@ defmodule Vivid do
Polygon,
Rasterize,
RGBA,
Transform,
Box
Transform
}
end
end

View file

@ -1,5 +1,5 @@
defmodule Vivid.Arc do
alias Vivid.{Arc, Point, Path}
alias Vivid.{Arc, Path, Point}
import Vivid.Math
defstruct ~w(center radius start_angle range steps)a
@ -24,7 +24,7 @@ defmodule Vivid.Arc do
"""
@opaque t :: %Arc{center: Point.t(), radius: number, start_angle: number, steps: integer}
@type t :: %Arc{center: Point.t(), radius: number, start_angle: number, steps: integer}
@doc ~S"""
Creates an Arc.
@ -65,7 +65,7 @@ defmodule Vivid.Arc do
iex> Vivid.Arc.init(Vivid.Point.init(10,10), 5, 0, 90, 12)
...> |> Vivid.Arc.center
#Vivid.Point<{10, 10}>
Vivid.Point.init(10, 10)
"""
@spec center(Arc.t()) :: Point.t()
def center(%Arc{center: p} = _arc), do: p
@ -78,7 +78,7 @@ defmodule Vivid.Arc do
iex> Vivid.Arc.init(Vivid.Point.init(10,10), 5, 0, 90, 12)
...> |> Vivid.Arc.center(Vivid.Point.init(15,15))
...> |> Vivid.Arc.center
#Vivid.Point<{15, 15}>
Vivid.Point.init(15, 15)
"""
@spec center(Arc.t(), Point.t()) :: Arc.t()
def center(%Arc{} = arc, %Point{} = point), do: %{arc | center: point}
@ -191,7 +191,7 @@ defmodule Vivid.Arc do
iex> Vivid.Arc.init(Vivid.Point.init(10,10), 5, 0, 90, 3)
...> |> Vivid.Arc.to_path
#Vivid.Path<[#Vivid.Point<{5, 10}>, #Vivid.Point<{6, 13}>, #Vivid.Point<{8, 14}>, #Vivid.Point<{10, 15}>]>
Vivid.Path.init([Vivid.Point.init(5, 10), Vivid.Point.init(6, 13), Vivid.Point.init(8, 14), Vivid.Point.init(10, 15)])
"""
@spec to_path(Arc.t()) :: Path.t()
def to_path(

View file

@ -1,5 +1,5 @@
defmodule Vivid.Bounds do
alias Vivid.{Bounds, Point, Shape, Bounds.Of}
alias Vivid.{Bounds, Bounds.Of, Point, Shape}
defstruct ~w(min max)a
@moduledoc """
@ -10,10 +10,10 @@ defmodule Vivid.Bounds do
iex> use Vivid
...> Box.init(Point.init(5,10),Point.init(15,20))
...> |> Bounds.bounds()
#Vivid.Bounds<[min: #Vivid.Point<{5, 10}>, max: #Vivid.Point<{15, 20}>]>
%Vivid.Bounds{min: Vivid.Point.init(5, 10), max: Vivid.Point.init(15, 20)}
"""
@opaque t :: %Bounds{min: Point.t(), max: Point.t()}
@type t :: %Bounds{min: Point.t(), max: Point.t()}
@doc """
Initialise arbitrary bounds.
@ -26,7 +26,7 @@ defmodule Vivid.Bounds do
## Example
iex> Vivid.Bounds.init(0, 0, 5, 5)
#Vivid.Bounds<[min: #Vivid.Point<{0, 0}>, max: #Vivid.Point<{5, 5}>]>
%Vivid.Bounds{min: Vivid.Point.init(0, 0), max: Vivid.Point.init(5, 5)}
"""
@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)}
@ -40,7 +40,7 @@ defmodule Vivid.Bounds do
iex> Vivid.Circle.init(Vivid.Point.init(10,10), 10)
...> |> Vivid.Bounds.bounds
#Vivid.Bounds<[min: #Vivid.Point<{0.0, 0.0}>, max: #Vivid.Point<{20.0, 20.0}>]>
%Vivid.Bounds{min: Vivid.Point.init(0.0, 0.0), max: Vivid.Point.init(20.0, 20.0)}
"""
@spec bounds(Shape.t()) :: Bounds.t()
def bounds(%Bounds{} = shape), do: shape
@ -89,7 +89,7 @@ defmodule Vivid.Bounds do
iex> Vivid.Circle.init(Vivid.Point.init(10,10), 10)
...> |> Vivid.Bounds.min
#Vivid.Point<{0.0, 0.0}>
Vivid.Point.init(0.0, 0.0)
"""
@spec min(Shape.t()) :: Point.t()
def min(%Bounds{min: min} = _shape), do: min
@ -104,7 +104,7 @@ defmodule Vivid.Bounds do
iex> Vivid.Circle.init(Vivid.Point.init(10,10), 10)
...> |> Vivid.Bounds.max
#Vivid.Point<{20.0, 20.0}>
Vivid.Point.init(20.0, 20.0)
"""
@spec max(Shape.t()) :: Point.t()
def max(%Bounds{max: max}), do: max
@ -120,7 +120,7 @@ defmodule Vivid.Bounds do
iex> Vivid.Circle.init(Vivid.Point.init(10,10), 10)
...> |> Vivid.Circle.to_polygon
...> |> Vivid.Bounds.center_of
#Vivid.Point<{10.0, 10.0}>
Vivid.Point.init(10.0, 10.0)
"""
@spec center_of(Shape.t()) :: Point.t()
def center_of(%Bounds{min: %Point{x: x0, y: y0}, max: %Point{x: x1, y: y1}}) do

View file

@ -1,12 +1,12 @@
defimpl Vivid.Bounds.Of, for: Vivid.Arc do
alias Vivid.{Arc, Bounds, Point}
alias Vivid.{Arc, Bounds}
@doc """
Find the bounds of a `arc`.
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Arc.t()) :: {Point.t(), Point.t()}
@impl true
def bounds(arc) do
arc
|> Arc.to_path()

View file

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

View file

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

View file

@ -1,12 +1,12 @@
defimpl Vivid.Bounds.Of, for: Vivid.Circle do
alias Vivid.{Circle, Bounds, Point}
alias Vivid.{Circle, Bounds}
@doc """
Find the bounds of a `circle`.
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Circle.t()) :: {Point.t(), Point.t()}
@impl true
def bounds(circle) do
circle
|> Circle.to_polygon()

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()}
@impl true
def bounds(%Frame{width: w, height: h} = _frame) do
{Point.init(0, 0), Point.init(w - 1, h - 1)}
end

View file

@ -1,15 +1,15 @@
defimpl Vivid.Bounds.Of, for: Vivid.Group do
alias Vivid.Point
alias Vivid.{Bounds, Group, Point}
@doc """
Find the bounds of a `group`.
Returns a two-element tuple of the bottom-left and top-right points.
"""
@spec bounds(Group.t()) :: {Point.t(), Point.t()}
def bounds(%Vivid.Group{shapes: shapes} = _group) do
@impl true
def bounds(%Group{shapes: shapes} = _group) do
shapes
|> Enum.map(&Vivid.Bounds.Of.bounds(&1))
|> Enum.map(&Bounds.Of.bounds(&1))
|> Enum.reduce(fn
{min, max}, nil ->
{min, max}

View file

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

View file

@ -6,7 +6,7 @@ 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()}
@impl true
def bounds(points) do
Enum.reduce(points, nil, fn
point, nil ->

View file

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

View file

@ -1,12 +1,10 @@
defimpl Vivid.Bounds.Of, for: Vivid.Point do
alias Vivid.Point
@doc """
Find the bounds of a `point`.
Since the bounds of a point are simply the point, it returns the point
twice.
"""
@spec bounds(Point.t()) :: {Point.t(), Point.t()}
@impl true
def bounds(point), do: {point, point}
end

View file

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

View file

@ -1,6 +1,6 @@
defmodule Vivid.Box do
alias Vivid.{Box, Point, Polygon, Bounds, Shape}
defstruct ~w(bottom_left top_right fill)a
alias Vivid.{Bounds, Box, Point, Polygon, Shape}
defstruct bottom_left: nil, top_right: nil, fill: false
@moduledoc ~S"""
Short-hand for creating rectangle polygons.
@ -28,7 +28,7 @@ defmodule Vivid.Box do
"@@@@@@@@@@@@@\n"
"""
@opaque t :: Box.t()
@type t :: Box.t()
@doc """
Initialize an unfilled Box from it's bottom left and top right points.
@ -40,7 +40,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Box.init(Point.init(1,1), Point.init(4,4))
#Vivid.Box<[bottom_left: #Vivid.Point<{1, 1}>, top_right: #Vivid.Point<{4, 4}>]>
%Box{bottom_left: Point.init(1, 1), top_right: Point.init(4, 4)}
"""
@spec init(Point.t(), Point.t()) :: Box.t()
def init(%Point{} = bottom_left, %Point{} = top_right), do: init(bottom_left, top_right, false)
@ -56,7 +56,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Box.init(Point.init(1,1), Point.init(4,4))
#Vivid.Box<[bottom_left: #Vivid.Point<{1, 1}>, top_right: #Vivid.Point<{4, 4}>]>
%Box{bottom_left: Point.init(1, 1), top_right: Point.init(4, 4)}
"""
@spec init(Point.t(), Point.t(), boolean) :: Box.t()
def init(%Point{} = bottom_left, %Point{} = top_right, fill) when is_boolean(fill),
@ -70,7 +70,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Circle.init(Point.init(5,5), 5)
...> |> Box.init_from_bounds
#Vivid.Box<[bottom_left: #Vivid.Point<{0.0, 0.2447174185242318}>, top_right: #Vivid.Point<{10.0, 9.755282581475768}>]>
Box.init(Point.init(0.0, 0.2447174185242318), Point.init(10.0, 9.755282581475768))
"""
@spec init_from_bounds(Shape.t(), boolean) :: Box.t()
def init_from_bounds(shape, fill \\ false) do
@ -88,7 +88,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Box.init(Point.init(1,1), Point.init(4,4))
...> |> Box.bottom_left
#Vivid.Point<{1, 1}>
Point.init(1, 1)
"""
@spec bottom_left(Box.t()) :: Point.t()
def bottom_left(%Box{bottom_left: bl}), do: bl
@ -101,7 +101,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Box.init(Point.init(1,1), Point.init(4,4))
...> |> Box.top_left
#Vivid.Point<{1, 4}>
Point.init(1, 4)
"""
@spec top_left(Box.t()) :: Point.t()
def top_left(%Box{bottom_left: bl, top_right: tr}), do: Point.init(bl.x, tr.y)
@ -114,7 +114,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Box.init(Point.init(1,1), Point.init(4,4))
...> |> Box.top_right
#Vivid.Point<{4, 4}>
Point.init(4, 4)
"""
@spec top_right(Box.t()) :: Point.t()
def top_right(%Box{top_right: tr}), do: tr
@ -127,7 +127,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Box.init(Point.init(1,1), Point.init(4,4))
...> |> Box.bottom_right
#Vivid.Point<{4, 1}>
Point.init(4, 1)
"""
@spec bottom_right(Box.t()) :: Point.t()
def bottom_right(%Box{bottom_left: bl, top_right: tr}), do: Point.init(tr.x, bl.y)
@ -140,7 +140,7 @@ defmodule Vivid.Box do
iex> use Vivid
...> Box.init(Point.init(1,1), Point.init(4,4))
...> |> Box.to_polygon
#Vivid.Polygon<[#Vivid.Point<{1, 1}>, #Vivid.Point<{1, 4}>, #Vivid.Point<{4, 4}>, #Vivid.Point<{4, 1}>]>
Polygon.init([Point.init(1, 1), Point.init(1, 4), Point.init(4, 4), Point.init(4, 1)])
"""
@spec to_polygon(Box.t()) :: Polygon.t()
def to_polygon(%Box{fill: fill} = box) do

View file

@ -1,5 +1,5 @@
defmodule Vivid.Buffer do
alias Vivid.{Buffer, Frame, Rasterize, Bounds, Point, RGBA}
alias Vivid.{Bounds, Buffer, Frame, Point, Rasterize, RGBA}
defstruct ~w(buffer rows columns)a
@moduledoc ~S"""
@ -17,7 +17,7 @@ defmodule Vivid.Buffer do
...> Frame.init(20, 10, RGBA.white())
...> |> Frame.push(box, RGBA.black())
...> |> Buffer.horizontal()
...> |> Stream.chunk(20)
...> |> Stream.chunk_every(20)
...> |> Stream.map(fn line ->
...> Stream.map(line, fn colour -> RGBA.to_ascii(colour) end)
...> |> Enum.join()
@ -35,7 +35,7 @@ defmodule Vivid.Buffer do
"@@@@@@@@@@@@@@@@@@@@"
"""
@opaque t :: %Buffer{buffer: [RGBA.t()], rows: integer, columns: integer}
@type t :: %Buffer{buffer: [RGBA.t()], rows: integer, columns: integer}
@doc ~S"""
Render the buffer horizontally, ie across rows then up columns.
@ -53,7 +53,7 @@ defmodule Vivid.Buffer do
"@@@@@\n" <>
"@@@@@\n"
"""
@spec horizontal(Frame.t()) :: [RGBA.t()]
@spec horizontal(Frame.t()) :: Buffer.t()
def horizontal(%Frame{shapes: shapes, width: w, height: h} = frame) do
empty_buffer = allocate(frame)
bounds = Bounds.bounds(frame)
@ -77,7 +77,7 @@ defmodule Vivid.Buffer do
"@@ @@\n" <>
"@@ @@\n"
"""
@spec vertical(Frame.t()) :: [RGBA.t()]
@spec vertical(Frame.t()) :: Buffer.t()
def vertical(%Frame{shapes: shapes, width: w, height: h} = frame) do
bounds = Bounds.bounds(frame)
empty_buffer = allocate(frame)

View file

@ -1,6 +1,6 @@
defmodule Vivid.Circle do
alias Vivid.{Circle, Point, Polygon}
defstruct ~w(center radius fill)a
defstruct center: nil, radius: nil, fill: false
import Vivid.Math
@moduledoc ~S"""
@ -35,7 +35,7 @@ defmodule Vivid.Circle do
"@@@@@@@@ @@@@@@@@\n" <>
"@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@opaque t :: %Circle{center: Point.t(), radius: number, fill: boolean}
@type t :: %Circle{center: Point.t(), radius: number, fill: boolean}
@doc """
Creates a circle from a point in 2D space and a radius.
@ -43,7 +43,7 @@ defmodule Vivid.Circle do
## Example
iex> Vivid.Circle.init(Vivid.Point.init(5,5), 4)
#Vivid.Circle<[center: #Vivid.Point<{5, 5}>, radius: 4]>
%Vivid.Circle{center: Vivid.Point.init(5, 5), radius: 4}
"""
@spec init(Point.t(), number) :: Circle.t()
def init(%Point{} = point, radius)
@ -70,7 +70,7 @@ defmodule Vivid.Circle do
...> |> Vivid.Circle.radius
4
"""
@spec radius(Cricle.t()) :: number
@spec radius(Circle.t()) :: number
def radius(%Circle{radius: r}), do: r
@doc """

View file

@ -1,5 +1,5 @@
defmodule Vivid.Font do
alias Vivid.{Hershey, Point, Group, Shape, Font.Char}
alias Vivid.{Font.Char, Group, Hershey, Point, Shape}
@font_vertical_offset 10

View file

@ -1,13 +1,13 @@
defmodule Vivid.Font.Char do
alias __MODULE__
alias Vivid.{Group, Path, Point}
alias Vivid.{Group, Path, Point, Shape}
defstruct ~w(character vertices left_pos right_pos coordinates)a
@moduledoc """
Describes an individual character defined by a Hershey font file.
"""
@opaque t :: %Char{}
@type t :: %Char{}
@doc """
Returns the (documented) width of a specific character.

View file

@ -1,5 +1,5 @@
defmodule Vivid.Frame do
alias Vivid.{Frame, RGBA, Buffer, Shape}
alias Vivid.{Buffer, Frame, RGBA, Shape}
defstruct ~w(width height background_colour shapes)a
@moduledoc ~S"""
@ -39,7 +39,12 @@ defmodule Vivid.Frame do
"@@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@opaque t :: %Frame{width: integer, height: integer, background_colour: RGBA.t(), shapes: []}
@type t :: %Frame{
width: pos_integer(),
height: pos_integer(),
background_colour: RGBA.t(),
shapes: [{Shape.t(), RGBA.t()}]
}
@doc """
Initialize a frame buffer.
@ -51,9 +56,9 @@ defmodule Vivid.Frame do
## Example
iex> Vivid.Frame.init(4, 4)
#Vivid.Frame<[width: 4, height: 4, background_colour: #Vivid.RGBA<{0, 0, 0, 0}>]>
Vivid.Frame.init(4, 4, Vivid.RGBA.init(0, 0, 0, 0))
"""
@spec init(integer(), integer(), Range.t()) :: Frame.t()
@spec init(pos_integer(), pos_integer(), RGBA.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
%Frame{width: width, height: height, background_colour: colour, shapes: []}
@ -175,7 +180,7 @@ defmodule Vivid.Frame do
## Example
iex> Vivid.Frame.init(80, 25) |> Vivid.Frame.background_colour
#Vivid.RGBA<{0, 0, 0, 0}>
Vivid.RGBA.init(0, 0, 0, 0)
"""
@spec background_colour(Frame.t()) :: RGBA.t()
def background_colour(%Frame{background_colour: c}), do: c
@ -188,7 +193,7 @@ defmodule Vivid.Frame do
iex> Vivid.Frame.init(80,25)
...> |> Vivid.Frame.background_colour(Vivid.RGBA.white)
...> |> Vivid.Frame.background_colour
#Vivid.RGBA<{1, 1, 1, 1}>
Vivid.RGBA.init(1, 1, 1, 1)
"""
@spec background_colour(Frame.t(), RGBA.t()) :: Frame.t()
def background_colour(%Frame{} = frame, %RGBA{} = colour),
@ -200,7 +205,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()) :: Buffer.t()
def buffer(%Frame{} = frame), do: Buffer.horizontal(frame)
@doc """
@ -214,7 +219,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) :: Buffer.t()
def buffer(%Frame{} = frame, :horizontal), do: Buffer.horizontal(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()]}
@type t :: %Group{shapes: MapSet.t(Shape.t())}
@doc """
Initialize an empty group.
@ -47,7 +47,7 @@ defmodule Vivid.Group do
## Examples
iex> Vivid.Group.init
#Vivid.Group<[]>
%Vivid.Group{shapes: MapSet.new([])}
"""
@spec init() :: Group.t()
def init, do: %Group{shapes: MapSet.new()}
@ -60,11 +60,11 @@ defmodule Vivid.Group do
iex> circle = Vivid.Circle.init(Vivid.Point.init(5,5), 5)
...> line = Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(10,10))
...> 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]>]>
Vivid.Group.init([Vivid.Line.init(Vivid.Point.init(1, 1), Vivid.Point.init(10, 10)), Vivid.Circle.init(Vivid.Point.init(5, 5), 5)])
"""
@spec init([Shape.t()]) :: Group.t()
@spec init(Enumerable.t(Shape.t())) :: Group.t()
def init(shapes) do
%Group{shapes: Enum.into(shapes, MapSet.new())}
%Group{shapes: MapSet.new(shapes)}
end
@doc """
@ -78,7 +78,7 @@ defmodule Vivid.Group do
%Vivid.Group{shapes: MapSet.new()}
"""
@spec delete(Group.t(), Shape.t()) :: Group.t()
def delete(%Group{shapes: shapes}, shape), do: shapes |> MapSet.delete(shape) |> init
def delete(%Group{shapes: shapes}, shape), do: shapes |> MapSet.delete(shape) |> init()
@doc """
Add a shape to a Group
@ -93,5 +93,5 @@ defmodule Vivid.Group do
])}
"""
@spec put(Group.t(), Shape.t()) :: Group.t()
def put(%Group{shapes: shapes}, shape), do: shapes |> MapSet.put(shape) |> init
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 ~c"R" |> List.first()
@moduledoc """
Supports reading the Hershey Vector Font format and converting them into paths.

View file

@ -21,7 +21,7 @@ defmodule Vivid.Line do
"@@@@@@@@\n"
"""
@opaque t :: %Line{origin: Point.t(), termination: Point.t()}
@type t :: %Line{origin: Point.t(), termination: Point.t()}
@doc ~S"""
Create a Line given an `origin` and `termination` point.
@ -64,7 +64,7 @@ defmodule Vivid.Line do
iex> use Vivid
...> Line.init(Point.init(1,1), Point.init(4,4))
...> |> Line.termination
#Vivid.Point<{4, 4}>
Vivid.Point.init(4, 4)
"""
@spec termination(Line.t()) :: Point.t()
def termination(%Line{termination: t}), do: t
@ -163,7 +163,7 @@ defmodule Vivid.Line do
iex> use Vivid
...> Line.init(Point.init(25, 15), Point.init(5, 2))
...> |> Line.x_intersect(10)
#Vivid.Point<{10, 5.25}>
Vivid.Point.init(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)
@ -195,7 +195,7 @@ defmodule Vivid.Line do
iex> use Vivid
...> Line.init(Point.init(25, 15), Point.init(5, 2))
...> |> Line.y_intersect(10)
#Vivid.Point<{17.307692307692307, 10}>
Vivid.Point.init(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)

View file

@ -1,5 +1,5 @@
defmodule Vivid.Path do
alias Vivid.{Path, Point, Line, Shape}
alias Vivid.{Line, Path, Point, Shape}
defstruct vertices: []
@moduledoc ~S"""
@ -30,7 +30,7 @@ defmodule Vivid.Path do
"@@@@@@@@@@@@\n"
"""
@opaque t :: %Path{vertices: [Shape.t()]}
@type t :: %Path{vertices: [Shape.t()]}
@doc """
Initialize an empty path.

View file

@ -19,7 +19,7 @@ defmodule Vivid.Point do
"@@@@@\n"
"""
@opaque t :: %Point{x: number, y: number}
@type t :: %Point{x: number, y: number}
@doc ~S"""
Creates a Point using `x` and `y` coordinates.
@ -65,7 +65,7 @@ defmodule Vivid.Point do
iex> Vivid.Point.init(13, 27)
...> |> Vivid.Point.swap_xy
#Vivid.Point<{27, 13}>
Vivid.Point.init(27, 13)
"""
@spec swap_xy(Point.t()) :: Point.t()
def swap_xy(%Point{x: x, y: y}), do: Point.init(y, x)
@ -93,7 +93,7 @@ defmodule Vivid.Point do
iex> Vivid.Point.init(1.23, 4.56)
...> |> Vivid.Point.round
#Vivid.Point<{1, 5}>
Vivid.Point.init(1, 5)
"""
@spec round(Point.t()) :: Point.t()
def round(%Point{x: x, y: y}), do: Point.init(Kernel.round(x), Kernel.round(y))

View file

@ -1,5 +1,5 @@
defmodule Vivid.Polygon do
alias Vivid.{Polygon, Point, Line}
alias Vivid.{Line, Point, Polygon}
defstruct vertices: [], fill: false
require Integer
@ -31,7 +31,7 @@ defmodule Vivid.Polygon do
"@@@@@@@@@@@@\n"
"""
@opaque t :: %Polygon{vertices: [Point.t()], fill: boolean}
@type t :: %Polygon{vertices: [Point.t()], fill: boolean}
@doc """
Initialize an empty Polygon.

View file

@ -1,5 +1,5 @@
defprotocol Vivid.Rasterize do
alias Vivid.Shape
alias Vivid.{Bounds, Shape}
@moduledoc ~S"""
The Rasterize protocol is responsible for converting shapes into bitmaps.
@ -19,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.t()
def rasterize(shape, bounds)
end

View file

@ -1,5 +1,5 @@
defimpl Vivid.Rasterize, for: Vivid.Arc do
alias Vivid.{Rasterize, Arc, Bounds}
alias Vivid.{Rasterize, Arc}
@moduledoc """
Rasterizes an Arc.
@ -12,10 +12,10 @@ defimpl Vivid.Rasterize, for: Vivid.Arc do
iex> Vivid.Arc.init(Vivid.Point.init(5,5), 5, 270, 90, 3)
...> |> Vivid.Rasterize.rasterize(Vivid.Bounds.init(0, 0, 5, 5))
#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}>]>
MapSet.new([Vivid.Point.init(0, 5), Vivid.Point.init(1, 3), Vivid.Point.init(1, 4), Vivid.Point.init(2, 2), Vivid.Point.init(3, 1), Vivid.Point.init(4, 1), Vivid.Point.init(5, 0)])
"""
@spec rasterize(Arc.t(), Bounds.t()) :: MapSet.t()
@impl true
def rasterize(arc, bounds) do
arc
|> Arc.to_path()

View file

@ -1,5 +1,5 @@
defimpl Vivid.Rasterize, for: Vivid.Box do
alias Vivid.{Box, Rasterize, Bounds}
alias Vivid.{Box, Rasterize}
@moduledoc """
Rasterizes a box into points.
@ -13,9 +13,9 @@ defimpl Vivid.Rasterize, for: Vivid.Box do
iex> use Vivid
...> box = Box.init(Point.init(2,2), Point.init(4,4))
...> 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}>]>
MapSet.new([Vivid.Point.init(2, 2), Vivid.Point.init(2, 3), Vivid.Point.init(2, 4), Vivid.Point.init(3, 2), Vivid.Point.init(3, 4), Vivid.Point.init(4, 2), Vivid.Point.init(4, 3), Vivid.Point.init(4, 4)])
"""
@spec rasterize(Box.t(), Bounds.t()) :: MapSet.t()
@impl true
def rasterize(box, bounds) do
box
|> Box.to_polygon()

View file

@ -27,7 +27,7 @@ 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()
@impl true
def rasterize(circle, bounds) do
circle
|> Circle.to_polygon()

View file

@ -1,5 +1,5 @@
defimpl Vivid.Rasterize, for: Vivid.Group do
alias Vivid.{Group, Rasterize, Bounds}
alias Vivid.{Group, Rasterize}
@moduledoc """
Rasterizes the Group into a sequence of points.
@ -12,9 +12,9 @@ defimpl Vivid.Rasterize, for: Vivid.Group do
iex> path = Vivid.Path.init([Vivid.Point.init(1,1), Vivid.Point.init(1,3), Vivid.Point.init(3,3), Vivid.Point.init(3,1)])
...> 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}>]>
MapSet.new([Vivid.Point.init(1, 1), Vivid.Point.init(1, 2), Vivid.Point.init(1, 3), Vivid.Point.init(2, 3), Vivid.Point.init(3, 1), Vivid.Point.init(3, 2), Vivid.Point.init(3, 3)])
"""
@spec rasterize(Group.t(), Bounds.t()) :: MapSet.t()
@impl true
def rasterize(%Group{shapes: shapes} = _group, bounds) do
Enum.reduce(shapes, MapSet.new(), fn shape, acc ->
MapSet.union(acc, Rasterize.rasterize(shape, bounds))

View file

@ -13,7 +13,7 @@ defimpl Vivid.Rasterize, for: Vivid.Line do
iex> Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(3,3))
...> |> Vivid.Rasterize.rasterize(Vivid.Bounds.init(0, 0, 3, 3))
#MapSet<[#Vivid.Point<{1, 1}>, #Vivid.Point<{2, 2}>, #Vivid.Point<{3, 3}>]>
MapSet.new([Vivid.Point.init(1, 1), Vivid.Point.init(2, 2), Vivid.Point.init(3, 3)])
iex> Vivid.Line.init(Vivid.Point.init(1,1), Vivid.Point.init(4,2))
...> |> Vivid.Rasterize.rasterize(Vivid.Bounds.init(0, 0, 4, 4))
@ -34,7 +34,7 @@ defimpl Vivid.Rasterize, for: Vivid.Line do
])
"""
@spec rasterize(Line.t(), Bounds.t()) :: MapSet.t()
@impl true
def rasterize(%Line{} = line, bounds) do
# Convert the line into absolute coordinates.
origin = line |> Line.origin() |> Point.round()

View file

@ -1,5 +1,5 @@
defimpl Vivid.Rasterize, for: Vivid.Path do
alias Vivid.{Path, Rasterize, Bounds}
alias Vivid.{Rasterize, Path}
@moduledoc """
Rasterizes the path into a sequence of points.
@ -22,7 +22,7 @@ defimpl Vivid.Rasterize, for: Vivid.Path do
%Vivid.Point{x: 3, y: 3}
])
"""
@spec rasterize(Path.t(), Bounds.t()) :: MapSet.t()
@impl true
def rasterize(%Path{} = path, bounds) do
lines = path |> Path.to_lines()

View file

@ -13,7 +13,7 @@ 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()
@impl true
def rasterize(point, bounds) do
point = point |> Point.round()

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()
@impl true
def rasterize(%Polygon{vertices: v}, _bounds) when length(v) < 3 do
raise InvalidPolygonError, "Polygon does not contain enough edges."
end
@ -52,10 +52,9 @@ defimpl Vivid.Rasterize, for: Vivid.Polygon do
end
defp polygon_border(polygon, bounds) do
lines = polygon |> Polygon.to_lines()
Enum.reduce(lines, MapSet.new(), fn line, acc ->
MapSet.union(acc, Rasterize.rasterize(line, bounds))
end)
polygon
|> Polygon.to_lines()
|> Enum.map(&Rasterize.rasterize(&1, bounds))
|> Enum.reduce(MapSet.new(), &MapSet.union(&1, &2))
end
end

View file

@ -15,23 +15,23 @@ defmodule Vivid.RGBA do
iex> use Vivid
...> RGBA.black()
#Vivid.RGBA<{0, 0, 0, 1}>
Vivid.RGBA.init(0, 0, 0, 1)
iex> RGBA.white()
#Vivid.RGBA<{1, 1, 1, 1}>
Vivid.RGBA.init(1, 1, 1, 1)
iex> RGBA.init(1, 0, 0, 0.5)
#Vivid.RGBA<{1, 0, 0, 0.5}>
Vivid.RGBA.init(1, 0, 0, 0.5)
"""
@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
}
@type 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 +54,7 @@ defmodule Vivid.RGBA do
## Example
iex> Vivid.RGBA.init(0.1, 0.2, 0.3, 0.4)
#Vivid.RGBA<{0.1, 0.2, 0.3, 0.4}>
Vivid.RGBA.init(0.1, 0.2, 0.3, 0.4)
"""
@spec init(zero_to_one, zero_to_one, zero_to_one) :: RGBA.t()
def init(red, green, blue), do: init(red, green, blue, 1)
@ -72,7 +72,7 @@ defmodule Vivid.RGBA do
## Example
iex> Vivid.RGBA.init(0.1, 0.2, 0.3, 0.4)
#Vivid.RGBA<{0.1, 0.2, 0.3, 0.4}>
Vivid.RGBA.init(0.1, 0.2, 0.3, 0.4)
"""
@spec init(zero_to_one, zero_to_one, zero_to_one, zero_to_one) :: RGBA.t()
def init(red, green, blue, 1)
@ -121,7 +121,7 @@ defmodule Vivid.RGBA do
## Example
iex> Vivid.RGBA.white
#Vivid.RGBA<{1, 1, 1, 1}>
Vivid.RGBA.init(1, 1, 1, 1)
"""
@spec white() :: RGBA.t()
def white, do: RGBA.init(1, 1, 1)
@ -132,7 +132,7 @@ defmodule Vivid.RGBA do
## Example
iex> Vivid.RGBA.black
#Vivid.RGBA<{0, 0, 0, 1}>
Vivid.RGBA.init(0, 0, 0, 1)
"""
@spec black() :: RGBA.t()
def black, do: RGBA.init(0, 0, 0)
@ -216,7 +216,7 @@ defmodule Vivid.RGBA do
## Examples
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}>
Vivid.RGBA.init(0.5, 0.5, 0.5, 1.0)
"""
@spec over(RGBA.t(), RGBA.t()) :: RGBA.t()
def over(nil, %RGBA{} = colour), do: colour

View file

@ -1,5 +1,5 @@
defmodule Vivid.SLPFA do
alias Vivid.{Polygon, Point, Line}
alias Vivid.{Line, Point, Polygon}
@moduledoc """
Scanline Polygon Filling Algorithm, as per
@ -13,6 +13,15 @@ defmodule Vivid.SLPFA do
defmodule EdgeBucket do
defstruct ~w(y_min y_max x sign distance_x distance_y sum)a
@moduledoc false
@type t :: %EdgeBucket{
y_min: non_neg_integer(),
y_max: non_neg_integer(),
sign: -1 | 1,
distance_x: non_neg_integer(),
distance_y: non_neg_integer(),
sum: non_neg_integer()
}
end
@doc ~S"""
@ -142,7 +151,7 @@ defmodule Vivid.SLPFA do
defp pixels_for_active_list(points, active, y) do
active
|> Stream.chunk(2)
|> Stream.chunk_every(2)
|> Enum.reduce(points, fn [a0, a1], points ->
Enum.reduce((a0.x + 1)..(a1.x - 1), points, fn x, points ->
MapSet.put(points, Point.init(x, y))

View file

@ -1,5 +1,5 @@
defmodule Vivid.Transform do
alias Vivid.{Point, Transform, Bounds, Shape}
alias Vivid.{Bounds, Point, Shape, Transform}
alias Vivid.Transformable
import Vivid.Math
defstruct operations: [], shape: nil
@ -8,9 +8,11 @@ defmodule Vivid.Transform do
alias __MODULE__
defstruct ~w(function name)a
@moduledoc false
@moduledoc """
An operation that can be applied to a shape.
"""
@opaque t :: %Operation{function: function, name: String.t()}
@type t :: %Operation{function: function, name: String.t()}
end
@moduledoc ~S"""
@ -72,7 +74,7 @@ defmodule Vivid.Transform do
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
"""
@opaque t :: %Transform{shape: Shape.t(), operations: [Operation.t()]}
@type t :: %Transform{shape: Shape.t(), operations: [Operation.t()]}
@type shape_or_transform :: Transform.t() | Shape.t()
@type degrees :: number
@ -84,7 +86,7 @@ defmodule Vivid.Transform do
iex> Vivid.Box.init(Vivid.Point.init(5,5), Vivid.Point.init(10,10))
...> |> Vivid.Transform.translate(5, 5)
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{15, 10}>, #Vivid.Point<{15, 15}>, #Vivid.Point<{10, 15}>, #Vivid.Point<{10, 10}>]>
Vivid.Polygon.init([Vivid.Point.init(15, 10), Vivid.Point.init(15, 15), Vivid.Point.init(10, 15), Vivid.Point.init(10, 10)])
"""
@spec translate(shape_or_transform, number, number) :: Transform.t()
def translate(shape, x, y) do
@ -103,7 +105,7 @@ defmodule Vivid.Transform do
iex> Vivid.Box.init(Vivid.Point.init(5,5), Vivid.Point.init(10,10))
...> |> Vivid.Transform.scale(2)
...> |> 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}>]>
Vivid.Polygon.init([Vivid.Point.init(12.5, 2.5), Vivid.Point.init(12.5, 12.5), Vivid.Point.init(2.5, 12.5), Vivid.Point.init(2.5, 2.5)])
"""
@spec scale(shape_or_transform, number) :: Transform.t()
def scale(shape, uniform) do
@ -123,7 +125,7 @@ defmodule Vivid.Transform do
iex> Vivid.Box.init(Vivid.Point.init(5,5), Vivid.Point.init(10,10))
...> |> Vivid.Transform.scale(2, 4)
...> |> Vivid.Transform.apply
#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}>]>
Vivid.Polygon.init([Vivid.Point.init(12.5, -2.5), Vivid.Point.init(12.5, 17.5), Vivid.Point.init(2.5, 17.5), Vivid.Point.init(2.5, -2.5)])
"""
@spec scale(shape_or_transform, number, number) :: Transform.t()
@ -264,7 +266,7 @@ defmodule Vivid.Transform do
iex> Vivid.Box.init(Vivid.Point.init(10,10), Vivid.Point.init(20,20))
...> |> Vivid.Transform.stretch(Vivid.Bounds.init(0, 0, 40, 80))
...> |> 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}>]>
Vivid.Polygon.init([Vivid.Point.init(40.0, 0.0), Vivid.Point.init(40.0, 80.0), Vivid.Point.init(0.0, 80.0), Vivid.Point.init(0.0, 0.0)])
"""
@spec stretch(shape_or_transform, Shape.t()) :: Transform.t()
def stretch(shape, bounds) do
@ -302,7 +304,7 @@ defmodule Vivid.Transform do
iex> Vivid.Box.init(Vivid.Point.init(10,10), Vivid.Point.init(20,20))
...> |> Vivid.Transform.fill(Vivid.Bounds.init(0, 0, 40, 80))
...> |> 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}>]>
Vivid.Polygon.init([Vivid.Point.init(40.0, 0.0), Vivid.Point.init(40.0, 40.0), Vivid.Point.init(0.0, 40.0), Vivid.Point.init(0.0, 0.0)])
"""
@spec fill(shape_or_transform, Shape.t()) :: Transform.t()
def fill(shape, bounds) do
@ -342,7 +344,7 @@ defmodule Vivid.Transform do
iex> Vivid.Box.init(Vivid.Point.init(10,10), Vivid.Point.init(20,20))
...> |> Vivid.Transform.overflow(Vivid.Bounds.init(0, 0, 40, 80))
...> |> 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}>]>
Vivid.Polygon.init([Vivid.Point.init(80.0, 0.0), Vivid.Point.init(80.0, 80.0), Vivid.Point.init(0.0, 80.0), Vivid.Point.init(0.0, 0.0)])
"""
@spec overflow(shape_or_transform, Shape.t()) :: Transform.t()
def overflow(shape, bounds) do
@ -396,7 +398,7 @@ defmodule Vivid.Transform do
...> end
...> end)
...> |> Vivid.Transform.apply
#Vivid.Polygon<[#Vivid.Point<{25, 10}>, #Vivid.Point<{25, 20}>, #Vivid.Point<{15, 20}>, #Vivid.Point<{15, 10}>]>
Vivid.Polygon.init([Vivid.Point.init(25, 10), Vivid.Point.init(25, 20), Vivid.Point.init(15, 20), Vivid.Point.init(15, 10)])
"""
@spec transform(shape_or_transform, function) :: Transform.t()
def transform(shape, fun), do: apply_transform(shape, fun, inspect(fun))

View file

@ -22,7 +22,7 @@ defmodule Vivid.Transform.Point do
@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()
@spec scale(Point.t(), number, number) :: Point.t()
def scale(%Point{} = point, x_factor, y_factor),
do: scale(point, x_factor, y_factor, Point.init(0, 0))

View file

@ -1,5 +1,5 @@
defimpl Vivid.Transformable, for: Vivid.Arc do
alias Vivid.{Arc, Transformable, Path, Point}
alias Vivid.{Arc, Transformable, Path}
@doc """
Apply an arbitrary transformation function to a arc.
@ -10,7 +10,7 @@ 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()
@impl true
def transform(arc, fun) do
arc
|> Arc.to_path()

View file

@ -1,5 +1,5 @@
defimpl Vivid.Transformable, for: Vivid.Box do
alias Vivid.{Box, Transformable, Point}
alias Vivid.{Box, Transformable}
@doc """
Apply an arbitrary transformation function to a box.
@ -7,7 +7,7 @@ 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()
@impl true
def transform(box, fun) do
box
|> Box.to_polygon()

View file

@ -10,7 +10,7 @@ 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()
@impl true
def transform(%Circle{fill: f} = circle, fun) do
circle
|> Circle.to_polygon()

View file

@ -7,7 +7,7 @@ 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()
@impl true
def transform(group, fun) do
group
|> Stream.map(&Transformable.transform(&1, fun))

View file

@ -1,5 +1,5 @@
defimpl Vivid.Transformable, for: Vivid.Line do
alias Vivid.{Line, Transformable, Point}
alias Vivid.{Line, Transformable}
@doc """
Apply an arbitrary transformation function to a line.
@ -7,7 +7,7 @@ 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()
@impl true
def transform(line, fun) do
origin = line |> Line.origin() |> Transformable.transform(fun)
term = line |> Line.termination() |> Transformable.transform(fun)

View file

@ -7,7 +7,7 @@ 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()
@impl true
def transform(path, fun) do
path
|> Stream.map(&Transformable.transform(&1, fun))

View file

@ -1,12 +1,10 @@
defimpl Vivid.Transformable, for: Vivid.Point do
alias Vivid.Point
@doc """
Apply an arbitrary transformation function to a point.
* `point` - the point to modify.
* `fun` - the transformation function to apply.
"""
@spec transform(Point.t(), (Point.t() -> Point.t())) :: Point.t()
@impl true
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()
@impl true
def transform(%Polygon{fill: fill} = polygon, fun) do
polygon
|> Stream.map(&Transformable.transform(&1, fun))

25
mix.exs
View file

@ -1,7 +1,8 @@
defmodule Vivid.Mixfile do
@moduledoc false
use Mix.Project
@version "0.4.3"
@version "0.4.4"
def project do
[
@ -17,7 +18,7 @@ defmodule Vivid.Mixfile do
source_ref: "v#{@version}",
main: "Vivid",
canonical: "http://hexdocs.pm/vivid",
source_url: "https://github.com/jamesotron/vivid.ex",
source_url: "https://code.harton.nz/james/vivid",
extras: ["guides/getting-started.md"]
]
]
@ -27,7 +28,7 @@ defmodule Vivid.Mixfile do
#
# Type "mix help compile.app" for more information
def application do
[applications: [:logger]]
[extra_applications: [:logger]]
end
def description do
@ -38,10 +39,10 @@ defmodule Vivid.Mixfile do
def package do
[
maintainers: ["James Harton <james@messagerocket.co>"],
maintainers: ["James Harton <james@harton.nz>"],
licenses: ["MIT"],
links: %{
"Source" => "https://github.com/jamesotron/vivid.ex"
"Source" => "https://code.harton.nz/james/vivid"
}
]
end
@ -56,11 +57,17 @@ defmodule Vivid.Mixfile do
#
# Type "mix help deps" for more examples and options
defp deps do
opts = [only: ~w[dev test]a, runtime: false]
[
{:ex_doc, ">= 0.0.0", only: :dev},
{:earmark, ">= 0.0.0", only: :dev},
{:credo, "~> 0.6", only: ~w(dev test)a},
{:inch_ex, "~> 0.5", only: :docs}
{:credo, "~> 1.7", opts},
{:dialyxir, "~> 1.3", opts},
{:doctor, "~> 0.21", opts},
{:earmark, ">= 0.0.0", opts},
{:ex_check, "~> 0.15", opts},
{:ex_doc, ">= 0.0.0", opts},
{:git_ops, "~> 2.6", opts},
{:mix_audit, "~> 2.1", opts}
]
end
end

View file

@ -1,9 +1,23 @@
%{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"credo": {:hex, :credo, "0.8.10", "261862bb7363247762e1063713bb85df2bbd84af8d8610d1272cd9c1943bba63", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"},
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [], [], "hexpm"},
"earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"},
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
"earmark": {:hex, :earmark, "1.4.39", "acdb2f02c536471029dbcc509fbd6b94b89f40ad7729fb3f68f4b6944843f01d", [:mix], [{:earmark_parser, "~> 1.4.33", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "156c9d8ec3cbeccdbf26216d8247bdeeacc8c76b4d9eee7554be2f1b623ea440"},
"earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_check": {:hex, :ex_check, "0.15.0", "074b94c02de11c37bba1ca82ae5cc4926e6ccee862e57a485b6ba60fca2d8dc1", [:mix], [], "hexpm", "33848031a0c7e4209c3b4369ce154019788b5219956220c35ca5474299fb6a0e"},
"ex_doc": {:hex, :ex_doc, "0.30.3", "bfca4d340e3b95f2eb26e72e4890da83e2b3a5c5b0e52607333bf5017284b063", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "fbc8702046c1d25edf79de376297e608ac78cdc3a29f075484773ad1718918b6"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
"git_ops": {:hex, :git_ops, "2.6.0", "e0791ee1cf5db03f2c61b7ebd70e2e95cba2bb9b9793011f26609f22c0900087", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "b98fca849b18aaf490f4ac7d1dd8c6c469b0cc3e6632562d366cab095e666ffe"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
"mix_audit": {:hex, :mix_audit, "2.1.1", "653aa6d8f291fc4b017aa82bdb79a4017903902ebba57960ef199cbbc8c008a1", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "541990c3ab3a7bb8c4aaa2ce2732a4ae160ad6237e5dcd5ad1564f4f85354db1"},
"nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"},
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
}