From 92d64f4e9924d6dfe2e2a199ebb028a97fc71a04 Mon Sep 17 00:00:00 2001 From: James Harton Date: Sun, 22 Jan 2017 09:23:52 +1300 Subject: [PATCH] So close I can almost taste it. --- lib/vivid/polygon.ex | 3 ++ lib/vivid/rasterize/polygon.ex | 53 ++++------------------------------ lib/vivid/slpfa.ex | 44 ++++++++++++++-------------- 3 files changed, 31 insertions(+), 69 deletions(-) diff --git a/lib/vivid/polygon.ex b/lib/vivid/polygon.ex index b7b10ea..563dc33 100644 --- a/lib/vivid/polygon.ex +++ b/lib/vivid/polygon.ex @@ -158,6 +158,9 @@ defmodule Vivid.Polygon do |> init end + def filled?(%Polygon{fill: fill}), do: fill + def fill(%Polygon{}=polygon, fill) when is_boolean(fill), do: %{polygon | fill: fill} + defp points_to_lines(lines, []) do origin = lines |> List.last |> Line.termination term = lines |> List.first |> Line.origin diff --git a/lib/vivid/rasterize/polygon.ex b/lib/vivid/rasterize/polygon.ex index fc02278..b4a243e 100644 --- a/lib/vivid/rasterize/polygon.ex +++ b/lib/vivid/rasterize/polygon.ex @@ -1,5 +1,5 @@ defimpl Vivid.Rasterize, for: Vivid.Polygon do - alias Vivid.{Polygon, Rasterize, Point, Bounds, Line} + alias Vivid.{Polygon, Rasterize, Point, Bounds, Line, SLPFA} require Integer defmodule InvalidPolygonError do @@ -42,52 +42,9 @@ defimpl Vivid.Rasterize, for: Vivid.Polygon do end def rasterize(%Polygon{fill: true}=polygon, bounds) do - range = polygon - |> Bounds.bounds - |> y_range - - lines = polygon - |> Polygon.to_lines - |> Enum.reject(&Line.horizontal?(&1)) - - points = Enum.reduce(range, MapSet.new, fn y, points -> - xs = lines - |> Stream.map(&Line.y_intersect(&1, y)) - |> Stream.reject(&is_nil(&1)) - |> Stream.map(&Point.x(&1)) - |> Stream.map(&round(&1)) - # |> Enum.dedup - |> Enum.sort - - MapSet.new - |> reduce_x_fill([], xs, y) - |> Stream.filter(&Bounds.contains?(bounds, &1)) - |> Enum.into(points) - end) - - lines - |> Stream.flat_map(&Enum.to_list(&1)) - |> Stream.map(&Point.round(&1)) - |> Stream.filter(&Bounds.contains?(bounds, &1)) - |> Enum.reduce(points, fn point, points -> MapSet.put(points, point) end) - end - - defp y_range(bounds) do - y0 = bounds |> Bounds.min |> Point.y |> round - y1 = bounds |> Bounds.max |> Point.y |> round - if y1 > y0, do: y0..y1, else: y1..y0 - end - - defp reduce_x_fill(points, _lhs, [], _y), do: points - - defp reduce_x_fill(points, lhs, rhs, y) when rem(length(lhs), 2) == 1 and rem(length(rhs), 2) == 1 do - [x0 | _] = lhs - [x1 | rhs] = rhs - points = Enum.reduce(x0..x1, points, fn x, points -> MapSet.put(points, Point.init(x, y)) end) - reduce_x_fill(points, [x1 | lhs], rhs, y) - end - - defp reduce_x_fill(points, lhs, [x | rhs], y) do - reduce_x_fill(points, [x | lhs], rhs, y) + polygon + |> SLPFA.fill + |> Enum.filter(&Bounds.contains?(bounds, &1)) + |> Enum.into(MapSet.new) end end diff --git a/lib/vivid/slpfa.ex b/lib/vivid/slpfa.ex index 4d3a4ce..b728c45 100644 --- a/lib/vivid/slpfa.ex +++ b/lib/vivid/slpfa.ex @@ -17,17 +17,17 @@ defmodule Vivid.SLPFA do |> process_edge_table end - defp process_edge_table([active0, active1 | edge_table]) do - scan_line = active0.y_min - active = [active0, active1] + defp process_edge_table([a0 | _]=edge_table) do + scan_line = a0.y_min + {active, edge_table} = update_active_list(scan_line, [], edge_table) points = pixels_for_active_list(MapSet.new, active, scan_line) process_edge_table(points, active, edge_table, scan_line + 1) end - defp process_edge_table(points, _active, [], scan_line), do: points + defp process_edge_table(points, []=_active, _edge_table, scan_line), do: points defp process_edge_table(points, active, edge_table, scan_line) do - active = update_active_list(scan_line, active, edge_table) + {active, edge_table} = update_active_list(scan_line, active, edge_table) points = pixels_for_active_list(points, active, scan_line) active = increment_active_edges(active) process_edge_table(points, active, edge_table, scan_line + 1) @@ -64,10 +64,23 @@ defmodule Vivid.SLPFA do end defp update_active_list(scan_line, active, edge_table) do - active - |> Enum.reject(&remove_processed_edges(&1, scan_line)) - |> add_active_edges(scan_line, edge_table) - |> Enum.sort(&sort_by_x_and_slope(&1, &2)) + {active, edge_table} = active + |> Stream.concat(edge_table) + |> Enum.reduce({[], []}, fn + %EdgeBucket{y_min: y_min, y_max: y_max}=edge, {active, edge_table} when y_min <= scan_line and y_max > scan_line -> + active = [edge | active] + {active, edge_table} + %EdgeBucket{y_min: y_min}=edge, {active, edge_table} when y_min >= scan_line -> + edge_table = [edge | edge_table] + {active, edge_table} + _edge_bucket, {active, edge_table} -> + {active, edge_table} + end) + + active = active + |> Enum.sort(&sort_by_x_and_slope(&1, &2)) + + {active, edge_table} end defp sort_by_x_and_slope(%EdgeBucket{x: x0}, @@ -75,22 +88,11 @@ defmodule Vivid.SLPFA do when x0 < x1, do: true defp sort_by_x_and_slope(%EdgeBucket{x: x0}, %EdgeBucket{x: x1}) - when x1 > x0, do: false + when x0 > x1, do: false defp sort_by_x_and_slope(%EdgeBucket{distance_x: dx0, distance_y: dy0}, %EdgeBucket{distance_x: dx1, distance_y: dy1}), do: (dx0 / dy0) < (dx1 / dy1) - defp add_active_edges(active, _scan_line, []), do: Enum.reverse(active) - defp add_active_edges(active, scan_line, [%EdgeBucket{y_min: y_min}=edge | edge_table]) when scan_line == y_min do - add_active_edges([edge | active], scan_line, edge_table) - end - defp add_active_edges(active, scan_line, [_ | edge_table]) do - add_active_edges(active, scan_line, edge_table) - end - - defp remove_processed_edges(%EdgeBucket{y_max: y}, scan_line) when y == scan_line, do: true - defp remove_processed_edges(_edge_bucket, _scan_line), do: false - defp create_edge_table(vertices) do vertices |> Stream.with_index