Skip to content

Commit 534a9e6

Browse files
Merge pull request #90 from CiaranOMara/hotfix/curve
Fixes a bug, where linetype = "curve" did not work and adds the ability to draw self-loops.
2 parents 6458908 + dea58c9 commit 534a9e6

File tree

6 files changed

+124
-102
lines changed

6 files changed

+124
-102
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
*.jl.mem
33
*.svg
44
Manifest.toml
5+
test/data/*-comparison.png

src/lines.jl

Lines changed: 86 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ function graphline(g, locs_x, locs_y, nodesize::Vector{T}, arrowlength, angleoff
99
j = dst(e)
1010
Δx = locs_x[j] - locs_x[i]
1111
Δy = locs_y[j] - locs_y[i]
12-
d = sqrt(Δx^2 + Δy^2)
13-
θ = atan(Δy,Δx)
12+
θ = atan(Δy,Δx)
1413
startx = locs_x[i] + nodesize[i]*cos(θ)
1514
starty = locs_y[i] + nodesize[i]*sin(θ)
16-
endx = locs_x[i] + (d-nodesize[j])*1.00*cos(θ)
17-
endy = locs_y[i] + (d-nodesize[j])*1.00*sin(θ)
15+
endx = locs_x[j] + nodesize[j]*cos+π)
16+
endy = locs_y[j] + nodesize[j]*sin+π)
1817
lines[e_idx] = [(startx, starty), (endx, endy)]
1918
arr1, arr2 = arrowcoords(θ, endx, endy, arrowlength, angleoffset)
2019
arrows[e_idx] = [arr1, (endx, endy), arr2]
@@ -30,12 +29,11 @@ function graphline(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Real, arrowlen
3029
j = dst(e)
3130
Δx = locs_x[j] - locs_x[i]
3231
Δy = locs_y[j] - locs_y[i]
33-
d = sqrt(Δx^2 + Δy^2)
34-
θ = atan(Δy,Δx)
32+
θ = atan(Δy,Δx)
3533
startx = locs_x[i] + nodesize*cos(θ)
3634
starty = locs_y[i] + nodesize*sin(θ)
37-
endx = locs_x[i] + (d-nodesize)*1.00*cos(θ)
38-
endy = locs_y[i] + (d-nodesize)*1.00*sin(θ)
35+
endx = locs_x[j] + nodesize*cos+π)
36+
endy = locs_y[j] + nodesize*sin+π)
3937
lines[e_idx] = [(startx, starty), (endx, endy)]
4038
arr1, arr2 = arrowcoords(θ, endx, endy, arrowlength, angleoffset)
4139
arrows[e_idx] = [arr1, (endx, endy), arr2]
@@ -50,12 +48,11 @@ function graphline(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Vector{<:Real}
5048
j = dst(e)
5149
Δx = locs_x[j] - locs_x[i]
5250
Δy = locs_y[j] - locs_y[i]
53-
d = sqrt(Δx^2 + Δy^2)
54-
θ = atan(Δy,Δx)
51+
θ = atan(Δy,Δx)
5552
startx = locs_x[i] + nodesize[i]*cos(θ)
5653
starty = locs_y[i] + nodesize[i]*sin(θ)
57-
endx = locs_x[i] + (d-nodesize[j])*1.00*cos(θ)
58-
endy = locs_y[i] + (d-nodesize[j])*1.00*sin(θ)
54+
endx = locs_x[j] + nodesize[j]*cos+π)
55+
endy = locs_y[j] + nodesize[j]*sin+π)
5956
lines[e_idx] = [(startx, starty), (endx, endy)]
6057
end
6158
lines
@@ -68,103 +65,118 @@ function graphline(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Real) where {T
6865
j = dst(e)
6966
Δx = locs_x[j] - locs_x[i]
7067
Δy = locs_y[j] - locs_y[i]
71-
d = sqrt(Δx^2 + Δy^2)
72-
θ = atan(Δy,Δx)
68+
θ = atan(Δy,Δx)
7369
startx = locs_x[i] + nodesize*cos(θ)
7470
starty = locs_y[i] + nodesize*sin(θ)
75-
endx = locs_x[i] + (d-nodesize)*1.00*cos(θ)
76-
endy = locs_y[i] + (d-nodesize)*1.00*sin(θ)
71+
endx = locs_x[j] + nodesize*cos+π)
72+
endy = locs_y[j] + nodesize*sin+π)
7773
lines[e_idx] = [(startx, starty), (endx, endy)]
7874
end
7975
return lines
8076
end
8177

8278
function graphcurve(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Vector{<:Real}, arrowlength, angleoffset, outangle=pi/5) where {T<:Integer}
83-
lines = Array{Vector}(ne(g))
79+
curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4)
8480
arrows = Array{Vector{Tuple{Float64,Float64}}}(undef, ne(g))
8581
for (e_idx, e) in enumerate(edges(g))
8682
i = src(e)
8783
j = dst(e)
8884
Δx = locs_x[j] - locs_x[i]
8985
Δy = locs_y[j] - locs_y[i]
90-
d = sqrt(Δx^2 + Δy^2)
91-
θ = atan(Δy,Δx)
92-
startx = locs_x[i] + nodesize[i]*cos(θ)
93-
starty = locs_y[i] + nodesize[i]*sin(θ)
94-
endx = locs_x[i] + (d-nodesize[j])*1.00*cos(θ)
95-
endy = locs_y[i] + (d-nodesize[j])*1.00*sin(θ)
96-
lines[e_idx] = curveedge(startx, starty, endx, endy, outangle)
97-
if startx <= endx
98-
arr1, arr2 = curvearrowcoords1(θ, outangle, endx, endy, arrowlength, angleoffset)
99-
arrows[e_idx] = [arr1, (endx, endy), arr2]
100-
else
101-
arr1, arr2 = curvearrowcoords2(θ, outangle, endx, endy, arrowlength, angleoffset)
102-
arrows[e_idx] = [arr1, (endx, endy), arr2]
86+
θ = atan(Δy,Δx)
87+
startx = locs_x[i] + nodesize[i]*cos+outangle)
88+
starty = locs_y[i] + nodesize[i]*sin+outangle)
89+
endx = locs_x[j] + nodesize[j]*cos+π-outangle)
90+
endy = locs_y[j] + nodesize[j]*sin+π-outangle)
91+
92+
d = hypot(endx-startx, endy-starty)
93+
94+
if i == j
95+
d = 2 * π * nodesize[i]
10396
end
97+
98+
curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d)
99+
100+
arr1, arr2 = arrowcoords-outangle, endx, endy, arrowlength, angleoffset)
101+
arrows[e_idx] = [arr1, (endx, endy), arr2]
104102
end
105-
return lines, arrows
103+
return curves, arrows
106104
end
107105

108106
function graphcurve(g, locs_x, locs_y, nodesize::Real, arrowlength, angleoffset, outangle=pi/5)
109-
lines = Array{Vector}(ne(g))
107+
curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4)
110108
arrows = Array{Vector{Tuple{Float64,Float64}}}(undef, ne(g))
111109
for (e_idx, e) in enumerate(edges(g))
112110
i = src(e)
113111
j = dst(e)
114112
Δx = locs_x[j] - locs_x[i]
115113
Δy = locs_y[j] - locs_y[i]
116-
d = sqrt(Δx^2 + Δy^2)
117-
θ = atan(Δy,Δx)
118-
startx = locs_x[i] + nodesize*cos(θ)
119-
starty = locs_y[i] + nodesize*sin(θ)
120-
endx = locs_x[i] + (d-nodesize)*1.00*cos(θ)
121-
endy = locs_y[i] + (d-nodesize)*1.00*sin(θ)
122-
lines[e_idx] = curveedge(startx, starty, endx, endy, outangle)
123-
if startx <= endx
124-
arr1, arr2 = curvearrowcoords1(θ, outangle, endx, endy, arrowlength, angleoffset)
125-
arrows[e_idx] = [arr1, (endx, endy), arr2]
126-
else
127-
arr1, arr2 = curvearrowcoords2(θ, outangle, endx, endy, arrowlength, angleoffset)
128-
arrows[e_idx] = [arr1, (endx, endy), arr2]
114+
θ = atan(Δy,Δx)
115+
startx = locs_x[i] + nodesize*cos+outangle)
116+
starty = locs_y[i] + nodesize*sin+outangle)
117+
endx = locs_x[j] + nodesize*cos+π-outangle)
118+
endy = locs_y[j] + nodesize*sin+π-outangle)
119+
120+
d = hypot(endx-startx, endy-starty)
121+
122+
if i == j
123+
d = 2 * π * nodesize
129124
end
125+
126+
curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d)
127+
128+
arr1, arr2 = arrowcoords-outangle, endx, endy, arrowlength, angleoffset)
129+
arrows[e_idx] = [arr1, (endx, endy), arr2]
130130
end
131-
return lines, arrows
131+
return curves, arrows
132132
end
133133

134134
function graphcurve(g, locs_x, locs_y, nodesize::Real, outangle)
135-
lines = Array{Vector}(ne(g))
135+
curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4)
136136
for (e_idx, e) in enumerate(edges(g))
137137
i = src(e)
138138
j = dst(e)
139139
Δx = locs_x[j] - locs_x[i]
140140
Δy = locs_y[j] - locs_y[i]
141-
d = sqrt(Δx^2 + Δy^2)
142-
θ = atan(Δy,Δx)
143-
startx = locs_x[i] + nodesize*cos(θ)
144-
starty = locs_y[i] + nodesize*sin(θ)
145-
endx = locs_x[i] + (d-nodesize)*1.00*cos(θ)
146-
endy = locs_y[i] + (d-nodesize)*1.00*sin(θ)
147-
lines[e_idx] = curveedge(startx, starty, endx, endy, outangle)
141+
θ = atan(Δy,Δx)
142+
startx = locs_x[i] + nodesize*cos+outangle)
143+
starty = locs_y[i] + nodesize*sin+outangle)
144+
endx = locs_x[j] + nodesize*cos+π-outangle)
145+
endy = locs_y[j] + nodesize*sin+π-outangle)
146+
147+
d = hypot(endx-startx, endy-starty)
148+
149+
if i == j
150+
d = 2 * π * nodesize
151+
end
152+
153+
curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d)
148154
end
149-
lines
155+
return curves
150156
end
151157

152158
function graphcurve(g::AbstractGraph{T}, locs_x, locs_y, nodesize::Vector{<:Real}, outangle) where {T<:Integer}
153-
lines = Array{Vector}(ne(g))
159+
curves = Matrix{Tuple{Float64,Float64}}(undef, ne(g), 4)
154160
for (e_idx, e) in enumerate(edges(g))
155161
i = src(e)
156162
j = dst(e)
157163
Δx = locs_x[j] - locs_x[i]
158164
Δy = locs_y[j] - locs_y[i]
159-
d = sqrt(Δx^2 + Δy^2)
160-
θ = atan(Δy,Δx)
161-
startx = locs_x[i] + nodesize*cos(θ)
162-
starty = locs_y[i] + nodesize*sin(θ)
163-
endx = locs_x[i] + (d-nodesize)*1.00*cos(θ)
164-
endy = locs_y[i] + (d-nodesize)*1.00*sin(θ)
165-
lines[e_idx] = curveedge(startx, starty, endx, endy, outangle)
165+
θ = atan(Δy,Δx)
166+
startx = locs_x[i] + nodesize[i]*cos+outangle)
167+
starty = locs_y[i] + nodesize[i]*sin+outangle)
168+
endx = locs_x[j] + nodesize[j]*cos+π-outangle)
169+
endy = locs_y[j] + nodesize[j]*sin+π-outangle)
170+
171+
d = hypot(endx-startx, endy-starty)
172+
173+
if i == j
174+
d = 2 * π * nodesize[i]
175+
end
176+
177+
curves[e_idx, :] = curveedge(startx, starty, endx, endy, θ, outangle, d)
166178
end
167-
return lines
179+
return curves
168180
end
169181

170182
# 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*π)
176188
return (arr1x, arr1y), (arr2x, arr2y)
177189
end
178190

179-
# using when startx <= endx
180-
# θ1 is the angle between line from start point to end point with x axis
181-
# θ2 is the out angle of edge
182-
function curvearrowcoords1(θ1, θ2, endx, endy, arrowlength, angleoffset=20.0/180.0*π)
183-
arr1x = endx + arrowlength*cos(pi+θ1-θ2-angleoffset)
184-
arr1y = endy + arrowlength*sin(pi+θ1-θ2-angleoffset)
185-
arr2x = endx + arrowlength*cos(pi+θ1-θ2+angleoffset)
186-
arr2y = endy + arrowlength*sin(pi+θ1-θ2+angleoffset)
187-
return (arr1x, arr1y), (arr2x, arr2y)
188-
end
191+
function curveedge(x1, y1, x2, y2, θ, outangle, d; k=0.5)
189192

190-
# using when startx > endx
191-
function curvearrowcoords2(θ1, θ2, endx, endy, arrowlength, angleoffset=20.0/180.0*π)
192-
arr1x = endx + arrowlength*cos(pi+θ1+θ2-angleoffset)
193-
arr1y = endy + arrowlength*sin(pi+θ1+θ2-angleoffset)
194-
arr2x = endx + arrowlength*cos(pi+θ1+θ2+angleoffset)
195-
arr2y = endy + arrowlength*sin(pi+θ1+θ2+angleoffset)
196-
return (arr1x, arr1y), (arr2x, arr2y)
197-
end
193+
r = d * k
198194

199-
function curveedge(x1, y1, x2, y2, θ)
200-
θ1 = atan((y2-y1)/(x2-x1))
201-
d = sqrt((x2-x1)^2+(y2-y1)^2)
202-
r = d/2cos(θ)
203-
if x1 <= x2
204-
x = x1 + r*cos+θ1)
205-
y = y1 + r*sin+θ1)
206-
else
207-
x = x2 + r*cos+θ1)
208-
y = y2 + r*sin+θ1)
209-
end
210-
return [:M, x1,y1, :Q, x, y, x2, y2]
195+
# Control points for left bending curve.
196+
xc1 = x1 + r * cos+ outangle)
197+
yc1 = y1 + r * sin+ outangle)
198+
199+
xc2 = x2 + r * cos+ π - outangle)
200+
yc2 = y2 + r * sin+ π - outangle)
201+
202+
return [(x1,y1) (xc1, yc1) (xc2, yc2) (x2, y2)]
211203
end

src/plot.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,12 @@ function gplot(g::AbstractGraph{T},
195195
lines, arrows = nothing, nothing
196196
if linetype == "curve"
197197
if arrowlengthfrac > 0.0
198-
lines_cord, arrows_cord = graphcurve(g, locs_x, locs_y, nodesize, arrowlengthfrac, arrowangleoffset, outangle)
199-
lines = path(lines_cord)
198+
curves_cord, arrows_cord = graphcurve(g, locs_x, locs_y, nodesize, arrowlengthfrac, arrowangleoffset, outangle)
199+
lines = curve(curves_cord[:,1], curves_cord[:,2], curves_cord[:,3], curves_cord[:,4])
200200
arrows = line(arrows_cord)
201201
else
202-
lines_cord = graphcurve(g, locs_x, locs_y, nodesize, outangle)
203-
lines = path(lines_cord)
202+
curves_cord = graphcurve(g, locs_x, locs_y, nodesize, outangle)
203+
lines = curve(curves_cord[:,1], curves_cord[:,2], curves_cord[:,3], curves_cord[:,4])
204204
end
205205
else
206206
if arrowlengthfrac > 0.0

test/data/curve.png

33.6 KB
Loading

test/data/self_directed.png

36.1 KB
Loading

test/runtests.jl

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ using Compose
1010
using Random
1111
using Test
1212
using VisualRegressionTests
13+
using ImageMagick
1314

1415
# global variables
1516
istravis = "TRAVIS" keys(ENV)
@@ -48,6 +49,13 @@ function plot_and_save(fname, g; gplot_kwargs...)
4849
draw(PNG(fname, 8inch, 8inch), gplot(g; layout=test_layout, gplot_kwargs...))
4950
end
5051

52+
function save_comparison(result::VisualTestResult)
53+
grid = hcat(result.refImage, result.testImage)
54+
path = joinpath(datadir, string(basename(result.refFilename)[1:end-length(".png")], "-comparison.png"))
55+
ImageMagick.save(path, grid)
56+
return result
57+
end
58+
5159
@testset "Karate Net" begin
5260
# auxiliary variables
5361
nodelabel = collect(1:nv(g))
@@ -56,25 +64,46 @@ end
5664
# test nodesize
5765
plot_and_save1(fname) = plot_and_save(fname, g, nodesize=nodesize.^0.3, nodelabel=nodelabel, nodelabelsize=nodesize.^0.3)
5866
refimg1 = joinpath(datadir, "karate_different_nodesize.png")
59-
@test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> success
67+
@test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> save_comparison |> success
6068

6169
# test directed graph
6270
plot_and_save2(fname) = plot_and_save(fname, g, arrowlengthfrac=0.02, nodelabel=nodelabel)
6371
refimg2 = joinpath(datadir, "karate_straight_directed.png")
64-
@test test_images(VisualTest(plot_and_save2, refimg2), popup=!istravis) |> success
72+
@test test_images(VisualTest(plot_and_save2, refimg2), popup=!istravis) |> save_comparison |> success
6573

6674
# test node membership
6775
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]
6876
nodecolor = [colorant"lightseagreen", colorant"orange"]
6977
nodefillc = nodecolor[membership]
7078
plot_and_save3(fname) = plot_and_save(fname, g, nodelabel=nodelabel, nodefillc=nodefillc)
7179
refimg3 = joinpath(datadir, "karate_groups.png")
72-
@test test_images(VisualTest(plot_and_save3, refimg3), popup=!istravis) |> success
80+
@test test_images(VisualTest(plot_and_save3, refimg3), popup=!istravis) |> save_comparison |> success
7381
end
7482

7583
@testset "WheelGraph" begin
7684
# default options
7785
plot_and_save1(fname) = plot_and_save(fname, h)
7886
refimg1 = joinpath(datadir, "wheel10.png")
79-
@test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> success
87+
@test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> save_comparison |> success
88+
end
89+
90+
@testset "Curves" begin
91+
92+
g2 = DiGraph(2)
93+
add_edge!(g2, 1,2)
94+
add_edge!(g2, 2,1)
95+
96+
plot_and_save1(fname) = plot_and_save(fname, g2, linetype="curve")
97+
refimg1 = joinpath(datadir, "curve.png")
98+
@test test_images(VisualTest(plot_and_save1, refimg1), popup=!istravis) |> save_comparison |> success
99+
100+
g3 = DiGraph(2)
101+
add_edge!(g3, 1,1)
102+
add_edge!(g3, 1,2)
103+
add_edge!(g3, 2,1)
104+
105+
plot_and_save2(fname) = plot_and_save(fname, g3, linetype="curve")
106+
refimg2 = joinpath(datadir, "self_directed.png")
107+
@test test_images(VisualTest(plot_and_save2, refimg2), popup=!istravis) |> save_comparison |> success
108+
80109
end

0 commit comments

Comments
 (0)