diff --git a/src/core.jl b/src/core.jl index 7115383..4322e9d 100644 --- a/src/core.jl +++ b/src/core.jl @@ -14,8 +14,13 @@ struct Point <: Drawable y::Int end -abstract type Line <: Drawable end -abstract type Circle <: Drawable end +abstract type AbstractPath <: Drawable end +abstract type AbstractLine <: Drawable end +abstract type AbstractShape <: Drawable end + +abstract type AbstractPolygon <: AbstractShape end +abstract type AbstractEllipse <: AbstractShape end +abstract type AbstractCircle <: AbstractEllipse end """ @@ -24,7 +29,7 @@ abstract type Circle <: Drawable end A `Drawable` infinite length line passing through the two points `p1` and `p2`. """ -struct LineTwoPoints <: Line +struct LineTwoPoints <: AbstractLine p1::Point p2::Point end @@ -36,7 +41,7 @@ A `Drawable` infinte length line having perpendicular length `ρ` from origin and angle `θ` between the perpendicular and x-axis """ -struct LineNormal{T<:Real, U<:Real} <: Line +struct LineNormal{T<:Real, U<:Real} <: AbstractLine ρ::T θ::U end @@ -46,7 +51,7 @@ end A `Drawable` circle passing through points `p1`, `p2` and `p3` """ -struct CircleThreePoints <: Circle +struct CircleThreePoints <: AbstractCircle p1::Point p2::Point p3::Point @@ -57,7 +62,7 @@ end A `Drawable` circle having center `center` and radius `ρ` """ -struct CirclePointRadius{T<:Real} <: Circle +struct CirclePointRadius{T<:Real} <: AbstractCircle center::Point ρ::T end @@ -67,7 +72,7 @@ end A `Drawable` finite length line between `p1` and `p2` """ -struct LineSegment <: Drawable +struct LineSegment <: AbstractLine p1::Point p2::Point end @@ -80,7 +85,7 @@ of points in `[point]`. !!! note This will create a non-closed path. For a closed path, see `Polygon` """ -struct Path <: Drawable +struct Path <: AbstractPath vertices::Vector{Point} end @@ -90,7 +95,7 @@ end A `Drawable` ellipse with center `center` and parameters `ρx` and `ρy` """ -struct Ellipse{T<:Real, U<:Real} <: Drawable +struct Ellipse{T<:Real, U<:Real} <: AbstractEllipse center::Point ρx::T ρy::U @@ -104,7 +109,7 @@ consecutive points in `[vertex]` along with the first and last point. !!! note This will create a closed path. For a non-closed path, see `Path` """ -struct Polygon <: Drawable +struct Polygon <: AbstractPolygon vertices::Vector{Point} end @@ -120,7 +125,7 @@ A `Drawable` regular polygon. * `θ::Real` : orientation of the polygon w.r.t x-axis (in radians) """ -struct RegularPolygon{T<:Real, U<:Real} <: Drawable +struct RegularPolygon{T<:Real, U<:Real} <: AbstractPolygon center::Point side_count::Int side_length::T @@ -131,7 +136,7 @@ end cross = Cross(c, range::UnitRange{Int}) A `Drawable` cross passing through the point `c` with arms ranging across `range`. """ -struct Cross <: Drawable +struct Cross <: AbstractPath c::Point range::UnitRange{Int} end @@ -182,8 +187,24 @@ Point(τ::Tuple{Int, Int}) = Point(τ...) Point(p::CartesianIndex) = Point(p[2], p[1]) function draw!(img::AbstractArray{T,2}, point::Point, color::T) where T<:Colorant - if checkbounds(Bool, img, point.y, point.x) - img[point.y, point.x] = color - end + drawifinbounds!(img, point, color) +end + +""" + + img_new = drawifinbounds!(img, y, x, color) + img_new = drawifinbounds!(img, Point, color) + img_new = drawifinbounds!(img, CartesianIndex, color) + +Draws a single point after checkbounds() for coordinate in the image. +Color Defaults to oneunit(T) + +""" + +drawifinbounds!(img::AbstractArray{T,2}, p::Point, color::T = oneunit(T)) where {T<:Colorant} = drawifinbounds!(img, p.y, p.x, color) +drawifinbounds!(img::AbstractArray{T,2}, p::CartesianIndex{2}, color::T = oneunit(T)) where {T<:Colorant} = drawifinbounds!(img, Point(p), color) + +function drawifinbounds!(img::AbstractArray{T,2}, y::Int, x::Int, color::T) where {T<:Colorant} + if checkbounds(Bool, img, y, x) img[y, x] = color end img end diff --git a/src/cross.jl b/src/cross.jl index ae9c9d6..cbf070a 100644 --- a/src/cross.jl +++ b/src/cross.jl @@ -6,10 +6,10 @@ Cross(c, arm::Int) = Cross(c, -arm:arm) function draw!(img::AbstractArray{T, 2}, cross::Cross, color::T) where T<:Colorant for Δx in cross.range - img[cross.c.y, cross.c.x + Δx] = color + drawifinbounds!(img, cross.c.y, cross.c.x + Δx, color) end for Δy in cross.range - img[cross.c.y + Δy, cross.c.x] = color + drawifinbounds!(img, cross.c.y + Δy, cross.c.x, color) end img end diff --git a/src/ellipse2d.jl b/src/ellipse2d.jl index 85bb407..9a166df 100644 --- a/src/ellipse2d.jl +++ b/src/ellipse2d.jl @@ -17,10 +17,10 @@ function draw!(img::AbstractArray{T, 2}, ellipse::Ellipse, color::T) where T<:Co end end for (yi, xi) in zip(ys, xs) - img[yi, xi] = color - img[2 * ellipse.center.y - yi, xi] = color - img[yi, 2 * ellipse.center.x - xi] = color - img[2 * ellipse.center.y - yi, 2 * ellipse.center.x - xi] = color + drawifinbounds!(img, yi, xi, color) + drawifinbounds!(img,2 * ellipse.center.y - yi, xi, color) + drawifinbounds!(img,yi, 2 * ellipse.center.x - xi, color) + drawifinbounds!(img, 2 * ellipse.center.y - yi, 2 * ellipse.center.x - xi, color) end img end diff --git a/src/line2d.jl b/src/line2d.jl index dd0781d..5689e7e 100644 --- a/src/line2d.jl +++ b/src/line2d.jl @@ -79,7 +79,7 @@ function bresenham(img::AbstractArray{T, 2}, y0::Int, x0::Int, y1::Int, x1::Int, err = (dx > dy ? dx : -dy) / 2 while true - img[y0, x0] = color + drawifinbounds!(img, y0, x0, color) (x0 != x1 || y0 != y1) || break e2 = err if e2 > -dx @@ -125,9 +125,9 @@ function xiaolin_wu(img::AbstractArray{T, 2}, y0::Int, x0::Int, y1::Int, x1::Int xpxl0 = xend ypxl0 = trunc(Int, yend) index = swapped ? CartesianIndex(xpxl0, ypxl0) : CartesianIndex(ypxl0, xpxl0) - if checkbounds(Bool, img, index) img[index] = T(rfpart(yend) * xgap) end + drawifinbounds!(img, index, T(rfpart(yend) * xgap)) index = swapped ? CartesianIndex(xpxl0, ypxl0 + 1) : CartesianIndex(ypxl0 + 1, xpxl0) - if checkbounds(Bool, img, index) img[index] = T(fpart(yend) * xgap) end + drawifinbounds!(img, index, T(fpart(yend) * xgap)) intery = yend + gradient xend = round(Int, x1) @@ -136,15 +136,15 @@ function xiaolin_wu(img::AbstractArray{T, 2}, y0::Int, x0::Int, y1::Int, x1::Int xpxl1 = xend ypxl1 = trunc(Int, yend) index = swapped ? CartesianIndex(xpxl1, ypxl1) : CartesianIndex(ypxl1, xpxl1) - if checkbounds(Bool, img, index) img[index] = T(rfpart(yend) * xgap) end + drawifinbounds!(img, index, T(rfpart(yend) * xgap)) index = swapped ? CartesianIndex(xpxl1, ypxl1 + 1) : CartesianIndex(ypxl1 + 1, xpxl1) - if checkbounds(Bool, img, index) img[index] = T(fpart(yend) * xgap) end + drawifinbounds!(img, index, T(fpart(yend) * xgap)) for i in (xpxl0 + 1):(xpxl1 - 1) index = swapped ? CartesianIndex(i, trunc(Int, intery)) : CartesianIndex(trunc(Int, intery), i) - if checkbounds(Bool, img, index) img[index] = T(rfpart(intery)) end + drawifinbounds!(img, index, T(rfpart(intery))) index = swapped ? CartesianIndex(i, trunc(Int, intery) + 1) : CartesianIndex(trunc(Int, intery) + 1, i) - if checkbounds(Bool, img, index) img[index] = T(fpart(intery)) end + drawifinbounds!(img, index, T(fpart(intery))) intery += gradient end img diff --git a/src/paths.jl b/src/paths.jl index b880ab7..02bcd8b 100644 --- a/src/paths.jl +++ b/src/paths.jl @@ -5,22 +5,10 @@ Path(v::AbstractVector{CartesianIndex{2}}) = Path([Point(p) for p in v]) function draw!(img::AbstractArray{T, 2}, path::Path, color::T) where T<:Colorant vertices = [CartesianIndex(p.y, p.x) for p in path.vertices] - f = CartesianIndex(map(r->first(r)-1, axes(img))) - l = CartesianIndex(map(r->last(r), axes(img))) - - if min(f,vertices[1])!=f || max(l,vertices[1])!=l - println(vertices[1]) - error("Point coordinates out of range.") - end - for i in 1:length(vertices)-1 - if min(f,vertices[i+1])==f && max(l,vertices[i+1])==l - draw!(img, LineSegment(vertices[i], vertices[i+1]), color) - else - println(vertices[i+1]) - error("Point coordinates out of range.") - end + draw!(img, LineSegment(vertices[i], vertices[i+1]), color) end + img end #Polygon methods diff --git a/test/paths.jl b/test/paths.jl index fa5cf8e..6488b3e 100644 --- a/test/paths.jl +++ b/test/paths.jl @@ -65,12 +65,14 @@ end @test all(x->x==RGB{N0f8}(0,0,0), img[2,1:3])==true @test img[2,4]==RGB{N0f8}(1,1,1) - img = zeros(Gray{N0f8},5,5) - invalid_points = [(6,1), (4,4), (1,7), (7,6)] - @test_throws ErrorException draw!(img, Path(invalid_points)) + # No longer invalid, the program will draw what it can on the image - invalid_points = [(4,4), (1,7), (7,6)] - @test_throws ErrorException draw!(img, Path(invalid_points)) + # img = zeros(Gray{N0f8},5,5) + # invalid_points = [(6,1), (4,4), (1,7), (7,6)] + # @test_throws ErrorException draw!(img, Path(invalid_points)) + # + # invalid_points = [(4,4), (1,7), (7,6)] + # @test_throws ErrorException draw!(img, Path(invalid_points)) end @testset "RegularPolygon" begin