improvement: bring code up to date and add linters, etc.
This commit is contained in:
parent
01a0089384
commit
2993639ae2
33
.check.exs
Normal file
33
.check.exs
Normal 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"}
|
||||
]
|
||||
]
|
145
.credo.exs
145
.credo.exs
|
@ -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
504
.drone.yml
Normal 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
4
.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
2
.tool-versions
Normal file
2
.tool-versions
Normal file
|
@ -0,0 +1,2 @@
|
|||
erlang 26.0.2
|
||||
elixir 1.15.4
|
23
README.md
23
README.md
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 """
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
25
mix.exs
|
@ -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
|
||||
|
|
28
mix.lock
28
mix.lock
|
@ -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"},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue