diff --git a/.gitignore b/.gitignore index ccb1692..de8079e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.jl.mem *.svg Manifest.toml +test/data/*-comparison.png diff --git a/src/lines.jl b/src/lines.jl index 2122483..4589641 100644 --- a/src/lines.jl +++ b/src/lines.jl @@ -9,12 +9,11 @@ function graphline(g, locs_x, locs_y, nodesize::Vector{T}, arrowlength, angleoff j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) + θ = atan(Δy,Δx) startx = locs_x[i] + nodesize[i]*cos(θ) starty = locs_y[i] + nodesize[i]*sin(θ) - endx = locs_x[i] + (d-nodesize[j])*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize[j])*1.00*sin(θ) + endx = locs_x[j] + nodesize[j]*cos(θ+π) + endy = locs_y[j] + nodesize[j]*sin(θ+π) lines[e_idx] = [(startx, starty), (endx, endy)] arr1, arr2 = arrowcoords(θ, endx, endy, arrowlength, angleoffset) arrows[e_idx] = [arr1, (endx, endy), arr2] @@ -30,12 +29,11 @@ function graphline(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Real, arrowlen j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) + θ = atan(Δy,Δx) startx = locs_x[i] + nodesize*cos(θ) starty = locs_y[i] + nodesize*sin(θ) - endx = locs_x[i] + (d-nodesize)*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize)*1.00*sin(θ) + endx = locs_x[j] + nodesize*cos(θ+π) + endy = locs_y[j] + nodesize*sin(θ+π) lines[e_idx] = [(startx, starty), (endx, endy)] arr1, arr2 = arrowcoords(θ, endx, endy, arrowlength, angleoffset) arrows[e_idx] = [arr1, (endx, endy), arr2] @@ -50,12 +48,11 @@ function graphline(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Vector{<:Real} j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) + θ = atan(Δy,Δx) startx = locs_x[i] + nodesize[i]*cos(θ) starty = locs_y[i] + nodesize[i]*sin(θ) - endx = locs_x[i] + (d-nodesize[j])*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize[j])*1.00*sin(θ) + endx = locs_x[j] + nodesize[j]*cos(θ+π) + endy = locs_y[j] + nodesize[j]*sin(θ+π) lines[e_idx] = [(startx, starty), (endx, endy)] end lines @@ -68,103 +65,118 @@ function graphline(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Real) where {T j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) + θ = atan(Δy,Δx) startx = locs_x[i] + nodesize*cos(θ) starty = locs_y[i] + nodesize*sin(θ) - endx = locs_x[i] + (d-nodesize)*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize)*1.00*sin(θ) + endx = locs_x[j] + nodesize*cos(θ+π) + endy = locs_y[j] + nodesize*sin(θ+π) lines[e_idx] = [(startx, starty), (endx, endy)] end return lines end function graphcurve(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Vector{<:Real}, arrowlength, angleoffset, outangle=pi/5) where {T<:Integer} - lines = Array{Vector}(ne(g)) + curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4) arrows = Array{Vector{Tuple{Float64,Float64}}}(undef, ne(g)) for (e_idx, e) in enumerate(edges(g)) i = src(e) j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) - startx = locs_x[i] + nodesize[i]*cos(θ) - starty = locs_y[i] + nodesize[i]*sin(θ) - endx = locs_x[i] + (d-nodesize[j])*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize[j])*1.00*sin(θ) - lines[e_idx] = curveedge(startx, starty, endx, endy, outangle) - if startx <= endx - arr1, arr2 = curvearrowcoords1(θ, outangle, endx, endy, arrowlength, angleoffset) - arrows[e_idx] = [arr1, (endx, endy), arr2] - else - arr1, arr2 = curvearrowcoords2(θ, outangle, endx, endy, arrowlength, angleoffset) - arrows[e_idx] = [arr1, (endx, endy), arr2] + θ = atan(Δy,Δx) + startx = locs_x[i] + nodesize[i]*cos(θ+outangle) + starty = locs_y[i] + nodesize[i]*sin(θ+outangle) + endx = locs_x[j] + nodesize[j]*cos(θ+π-outangle) + endy = locs_y[j] + nodesize[j]*sin(θ+π-outangle) + + d = hypot(endx-startx, endy-starty) + + if i == j + d = 2 * π * nodesize[i] end + + curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d) + + arr1, arr2 = arrowcoords(θ-outangle, endx, endy, arrowlength, angleoffset) + arrows[e_idx] = [arr1, (endx, endy), arr2] end - return lines, arrows + return curves, arrows end function graphcurve(g, locs_x, locs_y, nodesize::Real, arrowlength, angleoffset, outangle=pi/5) - lines = Array{Vector}(ne(g)) + curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4) arrows = Array{Vector{Tuple{Float64,Float64}}}(undef, ne(g)) for (e_idx, e) in enumerate(edges(g)) i = src(e) j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) - startx = locs_x[i] + nodesize*cos(θ) - starty = locs_y[i] + nodesize*sin(θ) - endx = locs_x[i] + (d-nodesize)*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize)*1.00*sin(θ) - lines[e_idx] = curveedge(startx, starty, endx, endy, outangle) - if startx <= endx - arr1, arr2 = curvearrowcoords1(θ, outangle, endx, endy, arrowlength, angleoffset) - arrows[e_idx] = [arr1, (endx, endy), arr2] - else - arr1, arr2 = curvearrowcoords2(θ, outangle, endx, endy, arrowlength, angleoffset) - arrows[e_idx] = [arr1, (endx, endy), arr2] + θ = atan(Δy,Δx) + startx = locs_x[i] + nodesize*cos(θ+outangle) + starty = locs_y[i] + nodesize*sin(θ+outangle) + endx = locs_x[j] + nodesize*cos(θ+π-outangle) + endy = locs_y[j] + nodesize*sin(θ+π-outangle) + + d = hypot(endx-startx, endy-starty) + + if i == j + d = 2 * π * nodesize end + + curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d) + + arr1, arr2 = arrowcoords(θ-outangle, endx, endy, arrowlength, angleoffset) + arrows[e_idx] = [arr1, (endx, endy), arr2] end - return lines, arrows + return curves, arrows end function graphcurve(g, locs_x, locs_y, nodesize::Real, outangle) - lines = Array{Vector}(ne(g)) + curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4) for (e_idx, e) in enumerate(edges(g)) i = src(e) j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) - startx = locs_x[i] + nodesize*cos(θ) - starty = locs_y[i] + nodesize*sin(θ) - endx = locs_x[i] + (d-nodesize)*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize)*1.00*sin(θ) - lines[e_idx] = curveedge(startx, starty, endx, endy, outangle) + θ = atan(Δy,Δx) + startx = locs_x[i] + nodesize*cos(θ+outangle) + starty = locs_y[i] + nodesize*sin(θ+outangle) + endx = locs_x[j] + nodesize*cos(θ+π-outangle) + endy = locs_y[j] + nodesize*sin(θ+π-outangle) + + d = hypot(endx-startx, endy-starty) + + if i == j + d = 2 * π * nodesize + end + + curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d) end - lines + return curves end function graphcurve(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Vector{<:Real}, outangle) where {T<:Integer} - lines = Array{Vector}(ne(g)) + curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4) for (e_idx, e) in enumerate(edges(g)) i = src(e) j = dst(e) Δx = locs_x[j] - locs_x[i] Δy = locs_y[j] - locs_y[i] - d = sqrt(Δx^2 + Δy^2) - θ = atan(Δy,Δx) - startx = locs_x[i] + nodesize*cos(θ) - starty = locs_y[i] + nodesize*sin(θ) - endx = locs_x[i] + (d-nodesize)*1.00*cos(θ) - endy = locs_y[i] + (d-nodesize)*1.00*sin(θ) - lines[e_idx] = curveedge(startx, starty, endx, endy, outangle) + θ = atan(Δy,Δx) + startx = locs_x[i] + nodesize[i]*cos(θ+outangle) + starty = locs_y[i] + nodesize[i]*sin(θ+outangle) + endx = locs_x[j] + nodesize[j]*cos(θ+π-outangle) + endy = locs_y[j] + nodesize[j]*sin(θ+π-outangle) + + d = hypot(endx-startx, endy-starty) + + if i == j + d = 2 * π * nodesize[i] + end + + curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d) end - return lines + return curves end # this function is copy from [IainNZ](https://github.com/IainNZ)'s [GraphLayout.jl](https://github.com/IainNZ/GraphLayout.jl) @@ -176,36 +188,16 @@ function arrowcoords(θ, endx, endy, arrowlength, angleoffset=20.0/180.0*π) return (arr1x, arr1y), (arr2x, arr2y) end -# using when startx <= endx -# θ1 is the angle between line from start point to end point with x axis -# θ2 is the out angle of edge -function curvearrowcoords1(θ1, θ2, endx, endy, arrowlength, angleoffset=20.0/180.0*π) - arr1x = endx + arrowlength*cos(pi+θ1-θ2-angleoffset) - arr1y = endy + arrowlength*sin(pi+θ1-θ2-angleoffset) - arr2x = endx + arrowlength*cos(pi+θ1-θ2+angleoffset) - arr2y = endy + arrowlength*sin(pi+θ1-θ2+angleoffset) - return (arr1x, arr1y), (arr2x, arr2y) -end +function curveedge(x1, y1, x2, y2, θ, outangle, d; k=0.5) -# using when startx > endx -function curvearrowcoords2(θ1, θ2, endx, endy, arrowlength, angleoffset=20.0/180.0*π) - arr1x = endx + arrowlength*cos(pi+θ1+θ2-angleoffset) - arr1y = endy + arrowlength*sin(pi+θ1+θ2-angleoffset) - arr2x = endx + arrowlength*cos(pi+θ1+θ2+angleoffset) - arr2y = endy + arrowlength*sin(pi+θ1+θ2+angleoffset) - return (arr1x, arr1y), (arr2x, arr2y) -end + r = d * k -function curveedge(x1, y1, x2, y2, θ) - θ1 = atan((y2-y1)/(x2-x1)) - d = sqrt((x2-x1)^2+(y2-y1)^2) - r = d/2cos(θ) - if x1 <= x2 - x = x1 + r*cos(θ+θ1) - y = y1 + r*sin(θ+θ1) - else - x = x2 + r*cos(θ+θ1) - y = y2 + r*sin(θ+θ1) - end - return [:M, x1,y1, :Q, x, y, x2, y2] + # Control points for left bending curve. + xc1 = x1 + r * cos(θ + outangle) + yc1 = y1 + r * sin(θ + outangle) + + xc2 = x2 + r * cos(θ + π - outangle) + yc2 = y2 + r * sin(θ + π - outangle) + + return [(x1,y1) (xc1, yc1) (xc2, yc2) (x2, y2)] end diff --git a/src/plot.jl b/src/plot.jl index 6422589..73b34a8 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -195,12 +195,12 @@ function gplot(g::AbstractGraph{T}, lines, arrows = nothing, nothing if linetype == "curve" if arrowlengthfrac > 0.0 - lines_cord, arrows_cord = graphcurve(g, locs_x, locs_y, nodesize, arrowlengthfrac, arrowangleoffset, outangle) - lines = path(lines_cord) + curves_cord, arrows_cord = graphcurve(g, locs_x, locs_y, nodesize, arrowlengthfrac, arrowangleoffset, outangle) + lines = curve(curves_cord[:,1], curves_cord[:,2], curves_cord[:,3], curves_cord[:,4]) arrows = line(arrows_cord) else - lines_cord = graphcurve(g, locs_x, locs_y, nodesize, outangle) - lines = path(lines_cord) + curves_cord = graphcurve(g, locs_x, locs_y, nodesize, outangle) + lines = curve(curves_cord[:,1], curves_cord[:,2], curves_cord[:,3], curves_cord[:,4]) end else if arrowlengthfrac > 0.0 diff --git a/test/data/curve.png b/test/data/curve.png new file mode 100644 index 0000000..668ca01 Binary files /dev/null and b/test/data/curve.png differ diff --git a/test/data/self_directed.png b/test/data/self_directed.png new file mode 100644 index 0000000..1eb1177 Binary files /dev/null and b/test/data/self_directed.png differ diff --git a/test/runtests.jl b/test/runtests.jl index 82a5ef3..7e5327f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,6 +10,7 @@ using Compose using Random using Test using VisualRegressionTests +using ImageMagick # global variables istravis = "TRAVIS" ∈ keys(ENV) @@ -48,6 +49,13 @@ function plot_and_save(fname, g; gplot_kwargs...) draw(PNG(fname, 8inch, 8inch), gplot(g; layout=test_layout, gplot_kwargs...)) end +function save_comparison(result::VisualTestResult) + grid = hcat(result.refImage, result.testImage) + path = joinpath(datadir, string(basename(result.refFilename)[1:end-length(".png")], "-comparison.png")) + ImageMagick.save(path, grid) + return result +end + @testset "Karate Net" begin # auxiliary variables nodelabel = collect(1:nv(g)) @@ -56,12 +64,12 @@ end # test nodesize plot_and_save1(fname) = plot_and_save(fname, g, nodesize=nodesize.^0.3, nodelabel=nodelabel, nodelabelsize=nodesize.^0.3) refimg1 = joinpath(datadir, "karate_different_nodesize.png") - @test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> success + @test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> save_comparison |> success # test directed graph plot_and_save2(fname) = plot_and_save(fname, g, arrowlengthfrac=0.02, nodelabel=nodelabel) refimg2 = joinpath(datadir, "karate_straight_directed.png") - @test test_images(VisualTest(plot_and_save2, refimg2), popup=!istravis) |> success + @test test_images(VisualTest(plot_and_save2, refimg2), popup=!istravis) |> save_comparison |> success # test node membership membership = [1,1,1,1,1,1,1,1,2,1,1,1,1,1,2,2,1,1,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2] @@ -69,12 +77,33 @@ end nodefillc = nodecolor[membership] plot_and_save3(fname) = plot_and_save(fname, g, nodelabel=nodelabel, nodefillc=nodefillc) refimg3 = joinpath(datadir, "karate_groups.png") - @test test_images(VisualTest(plot_and_save3, refimg3), popup=!istravis) |> success + @test test_images(VisualTest(plot_and_save3, refimg3), popup=!istravis) |> save_comparison |> success end @testset "WheelGraph" begin # default options plot_and_save1(fname) = plot_and_save(fname, h) refimg1 = joinpath(datadir, "wheel10.png") - @test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> success + @test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> save_comparison |> success +end + +@testset "Curves" begin + + g2 = DiGraph(2) + add_edge!(g2, 1,2) + add_edge!(g2, 2,1) + + plot_and_save1(fname) = plot_and_save(fname, g2, linetype="curve") + refimg1 = joinpath(datadir, "curve.png") + @test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> save_comparison |> success + + g3 = DiGraph(2) + add_edge!(g3, 1,1) + add_edge!(g3, 1,2) + add_edge!(g3, 2,1) + + plot_and_save2(fname) = plot_and_save(fname, g3, linetype="curve") + refimg2 = joinpath(datadir, "self_directed.png") + @test test_images(VisualTest(plot_and_save2, refimg2), popup=!istravis) |> save_comparison |> success + end