From 502d6a526854e3f40f4e43ab2e2b1b63fddfa837 Mon Sep 17 00:00:00 2001 From: Kesler <4ck@mac101489.ornl.gov> Date: Mon, 3 Jun 2019 12:46:13 -0400 Subject: [PATCH 1/4] Added checkbounds() testing to all draw!() functions as to not throw errors when shapes which are partially in the images are attempted to be drawn. --- src/core.jl | 22 +++++++++++++++++++--- src/cross.jl | 4 ++-- src/ellipse2d.jl | 8 ++++---- src/line2d.jl | 14 +++++++------- src/paths.jl | 35 ++++++++++++++++++++++------------- 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/core.jl b/src/core.jl index 7115383..4fa1ff6 100644 --- a/src/core.jl +++ b/src/core.jl @@ -182,8 +182,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..3221b65 100644 --- a/src/paths.jl +++ b/src/paths.jl @@ -5,22 +5,31 @@ 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 + + # No need for corrdinate checking at this level as draw!(::LineSegment, ...) + # checks and only draws pixels which are on the image + + # 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 + # end + end #Polygon methods From 40bda8017c57da5330cd7d40c1e83e11750e85cb Mon Sep 17 00:00:00 2001 From: Kesler <4ck@mac101489.ornl.gov> Date: Mon, 3 Jun 2019 15:42:42 -0400 Subject: [PATCH 2/4] Changed Test Files to no longer check for Exception thrown for drawing paths that went 'off image' --- src/paths.jl | 57 +++++++++++++++++++++++++++------------------------ test/paths.jl | 12 ++++++----- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/paths.jl b/src/paths.jl index 3221b65..6c9de5f 100644 --- a/src/paths.jl +++ b/src/paths.jl @@ -4,33 +4,36 @@ Path(v::AbstractVector{Tuple{Int, Int}}) = Path([Point(p...) for p in v]) 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] - - for i in 1:length(vertices)-1 - draw!(img, LineSegment(vertices[i], vertices[i+1]), color) - end - - # No need for corrdinate checking at this level as draw!(::LineSegment, ...) - # checks and only draws pixels which are on the image - - # 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 - # end - -end + 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))) + + for i in 1:length(vertices)-1 + draw!(img, LineSegment(vertices[i], vertices[i+1]), color) + end + + img + # No need for corrdinate checking at this level as draw!(::LineSegment, ...) + # checks and only draws pixels which are on the image + + # 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 + # end + + 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 From de360830a92e7b9532f2c3e1199d2d0a8fa320b9 Mon Sep 17 00:00:00 2001 From: Kesler <4ck@mac101489.ornl.gov> Date: Tue, 4 Jun 2019 10:30:49 -0400 Subject: [PATCH 3/4] Fixed spacing and deleted unnecessary code --- src/paths.jl | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/paths.jl b/src/paths.jl index 6c9de5f..02bcd8b 100644 --- a/src/paths.jl +++ b/src/paths.jl @@ -4,36 +4,12 @@ Path(v::AbstractVector{Tuple{Int, Int}}) = Path([Point(p...) for p in v]) 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))) - - for i in 1:length(vertices)-1 - draw!(img, LineSegment(vertices[i], vertices[i+1]), color) - end - - img - # No need for corrdinate checking at this level as draw!(::LineSegment, ...) - # checks and only draws pixels which are on the image - - # 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 - # end - - end + vertices = [CartesianIndex(p.y, p.x) for p in path.vertices] + for i in 1:length(vertices)-1 + draw!(img, LineSegment(vertices[i], vertices[i+1]), color) + end + img +end #Polygon methods From 69b2710d09c7afe9a5ac061ad8e80efb7f23d0f6 Mon Sep 17 00:00:00 2001 From: Kesler <4ck@mac101489.ornl.gov> Date: Wed, 19 Jun 2019 18:11:30 -0400 Subject: [PATCH 4/4] Added to and expanded the type hierarchy for drawable objects --- src/core.jl | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/core.jl b/src/core.jl index 4fa1ff6..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