Skip to content
Merged
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
7 changes: 2 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ name = "Contour"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.5.7"

[deps]
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
StaticArrays = "0.10,0.11,0.12, 1.0"
julia = "0.7, 1"

[extras]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["LinearAlgebra", "StatsBase", "Test", "OffsetArrays"]
test = ["LinearAlgebra", "StatsBase", "Test", "OffsetArrays", "StaticArrays"]
28 changes: 13 additions & 15 deletions src/Contour.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
module Contour

using StaticArrays

include("interpolate.jl")

export
Expand All @@ -17,17 +15,17 @@ export
import Base: push!, length, eltype, show

struct Curve2{T}
vertices::Vector{SVector{2,T}}
vertices::Vector{T}
end
Curve2(::Type{T}) where {T} = Curve2(SVector{2,T}[])
Curve2(::Type{T}) where {T} = Curve2(T[])
show(io::IO, ::MIME"text/plain", c2::Curve2) = write(io, "$(typeof(c2))\n with $(length(c2.vertices)-1) vertices")
show(io::IO, ::MIME"text/plain", c2s::Vector{Curve2{T}}) where {T} = write(io, "$(typeof(c2s))\n $(length(c2s)) contour line(s)")

struct ContourLevel{T}
level::T
struct ContourLevel{T, L}
level::L
lines::Vector{Curve2{T}}
end
ContourLevel(h::T) where {T <: AbstractFloat} = ContourLevel(h, Curve2{T}[])
ContourLevel(h::T) where {T <: AbstractFloat} = ContourLevel(h, Curve2{NTuple{2,T}}[])
ContourLevel(h::T) where {T} = ContourLevel(Float64(h))
show(io::IO, ::MIME"text/plain", cl::ContourLevel) = write(io, "$(typeof(cl))\n at $(level(cl)) with $(length(lines(cl))) line(s)")
show(io::IO, ::MIME"text/plain", cls::Vector{ContourLevel{T}}) where {T} = write(io, "$(typeof(cls))\n $(length(cls)) contour level(s)")
Expand Down Expand Up @@ -62,11 +60,12 @@ argument `level`.
You'll usually call [`lines`](@ref) on the output of `contour`, and then iterate
over the result.
"""
function contour(x, y, z, level::Number)
function contour(x, y, z, level::Number; VT=nothing)
if !(axes(x) == (axes(z,1),) && axes(y) == (axes(z,2),) || axes(x) == axes(y) == axes(z))
throw(ArgumentError("Incompatible input axes in `Contour.contour`."))
end
trace_contour(x, y, z, level, get_level_cells(z, level))
VT = VT === nothing ? NTuple{2,promote_type(map(eltype, (x, y, z))...)} : VT
trace_contour(x, y, z, level, get_level_cells(z, level), VT)
end

"""
Expand Down Expand Up @@ -111,8 +110,9 @@ a tuple of lists.
"""
function coordinates(c::Curve2{T}) where {T}
N = length(c.vertices)
xlist = Vector{T}(undef, N)
ylist = Vector{T}(undef, N)
E = eltype(T)
xlist = Vector{E}(undef, N)
ylist = Vector{E}(undef, N)

for (i, v) in enumerate(c.vertices)
xlist[i] = v[1]
Expand Down Expand Up @@ -264,17 +264,15 @@ function chase!(cells, curve, x, y, z, h, start, entry_edge, xi_range, yi_range,
end


function trace_contour(x, y, z, h::Number, cells::Dict)
function trace_contour(x, y, z, h::Number, cells::Dict, VT)

contours = ContourLevel(h)
contours = ContourLevel(h, Curve2{VT}[])

x_ax, y_ax = axes(z)
xi_range = first(x_ax):last(x_ax)-1
yi_range = first(y_ax):last(y_ax)-1


VT = SVector{2,promote_type(map(eltype, (x, y, z))...)}

# When tracing out contours, this algorithm picks an arbitrary
# starting cell, then first follows the contour in one direction
# until it either ends up where it started # or at one of the boundaries.
Expand Down
6 changes: 3 additions & 3 deletions src/interpolate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function interpolate(x, y, z::AbstractMatrix, h::Number, ind, edge::UInt8, ::Typ
x_interp = x[xi] + (x[xi + 1] - x[xi]) * (h - z[xi, yi]) / (z[xi + 1, yi] - z[xi, yi])
end

return VT(x_interp, y_interp)
return VT <: Tuple ? (x_interp, y_interp) : VT(x_interp, y_interp)
end

function interpolate(x::AbstractRange, y::AbstractRange, z::AbstractMatrix, h::Number, ind, edge::UInt8, ::Type{VT}) where {VT}
Expand All @@ -36,7 +36,7 @@ function interpolate(x::AbstractRange, y::AbstractRange, z::AbstractMatrix, h::N
x_interp = x[xi] + step(x) * (h - z[xi, yi]) / (z[xi + 1, yi] - z[xi, yi])
end

return VT(x_interp, y_interp)
return VT <: Tuple ? (x_interp, y_interp) : VT(x_interp, y_interp)
end

function interpolate(x::AbstractMatrix, y::AbstractMatrix, z::AbstractMatrix, h::Number, ind, edge::UInt8, ::Type{VT}) where {VT}
Expand All @@ -59,5 +59,5 @@ function interpolate(x::AbstractMatrix, y::AbstractMatrix, z::AbstractMatrix, h:
x_interp = x[xi,yi] + Δ[2]
end

return VT(x_interp, y_interp)
return VT <: Tuple ? (x_interp, y_interp) : VT(x_interp, y_interp)
end
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using Contour, Test

@test length(detect_ambiguities(Base, Contour)) == 0

include("verify_vertices.jl")
include("interface.jl")

# @show detect_ambiguities(Base, Contour) # should be zero but there are a few stragglers in the imports

#issue 59
@inferred collect(())
22 changes: 11 additions & 11 deletions test/verify_vertices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Y = collect(0:Δ:3) .+ φ
Z = [(x^2 + y^2)::Float64 for x in X, y in Y]
h = rand() * (maximum(Z) - minimum(Z)) + minimum(Z)

contourlevels = Contour.contour(X, Y, Z, h)
contourlevels = Contour.contour(X, Y, Z, h; VT=SVector{2,Float64})
for line in contourlevels.lines
# Contour vertices lie on a circle around the origin
for v in line.vertices
Expand All @@ -42,7 +42,7 @@ h = 1
x0, y0 = (.5, -1.5)
Z = Float64[(x - x0)^2 + (y - y0)^2 for x in X, y in Y]

contourlevels = Contour.contour(X, Y, Z, h)
contourlevels = Contour.contour(X, Y, Z, h, VT=SVector{2,Float64})
for line in contourlevels.lines
for v in line.vertices
@test isapprox((v[1] - x0)^2 + (v[2] - y0)^2, h, atol=0.01Δ)
Expand All @@ -65,7 +65,7 @@ lines = Contour.contour(X, Y, Z, h).lines

for line in lines
@test length(line.vertices) == 2
d = line.vertices[2] - line.vertices[1]
d = line.vertices[2] .- line.vertices[1]
@test d[2] / d[1] ≈ -1.0
end

Expand All @@ -80,7 +80,7 @@ lines = Contour.contour(X, Y, Z, h).lines

for line in lines
@test length(line.vertices) == 2
d = line.vertices[2] - line.vertices[1]
d = line.vertices[2] .- line.vertices[1]
@test d[2] / d[1] ≈ 1.0
end

Expand All @@ -94,7 +94,7 @@ lines = Contour.contour(X, Y, Z, h).lines

for line in lines
@test length(line.vertices) == 2
d = line.vertices[2] - line.vertices[1]
d = line.vertices[2] .- line.vertices[1]
@test d[2] / d[1] ≈ 1.0
end

Expand All @@ -104,12 +104,12 @@ Z = float([0 1;
1 0])
h = 0.9

lines = Contour.contour(X, Y, Z, h).lines
lines = Contour.contour(X, Y, Z, h, VT=SVector{2, Float64}).lines
@test length(lines) == 2

for line in lines
@test length(line.vertices) == 2
d = line.vertices[2] - line.vertices[1]
d = line.vertices[2] .- line.vertices[1]
@test d[2] / d[1] ≈ -1.0
end

Expand All @@ -120,7 +120,7 @@ R = range(1.0, stop=2.0, length=100)
x, y, z = real.(ζ), imag.(ζ), abs.(ζ)

h = 1 + rand()
xs, ys = coordinates(contour(x, y, z, h).lines[1])
xs, ys = coordinates(contour(x, y, z, h, VT=SVector{2, Float64}).lines[1])
@test all(xs.^2 + ys.^2 .≈ h^2)


Expand All @@ -131,8 +131,8 @@ zoff = OffsetArray(z, offset_x, offset_y)

x, y = axes(z)
xoff, yoff = axes(zoff)
curves = Contour.contour(x,y,z,0.5)
curves_off = Contour.contour(xoff, yoff, zoff, 0.5)
curves = Contour.contour(x,y,z,0.5, VT=SVector{2, Float64})
curves_off = Contour.contour(xoff, yoff, zoff, 0.5,VT=SVector{2, Float64})

# sort offset and non-offset curves to the same order
offset = SVector(offset_x, offset_y)
Expand Down Expand Up @@ -172,7 +172,7 @@ Y = collect(-3:Δ:3)
Z = [(x^2 - y^2)::Float64 for x in X, y in Y]
h = rand() * (maximum(Z) - minimum(Z)) + minimum(Z)

contourlevels = Contour.contour(X, Y, Z, h)
contourlevels = Contour.contour(X, Y, Z, h) #, VT=SVector{2, Float64})
for line in contourlevels.lines
# Contour vertices lie on a circle around the origin
for v in line.vertices
Expand Down