So close I can almost taste it.
This commit is contained in:
parent
8dbe321e17
commit
92d64f4e99
3 changed files with 31 additions and 69 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{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
|
||||
|
|
Loading…
Reference in a new issue