Skip to content

Instantly share code, notes, and snippets.

@seivan
Created March 26, 2018 20:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seivan/83072389d8aca98f77cf7a991f940c31 to your computer and use it in GitHub Desktop.
Save seivan/83072389d8aca98f77cf7a991f940c31 to your computer and use it in GitHub Desktop.
Drawing
defmodule Drawille.Braille do
@moduledoc """
Module for a braille code.
"""
@braille_offset 0x2800
@doc """
You can get "braille byte" by pixel_map.
pixel map to n-th bits:
| |x=0|x=1|
|-- |-- |-- |
|y=0| 1 | 4 |
|y=1| 2 | 5 |
|y=2| 3 | 6 |
|y=3| 7 | 8 |
"""
def pixel_map(0, 0), do: 0x01
def pixel_map(1, 0), do: 0x08
def pixel_map(0, 1), do: 0x02
def pixel_map(1, 1), do: 0x10
def pixel_map(0, 2), do: 0x04
def pixel_map(1, 2), do: 0x20
def pixel_map(0, 3), do: 0x40
def pixel_map(1, 3), do: 0x80
@doc """
Get a printable UTF8 code.
"""
def to_utf8_code(0), do: @braille_offset
def to_utf8_code braille_byte do
braille_byte + @braille_offset
end
end
defmodule Drawille.Canvas do
@moduledoc """
Module for still drawing.
"""
defstruct chars: %{}, top_left: :undefined, down_right: :undefined
use Bitwise
alias Drawille.Canvas, as: Canvas
alias Drawille.Braille, as: Braille
@doc """
Make new empty canvas.
"""
def new, do: %Canvas{}
@doc """
Set a dot at (x, y) to a canvas.
"""
def set(%{chars: chars, top_left: top_left, down_right: down_right}, x, y)
when x > 0 and y > 0
do
{{chars_x, chars_y} = chars_xy, {dots_x, dots_y}} = normalize x, y
braille_byte_to_be_set = Braille.pixel_map(dots_x, dots_y)
chars = case Map.fetch chars, chars_xy do
{:ok, char0} -> Map.put(chars, chars_xy, char0 ||| braille_byte_to_be_set)
:error -> Map.put_new(chars, chars_xy, braille_byte_to_be_set)
end
top_left = case top_left do
:undefined -> chars_xy
{top_left_x, top_left_y} -> {min(top_left_x, chars_x), min(top_left_y, chars_y)}
end
down_right = case down_right do
:undefined -> chars_xy
{down_right_x, down_right_y} -> {max(down_right_x, chars_x), max(down_right_y, chars_y)}
end
%Canvas{
chars: chars,
top_left: top_left,
down_right: down_right}
end
def set(_, x, y) do
raise("x(#{x}) and y(#{y}) must be positive.")
end
@doc """
Unset a dot at (x, y) from a canvas.
"""
def unset(canvas = %{chars: chars}, x, y)
when x > 0 and y > 0
do
{chars_xy, {dots_x, dots_y}} = normalize x, y
braille_byte_to_be_unset = Braille.pixel_map(dots_x, dots_y)
chars = case Map.fetch chars, chars_xy do
{:ok, char0} ->
case char0 &&& braille_byte_to_be_unset do
0 -> chars
_ ->
case bxor(char0, braille_byte_to_be_unset) do
0 -> Map.delete(chars, chars_xy)
new_char -> Map.put(chars, chars_xy, new_char)
end
end
:error -> chars
end
%{canvas | chars: chars}
end
@doc """
Draw a canvas.
"""
def frame %{chars: chars, top_left: {tl_x, tl_y}, down_right: {dr_x, dr_y}} do
frame_string =
for y <- tl_y .. dr_y,
x <- tl_x .. dr_x,
into: ""
do
char =
case Map.fetch chars, {x, y} do
{:ok, char} -> char
:error -> 0
end
code = Braille.to_utf8_code char
string = <<code :: utf8>>
if x == dr_x do
string <> "\n"
else
string
end
end
IO.puts frame_string
end
defp normalize x, y do
rounded_x = Kernel.round x
rounded_y = Kernel.round y
chars_x = div rounded_x, 2
chars_y = div rounded_y, 4
dots_x = rem rounded_x, 2
dots_y = rem rounded_y, 4
{{chars_x, chars_y}, {dots_x, dots_y}}
end
end
defmodule Drawille.Examples.Circular do
alias Drawille.Canvas
def test do
1..5 |> Enum.map(fn(x) ->
Rect.new(x * 10, x * 10, 20, 20 )
end)
|> Enum.each(fn(x) ->
sx = x |> Rect.min(:x)
ex = x |> Rect.max(:x)
sy = x |> Rect.min(:y)
ey = x |> Rect.max(:y)
canvas = sx..ex |> Enum.reduce(Canvas.new, fn(dot, canvas) ->
Canvas.set(canvas, dot, sy)
end)
canvas = sy..ey |> Enum.reduce(canvas, fn(dot, canvas) ->
Canvas.set(canvas, ex, dot)
end)
canvas = ex..sx |> Enum.reduce(canvas, fn(dot, canvas) ->
Canvas.set(canvas, dot, ey)
end)
canvas = ey..sy |> Enum.reduce(canvas, fn(dot, canvas) ->
Canvas.set(canvas, sx, dot)
end)
Canvas.frame(canvas)
end)
end
def test2 do
n = 200
Enum.reduce(1..n, Canvas.new,
fn(t, acc_canvas) ->
theta = 2 * :math.pi * (t/200)
x = 30 * :math.cos(theta) + 40
y = 30 * :math.sin(theta) + 40
Canvas.set(acc_canvas, x, y)
end)
|> Canvas.frame
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment