From f09bf246d81bbea5abfb38cd630be3490495a0b7 Mon Sep 17 00:00:00 2001 From: James Harton Date: Thu, 22 Dec 2016 14:04:29 +1300 Subject: [PATCH] Fix bugs in circle and line rasterizing. --- lib/vivid.ex | 16 ++++++++++++++++ lib/vivid/frame.ex | 24 +++++++++++++++++------- lib/vivid/rasterize/circle.ex | 34 +++++++++++++++++++++------------- lib/vivid/rasterize/line.ex | 19 ++++++++++++------- 4 files changed, 66 insertions(+), 27 deletions(-) diff --git a/lib/vivid.ex b/lib/vivid.ex index c564ec6..328e8a8 100644 --- a/lib/vivid.ex +++ b/lib/vivid.ex @@ -1,2 +1,18 @@ defmodule Vivid do + + def example do + polygon = [{1,1}, {1,4}, {4,4}, {4,1}] + |> Enum.map(fn {x,y} -> Vivid.Point.init(x,y) end) + |> Vivid.Polygon.init + + line = [{9,1}, {1,9}] + |> Enum.map(fn {x,y} -> Vivid.Point.init(x,y) end) + |> Vivid.Line.init + + Vivid.Frame.init(10,10) + |> Vivid.Frame.push(polygon, 1) + |> Vivid.Frame.push(line, 1) + |> Vivid.Frame.puts + end + end diff --git a/lib/vivid/frame.ex b/lib/vivid/frame.ex index 6f861b2..d6ea013 100644 --- a/lib/vivid/frame.ex +++ b/lib/vivid/frame.ex @@ -74,15 +74,15 @@ defmodule Vivid.Frame do ...> |> Vivid.Frame.push(circle, 1) ...> |> Vivid.Frame.to_string "...........\n" <> - ".....X.....\n" <> - "..X.....X..\n" <> + "....XXX....\n" <> + "..XX...XX..\n" <> "..X.....X..\n" <> ".X.......X.\n" <> ".X.......X.\n" <> ".X.......X.\n" <> "..X.....X..\n" <> - "..X.....X..\n" <> - ".....X.....\n" + "..XX...XX..\n" <> + "....XXX....\n" iex> line = Vivid.Line.init(Vivid.Point.init(0,0), Vivid.Point.init(50,50)) ...> Vivid.Frame.init(5,5) @@ -97,9 +97,13 @@ defmodule Vivid.Frame do def push(%Frame{buffer: buffer, colour_depth: c, width: w}=frame, shape, colour) when colour <= c do points = Vivid.Rasterize.rasterize(shape) buffer = Enum.reduce(points, buffer, fn(point, buffer) -> - x = point |> Point.x - y = point |> Point.y - List.replace_at(buffer, (x * w) + y, colour) + if point_inside_bounds?(point, frame) do + x = point |> Point.x + y = point |> Point.y + List.replace_at(buffer, (x * w) + y, colour) + else + buffer + end end) %{frame | buffer: buffer} end @@ -143,4 +147,10 @@ defmodule Vivid.Frame do defp allocate_buffer(size) do Enum.map((1..size), fn(_) -> 0 end) end + + defp point_inside_bounds?(%Point{x: x}, _frame) when x < 0, do: false + defp point_inside_bounds?(%Point{y: y}, _frame) when y < 0, do: false + defp point_inside_bounds?(%Point{x: x}, %Frame{width: w}) when x >= w, do: false + defp point_inside_bounds?(%Point{y: y}, %Frame{height: h}) when y >= h, do: false + defp point_inside_bounds?(_point, _frame), do: true end \ No newline at end of file diff --git a/lib/vivid/rasterize/circle.ex b/lib/vivid/rasterize/circle.ex index 8972850..80ee3d3 100644 --- a/lib/vivid/rasterize/circle.ex +++ b/lib/vivid/rasterize/circle.ex @@ -1,5 +1,5 @@ defimpl Vivid.Rasterize, for: Vivid.Circle do - alias Vivid.{Circle, Point} + alias Vivid.{Circle, Point, Polygon, Rasterize} import :math, only: [pow: 2, sqrt: 1] @moduledoc """ @@ -19,14 +19,18 @@ defimpl Vivid.Rasterize, for: Vivid.Circle do iex> Vivid.Circle.init(Vivid.Point.init(5,5), 4) ...> |> Vivid.Rasterize.rasterize MapSet.new([ - %Vivid.Point{x: 1, y: 5}, %Vivid.Point{x: 2, y: 2}, + %Vivid.Point{x: 1, y: 4}, %Vivid.Point{x: 1, y: 5}, + %Vivid.Point{x: 1, y: 6}, %Vivid.Point{x: 2, y: 2}, + %Vivid.Point{x: 2, y: 3}, %Vivid.Point{x: 2, y: 7}, %Vivid.Point{x: 2, y: 8}, %Vivid.Point{x: 3, y: 2}, %Vivid.Point{x: 3, y: 8}, %Vivid.Point{x: 4, y: 1}, %Vivid.Point{x: 4, y: 9}, %Vivid.Point{x: 5, y: 1}, %Vivid.Point{x: 5, y: 9}, %Vivid.Point{x: 6, y: 1}, %Vivid.Point{x: 6, y: 9}, %Vivid.Point{x: 7, y: 2}, %Vivid.Point{x: 7, y: 8}, %Vivid.Point{x: 8, y: 2}, - %Vivid.Point{x: 8, y: 8}, %Vivid.Point{x: 9, y: 5} + %Vivid.Point{x: 8, y: 3}, %Vivid.Point{x: 8, y: 7}, + %Vivid.Point{x: 8, y: 8}, %Vivid.Point{x: 9, y: 4}, + %Vivid.Point{x: 9, y: 5}, %Vivid.Point{x: 9, y: 6} ]) """ @@ -35,16 +39,20 @@ defimpl Vivid.Rasterize, for: Vivid.Circle do y_center = point |> Point.y r_squared = pow(radius, 2) - Enum.reduce(0-radius..radius, MapSet.new, fn (x, points) -> - y = sqrt(r_squared - pow(x, 2)) |> round - - x = x_center + x - y0 = y_center + y - y1 = y_center - y - - points - |> MapSet.put(Point.init(x, y0)) - |> MapSet.put(Point.init(x, y1)) + {points0, points1} = Enum.reduce(0-radius..radius, {[], []}, fn (x, {points0, points1}) -> + y = sqrt(r_squared - pow(x, 2)) |> round + x = x_center + x + y0 = y_center - y + y1 = y_center + y + points0 = [ Point.init(x, y0) | points0 ] + points1 = [ Point.init(x, y1) | points1 ] + {points0, points1} end) + + points1 = points1 |> Enum.reverse + + points0 ++ points1 + |> Polygon.init + |> Rasterize.rasterize end end \ No newline at end of file diff --git a/lib/vivid/rasterize/line.ex b/lib/vivid/rasterize/line.ex index 1106cb4..7866666 100644 --- a/lib/vivid/rasterize/line.ex +++ b/lib/vivid/rasterize/line.ex @@ -36,20 +36,25 @@ defimpl Vivid.Rasterize, for: Vivid.Line do """ def rasterize(%Line{}=line) do + origin = line |> Line.origin dx = line |> Line.x_distance dy = line |> Line.y_distance steps = choose_largest_of(abs(dx), abs(dy)) - x_increment = dx / steps - y_increment = dy / steps - origin = line |> Line.origin + if steps == 0 do + MapSet.new([origin]) + else + x_increment = dx / steps + y_increment = dy / steps - points = MapSet.new([origin]) - current_x = origin |> Point.x - current_y = origin |> Point.y - reduce_points(points, steps, current_x, current_y, x_increment, y_increment) + points = MapSet.new([origin]) + current_x = origin |> Point.x + current_y = origin |> Point.y + + reduce_points(points, steps, current_x, current_y, x_increment, y_increment) + end end defp reduce_points(points, 0, _, _, _, _), do: points