Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thickness and In Bounds check - keyword args #31

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name = "ImageDraw"
uuid = "4381153b-2b60-58ae-a1ba-fd683676385f"
version = "0.2.2"
version = "0.2.3"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Expand Down
6 changes: 5 additions & 1 deletion src/ImageDraw.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module ImageDraw

# package code goes here
using ImageCore, Distances
using ImageCore, Distances, Combinatorics

include("core.jl")
include("line2d.jl")
Expand All @@ -10,6 +10,10 @@ include("circle2d.jl")
include("paths.jl")
include("cross.jl")

if v"1.0" <= VERSION < v"1.1"
isnothing(x) = x===nothing
end

#export methods
export
draw,
Expand Down
7 changes: 4 additions & 3 deletions src/circle2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import LinearAlgebra: det
CirclePointRadius(x::Int, y::Int, ρ::T) where {T<:Real} = CirclePointRadius(Point(x,y), ρ)
CirclePointRadius(p::CartesianIndex{2}, ρ::T) where {T<:Real} = CirclePointRadius(Point(p), ρ)

draw!(img::AbstractArray{T, 2}, circle::CirclePointRadius, color::T) where {T<:Colorant} = draw!(img, Ellipse(circle), color)
draw!(img::AbstractArray{T, 2}, circle::CirclePointRadius, color::T; in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where {T<:Colorant} =
draw!(img, Ellipse(circle), color, in_bounds=in_bounds, thickness=thickness)

#CircleThreePoints methods

Expand All @@ -14,7 +15,7 @@ CircleThreePoints(x1::Int, y1::Int, x2::Int, y2::Int, x3::Int, y3::Int) =
CircleThreePoints(p1::CartesianIndex{2}, p2::CartesianIndex{2}, p3::CartesianIndex{2}) =
CircleThreePoints(Point(p1), Point(p2), Point(p3))

function draw!(img::AbstractArray{T, 2}, circle::CircleThreePoints, color::T) where T<:Colorant
function draw!(img::AbstractArray{T, 2}, circle::CircleThreePoints, color::T; in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where T<:Colorant
ind = axes(img)
x1 = circle.p1.x; y1 = circle.p1.y
x2 = circle.p2.x; y2 = circle.p2.y
Expand All @@ -25,5 +26,5 @@ function draw!(img::AbstractArray{T, 2}, circle::CircleThreePoints, color::T) wh
ρ = euclidean([x1, y1], R)
(first(ind[2]) <= R[1] <= last(ind[2]) && first(ind[1]) <= R[2] <= last(ind[1])) || error("Center of circle is out of the bounds of image")
ρ - 1 <= minimum(abs, [R[1] - first(ind[2]), R[1] - last(ind[2]), R[2] - first(ind[1]), R[2] - last(ind[1])]) || error("Circle is out of the bounds of image : Radius is too large")
draw!(img, CirclePointRadius(Point(round(Int,R[1]), round(Int,R[2])), ρ), color)
draw!(img, CirclePointRadius(Point(round(Int,R[1]), round(Int,R[2])), ρ), color, in_bounds=in_bounds, thickness=thickness)
end
137 changes: 100 additions & 37 deletions src/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ 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


"""
Expand All @@ -24,7 +30,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
Expand All @@ -36,7 +42,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
Expand All @@ -46,7 +52,7 @@ end

A `Drawable` circle passing through points `p1`, `p2` and `p3`
"""
struct CircleThreePoints <: Circle
struct CircleThreePoints <: AbstractCircle
p1::Point
p2::Point
p3::Point
Expand All @@ -57,7 +63,7 @@ end

A `Drawable` circle having center `center` and radius `ρ`
"""
struct CirclePointRadius{T<:Real} <: Circle
struct CirclePointRadius{T<:Real} <: AbstractCircle
center::Point
ρ::T
end
Expand All @@ -67,7 +73,7 @@ end

A `Drawable` finite length line between `p1` and `p2`
"""
struct LineSegment <: Drawable
struct LineSegment <: AbstractLine
p1::Point
p2::Point
end
Expand All @@ -80,7 +86,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

Expand All @@ -90,7 +96,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
Expand All @@ -104,7 +110,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

Expand All @@ -120,7 +126,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
Expand All @@ -131,42 +137,61 @@ 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

"""
img = draw!(img, drawable, color)
img = draw!(img, drawable)
img_map = ImageMap(img, x, y)

Overlays an image on an larger image. (x, y) is the center for placing the image
"""
struct ImageMap <: Drawable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the usage of this? I can't find anywhere this struct is used in your PR.

image
x
y
end

"""
img = draw!(img, drawable; in_bounds, thickness)

Draws `drawable` on `img` using color `color` which
defaults to `oneunit(eltype(img))`
"""
draw!(img::AbstractArray{T,2}, object::Drawable) where {T<:Colorant} = draw!(img, object, oneunit(T))
draw!(img::AbstractArray{T,2}, object::Drawable; in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where {T<:Colorant} =
draw!(img, object, oneunit(T), in_bounds=in_bounds, thickness=thickness)


"""
img = draw!(img, [drawable], [color])
img = draw!(img, [drawable] ,color)
img = draw!(img, [drawable])
img = draw!(img, [drawable] ,color; in_bounds, thickness)

Draws all objects in `[drawable]` in the given order on `img` using
corresponding colors from `[color]` which defaults to `oneunit(eltype(img))`
If only a single color `color` is specified then all objects will be
colored with that color.
"""
function draw!(img::AbstractArray{T,2}, objects::AbstractVector{U}, colors::AbstractVector{V}) where {T<:Colorant, U<:Drawable, V<:Colorant}
colors = copy(colors)
while length(colors) < length(objects)
push!(colors, oneunit(T))
end
foreach((object, color) -> draw!(img, object, color), objects, colors)
corresponding colors from `color` which defaults to `oneunit(eltype(img))`.
color, in_bounds and thickness flags are expanded to length of objects.
"""
function draw!(img::AbstractArray{T, 2}, objects::AbstractVector{U}, color::T = oneunit(T); in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where {T<:Colorant, U<:Drawable}
draw!(img, objects, repeat([color], length(objects));
in_bounds = repeat([in_bounds], length(objects)),
thickness = repeat([thickness], length(objects)))
end


"""
draw!(img, [drawable], [colors]; [bool], [Union{Integer,Nothing}])

Draws objects with given colors, in_bounds flags, and thickness flags.
Length of objects, colors, in_bounds flags, thickness flags, all need to be of equal length
"""
function draw!(img::AbstractArray{T,2}, objects::AbstractVector{U}, colors::AbstractVector{V}; in_bounds::Union{AbstractVector{Bool},Nothing}=nothing, thickness::Union{AbstractVector{<:Union{Integer, Nothing}},Nothing}=nothing) where {T<:Colorant, U<:Drawable, V<:Colorant}
length(colors) == length(objects) || throw("The number of colors and objects should be equal.")
isnothing(in_bounds) || length(in_bounds) == length(objects) || throw("The number of in_bounds vars and objects should be equal")
isnothing(thickness) || length(thickness) == length(objects) || throw("The number of thicknesses and objects should be equal")
isnothing(in_bounds) && isnothing(thickness) ? foreach((object, color) -> draw!(img, object, color), objects, colors) :
foreach((object, color, in_b, thick) -> draw!(img, object, color, in_bounds=in_b, thickness=thick), objects, colors, in_bounds, thickness)
img
end

draw!(img::AbstractArray{T,2}, objects::AbstractVector{U}, color::T = oneunit(T)) where {T<:Colorant, U<:Drawable} =
draw!(img, objects, [color for i in 1:length(objects)])

"""
img_new = draw(img, drawable, color)
Expand All @@ -176,20 +201,33 @@ Draws the `drawable` object on a copy of image `img` using color
`color`. Can also draw multiple `Drawable` objects when passed
as a `AbstractVector{Drawable}` with corresponding colors in `[color]`
"""
draw(img::AbstractArray{T,2}, args...; in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where {T<:Colorant} =
draw!(copy(img), args...; in_bounds=in_bounds, thickness=thickness)

draw(img::AbstractArray{T,2}, args...) where {T<:Colorant} = draw!(copy(img), args...)

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
drawifinbounds!(img, point, color)

draw!(img::AbstractArray{T,2}, p::Point, color::T = oneunit(T); in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where {T<:Colorant} =
draw!(img, p.y, p.x, color, in_bounds=in_bounds, thickness=thickness)
draw!(img::AbstractArray{T,2}, p::CartesianIndex{2}, color::T = oneunit(T); in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where {T<:Colorant} =
draw!(img, Point(p), color, in_bounds=in_bounds, thickness=thickness)

function draw!(img::AbstractArray{T,2}, y::Integer, x::Integer, color::T; in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where T<:Colorant
if !isnothing(thickness)
drawwiththickness!(img, y, x, color, in_bounds, thickness) # recursively call drawifinbounds!
else
in_bounds ? img[y, x] = color : drawifinbounds!(img, y, x, color)
end
img
end

"""

img_new = drawifinbounds!(img, y, x, color)
img_new = drawifinbounds!(img, Point, color)
img_new = drawifinbounds!(img, CartesianIndex, color)
img_new = drawifinbounds!(img, y, x, color; in_bounds, thickness)
img_new = drawifinbounds!(img, point, color; in_bounds, thickness)
img_new = drawifinbounds!(img, cartesianIndex, color; in_bounds, thickness)

Draws a single point after checkbounds() for coordinate in the image.
Color Defaults to oneunit(T)
Expand All @@ -200,6 +238,31 @@ drawifinbounds!(img::AbstractArray{T,2}, p::Point, color::T = oneunit(T)) where
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
checkbounds(Bool, img, y, x) && (img[y, x] = color)
img
end

"""
img_new = drawwiththickness!(img, y, x, color; in_bounds, thickness)
img_new = drawwiththickness!(img, point, color; in_bounds, thickness)
img_new = drawwiththickness!(img, cartesianIndex, color; in_bounds, thickness)

Draws pixel with given thickness
Color Defaults to oneunit(T)
Thickness defaults to 1

"""

drawwiththickness!(img::AbstractArray{T,2}, p::Point, color::T, in_bounds::Bool, thickness::Integer) where {T<:Colorant} = drawwiththickness!(img, p.y, p.x, color, in_bounds, thickness)
drawwiththickness!(img::AbstractArray{T,2}, p::CartesianIndex{2}, color::T, in_bounds::Bool, thickness::Integer) where {T<:Colorant} = drawifinbounds!(img, Point(p), color, in_bounds, thickness)

function drawwiththickness!(img::AbstractArray{T,2}, y0::Int, x0::Int, color::T, in_bounds::Bool, thickness::Integer) where {T<:Colorant}
m = ceil(Int, thickness/2) - 1
n = thickness % 2 == 0 ? m+1 : m
pixels = [i for i = -m:n]

for x in pixels, y in pixels
draw!(img, y0+y, x0+x, color, in_bounds=in_bounds)
Codyk12 marked this conversation as resolved.
Show resolved Hide resolved
end
img
end
8 changes: 4 additions & 4 deletions src/cross.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
"""
cross = Cross(c, arm::Int)
cross = Cross(c, arm::Int, color; in_bounds, thickness))
A `Drawable` cross passing through the point `c` with arms that are `arm` pixels long.
"""
Cross(c, arm::Int) = Cross(c, -arm:arm)

function draw!(img::AbstractArray{T, 2}, cross::Cross, color::T) where T<:Colorant
function draw!(img::AbstractArray{T, 2}, cross::Cross, color::T; in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where T<:Colorant
for Δx in cross.range
drawifinbounds!(img, cross.c.y, cross.c.x + Δx, color)
draw!(img, cross.c.y, cross.c.x + Δx, color, in_bounds=in_bounds, thickness=thickness)
end
for Δy in cross.range
drawifinbounds!(img, cross.c.y + Δy, cross.c.x, color)
draw!(img, cross.c.y + Δy, cross.c.x, color, in_bounds=in_bounds, thickness=thickness)
end
img
end
10 changes: 5 additions & 5 deletions src/ellipse2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Ellipse(x::Int, y::Int, ρx::T, ρy::U) where {T<:Real, U<:Real} = Ellipse(Point
Ellipse(p::CartesianIndex{2}, ρx::T, ρy::U) where {T<:Real, U<:Real} = Ellipse(Point(p), ρx, ρy)
Ellipse(circle::CirclePointRadius) = Ellipse(circle.center, circle.ρ, circle.ρ)

function draw!(img::AbstractArray{T, 2}, ellipse::Ellipse, color::T) where T<:Colorant
function draw!(img::AbstractArray{T, 2}, ellipse::Ellipse, color::T; in_bounds::Bool=false, thickness::Union{Integer, Nothing}=nothing) where T<:Colorant
ys = Int[]
xs = Int[]
for i in ellipse.center.y : ellipse.center.y + ellipse.ρy
Expand All @@ -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)
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)
draw!(img, yi, xi, color, in_bounds=in_bounds, thickness=thickness)
draw!(img,2 * ellipse.center.y - yi, xi, color, in_bounds=in_bounds, thickness=thickness)
draw!(img,yi, 2 * ellipse.center.x - xi, color, in_bounds=in_bounds, thickness=thickness)
draw!(img, 2 * ellipse.center.y - yi, 2 * ellipse.center.x - xi, color, in_bounds=in_bounds, thickness=thickness)
end
img
end
Loading