diff --git a/Project.toml b/Project.toml index faadf18..c41894f 100644 --- a/Project.toml +++ b/Project.toml @@ -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"] diff --git a/src/Contour.jl b/src/Contour.jl index ca873a4..ecd2c7e 100644 --- a/src/Contour.jl +++ b/src/Contour.jl @@ -1,7 +1,5 @@ module Contour -using StaticArrays - include("interpolate.jl") export @@ -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)") @@ -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 """ @@ -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] @@ -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. diff --git a/src/interpolate.jl b/src/interpolate.jl index ead6cc2..13ba48b 100644 --- a/src/interpolate.jl +++ b/src/interpolate.jl @@ -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} @@ -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} @@ -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 diff --git a/test/runtests.jl b/test/runtests.jl index 58687a2..8eb3c88 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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(()) diff --git a/test/verify_vertices.jl b/test/verify_vertices.jl index b771069..8e182ea 100644 --- a/test/verify_vertices.jl +++ b/test/verify_vertices.jl @@ -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 @@ -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Δ) @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) @@ -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) @@ -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