Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ae22f5d
remove implicit cycling
BeastyBlacksmith Oct 23, 2025
e103325
adjust compat bounds
BeastyBlacksmith Oct 26, 2025
673acfc
fix example 1
BeastyBlacksmith Oct 26, 2025
736dfa4
fix more examples
BeastyBlacksmith Oct 29, 2025
962bc41
cleanup GRExt
BeastyBlacksmith Oct 29, 2025
864613a
cleanup GastonExt
BeastyBlacksmith Oct 29, 2025
3977448
cleanup PGFPlotsXExt
BeastyBlacksmith Oct 29, 2025
6b04ae3
format
BeastyBlacksmith Oct 29, 2025
fee0ee3
cleanup PythonPlotExt
BeastyBlacksmith Oct 29, 2025
c002da1
cleanup Annotations
BeastyBlacksmith Oct 29, 2025
3187017
more cleanups
BeastyBlacksmith Oct 29, 2025
cf9e6c2
finish plotly
BeastyBlacksmith Oct 29, 2025
0778d32
finish cleanup PlotsBase
BeastyBlacksmith Oct 30, 2025
d366568
cleanup StatsPlots
BeastyBlacksmith Oct 30, 2025
51798a0
fix lens recipe
BeastyBlacksmith Oct 30, 2025
f99729f
fix color handling of series
BeastyBlacksmith Oct 30, 2025
98ee31b
enable scalar cycling
BeastyBlacksmith Nov 3, 2025
3dc32e6
improve GraphRecipes tests
BeastyBlacksmith Nov 3, 2025
2956dc7
use using where possible in Subplots
BeastyBlacksmith Nov 3, 2025
c4be23f
fix explicit series attribute retrieval
BeastyBlacksmith Nov 3, 2025
53378b2
fix use of non-existent markerstrokesize
BeastyBlacksmith Nov 3, 2025
64d3a97
fix line_z, etc
BeastyBlacksmith Nov 3, 2025
4535f03
fix GraphRecipes tests
BeastyBlacksmith Nov 3, 2025
3ab43ae
get closer to resolve slicing and cycling interaction
BeastyBlacksmith Nov 3, 2025
0c1b467
make more sense of ribbon tests
BeastyBlacksmith Jan 9, 2026
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
1 change: 1 addition & 0 deletions GraphRecipes/src/GraphRecipes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module GraphRecipes
using Graphs
using PlotUtils # ColorGradient
using RecipesBase
using PlotsBase: PlotsBase, partialcircle

using InteractiveUtils # subtypes
using LinearAlgebra
Expand Down
4 changes: 2 additions & 2 deletions GraphRecipes/src/graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,7 @@ more details.
colorbar_entry --> false
markersize := 0
markeralpha := 0
markerstrokesize := 0
makerstrokewidth := 0
isnothing(edgelabel) || (annotations --> edge_label_array)
else
seriestype := :scatter
Expand All @@ -1153,7 +1153,7 @@ more details.
colorbar_entry --> false
markersize := 0
markeralpha := 0
markerstrokesize := 0
makerstrokewidth := 0
annotations --> edge_label_array
end
end
Expand Down
9 changes: 1 addition & 8 deletions GraphRecipes/src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,8 @@ function process_edge_attribute(attr, source, destiny, weights)
end
return attr
end
# Function from Plots/src/components.jl
"get an array of tuples of points on a circle with radius `r`"
function partialcircle(start_θ, end_θ, n = 20, r = 1)
return Tuple{Float64, Float64}[
(r * cos(u), r * sin(u)) for u in range(start_θ, stop = end_θ, length = n)
]
end

function partialcircle(start_θ, end_θ, circle_center::Array{T, 1}, n = 20, r = 1) where {T}
function PlotsBase.Shapes.partialcircle(start_θ, end_θ, circle_center::Array{T, 1}, n = 20, r = 1) where {T}
return Tuple{Float64, Float64}[
(r * cos(u) + circle_center[1], r * sin(u) + circle_center[2]) for
u in range(start_θ, stop = end_θ, length = n)
Expand Down
4 changes: 4 additions & 0 deletions GraphRecipes/test/functions.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using AbstractTrees
using StableRNGs
using GraphRecipes.Graphs

function random_labelled_graph()
n = 15
rng = StableRNG(1)
Expand Down
2 changes: 1 addition & 1 deletion GraphRecipes/test/parse_readme.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ end
# the installation instructions.
readme_exprs = [Meta.parse("begin $(code_blocks[i]) end") for i in 2:length(code_blocks)]

julia_logo_pun() = eval(readme_exprs[1])
readme_julia_logo_pun() = eval(readme_exprs[1])
137 changes: 52 additions & 85 deletions GraphRecipes/test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
using VisualRegressionTests
using AbstractTrees
using LinearAlgebra
using GraphRecipes
using Logging
using GraphRecipes
using SparseArrays
using ImageMagick
using StableRNGs
using Logging
using Graphs
using Plots
using Plots.PlotsBase
using Test
using Gtk # for popup

const PlotsBase = Plots.PlotsBase

isci() = get(ENV, "CI", "false") == "true"
itol(tol = nothing) = something(tol, isci() ? 1.0e-3 : 1.0e-5)

Expand All @@ -22,45 +20,29 @@ include("parse_readme.jl")

default(show = false, reuse = true)

@testset "functions" begin
rng = StableRNG(1)
for method in keys(GraphRecipes._graph_funcs)
method ≡ :spectral && continue # FIXME
dat = if (inp = GraphRecipes._graph_inputs[method]) ≡ :adjmat
[
0 1 1
1 0 1
1 1 0
]
elseif inp ≡ :sourcedestiny
Symmetric(sparse(rand(rng, 0:1, 8, 8)))
elseif inp ≡ :adjlist
dat = [
0 1 1 0 0 0 0 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 1 0 0 1 0 1 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 1
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
]
else
@error "wrong input $inp"
cd(joinpath(@__DIR__, "..", "assets")) do
@testset "TestImages" begin
figure_files = readdir()
@testset "$figure_file" for figure_file in figure_files
figure = splitext(figure_file)[1]
if figure == "julia_type_tree"
if VERSION >= v"1.11" # julia 1.11 introduced Core.BFloat16
@plottest julia_type_tree() "julia_type_tree.png" popup = !isci() tol = itol()
end
else
@plottest getproperty(@__MODULE__, Symbol(figure))() figure_file popup = !isci() tol =
itol()
end
end
pl = graphplot(dat; method)
@test pl isa PlotsBase.Plot
end
end

@testset "issues" begin
@testset "143" begin
g = SimpleGraph(7)

add_edge!(g, 2, 3)
add_edge!(g, 3, 4)

@test g.ne == 2
al = GraphRecipes.get_adjacency_list(g)
@test isempty(al[1])
Expand Down Expand Up @@ -96,22 +78,22 @@ end
@testset "180" begin
rng = StableRNG(1)
mat = Symmetric(sparse(rand(rng, 0:1, 8, 8)))
graphplot(mat; method = :arcdiagram, rng)
graphplot(mat, method = :arcdiagram, rng = rng)
end
end

@testset "utils.jl" begin
rng = StableRNG(1)
@test GraphRecipes.directed_curve(0.0, 1.0, 0.0, 1.0; rng) ==
GraphRecipes.directed_curve(0, 1, 0, 1; rng)
@test GraphRecipes.directed_curve(0.0, 1.0, 0.0, 1.0, rng = rng) ==
GraphRecipes.directed_curve(0, 1, 0, 1, rng = rng)

@test GraphRecipes.isnothing(nothing) == PlotsBase.isnothing(nothing)
@test GraphRecipes.isnothing(missing) == PlotsBase.isnothing(missing)
@test GraphRecipes.isnothing(NaN) == PlotsBase.isnothing(NaN)
@test GraphRecipes.isnothing(0) == PlotsBase.isnothing(0)
@test GraphRecipes.isnothing(1) == PlotsBase.isnothing(1)
@test GraphRecipes.isnothing(0.0) == PlotsBase.isnothing(0.0)
@test GraphRecipes.isnothing(1.0) == PlotsBase.isnothing(1.0)
@test GraphRecipes.isnothing(nothing) == Plots.isnothing(nothing)
@test GraphRecipes.isnothing(missing) == Plots.isnothing(missing)
@test GraphRecipes.isnothing(NaN) == Plots.isnothing(NaN)
@test GraphRecipes.isnothing(0) == Plots.isnothing(0)
@test GraphRecipes.isnothing(1) == Plots.isnothing(1)
@test GraphRecipes.isnothing(0.0) == Plots.isnothing(0.0)
@test GraphRecipes.isnothing(1.0) == Plots.isnothing(1.0)

for (s, e) in [(rand(rng), rand(rng)) for i in 1:100]
@test GraphRecipes.partialcircle(s, e) == PlotsBase.partialcircle(s, e)
Expand Down Expand Up @@ -144,52 +126,37 @@ end
# checking that they don't error. Also, test all of the different aliases.
@testset "Aliases" begin
A = [1 0 1 0; 0 0 1 1; 1 1 1 1; 0 0 1 1]
graphplot(A; markercolor = :red, markershape = :rect, markersize = 0.5, rng)
graphplot(A; nodeweights = 1:4, rng)
graphplot(A; curvaturescalar = 0, rng)
graphplot(A; el = Dict((1, 2) => ""), elb = true, rng)
graphplot(A; ew = (s, d, w) -> 3, rng)
graphplot(A; ses = 0.5, rng)
graphplot(A, markercolor = :red, markershape = :rect, markersize = 0.5, rng = rng)
graphplot(A, nodeweights = 1:4, rng = rng)
graphplot(A, curvaturescalar = 0, rng = rng)
graphplot(A, el = Dict((1, 2) => ""), elb = true, rng = rng)
graphplot(A, ew = (s, d, w) -> 3, rng = rng)
graphplot(A, ses = 0.5, rng = rng)
end
end

cd(joinpath(@__DIR__, "..", "assets")) do
@testset "FIGURES" begin
@plottest random_labelled_graph() "random_labelled_graph.png" popup = !isci() tol =
itol()

@plottest random_3d_graph() "random_3d_graph.png" popup = !isci() tol = itol()

@plottest light_graphs() "light_graphs.png" popup = !isci() tol = itol()
# -----------------------------------------
# marginalhist

@plottest directed() "directed.png" popup = !isci() tol = itol()
# using Distributions
# n = 1000
# x = rand(RNG, Gamma(2), n)
# y = -0.5x + randn(RNG, n)
# marginalhist(x, y)

@plottest marker_properties() "marker_properties.png" popup = !isci() tol = itol()
# -----------------------------------------
# portfolio composition map

@plottest edgelabel() "edgelabel.png" popup = !isci() tol = itol()
# # fake data
# tickers = ["IBM", "Google", "Apple", "Intel"]
# N = 10
# D = length(tickers)
# weights = rand(RNG, N, D)
# weights ./= sum(weights, 2)
# returns = sort!((1:N) + D*randn(RNG, N))

@plottest selfedges() "selfedges.png" popup = !isci() tol = itol()
# # plot it
# portfoliocomposition(weights, returns, labels = tickers')

@plottest multigraphs() "multigraphs.png" popup = !isci() tol = itol()

@plottest arc_chord_diagrams() "arc_chord_diagrams.png" popup = !isci() tol = itol()

@plottest ast_example() "ast_example.png" popup = !isci() tol = itol()

@plottest julia_type_tree() "julia_type_tree.png" popup = !isci() tol = itol(2.0e-2)
@plottest julia_dict_tree() "julia_dict_tree.png" popup = !isci() tol = itol()

@plottest funky_edge_and_marker_args() "funky_edge_and_marker_args.png" popup =
!isci() tol = itol()

@plottest custom_nodeshapes_single() "custom_nodeshapes_single.png" popup = !isci() tol =
itol()

@plottest custom_nodeshapes_various() "custom_nodeshapes_various.png" popup =
!isci() tol = itol()
end

@testset "README" begin
@plottest julia_logo_pun() "readme_julia_logo_pun.png" popup = !isci() tol = itol()
end
end
# -----------------------------------------
#
2 changes: 1 addition & 1 deletion PlotsBase/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Printf = "1"
PythonPlot = "1"
Random = "1"
REPL = "1"
RecipesBase = "1.3.1"
RecipesBase = "1.4.0"
RecipesPipeline = "1"
Reexport = "1"
Scratch = "1"
Expand Down
32 changes: 16 additions & 16 deletions PlotsBase/ext/GRExt.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module GRExt

import PlotsBase: PlotsBase, PrecompileTools, RecipesPipeline, _cycle
import PlotsBase: PlotsBase, PrecompileTools, RecipesPipeline

import NaNMath
import GR
Expand Down Expand Up @@ -331,11 +331,11 @@ function gr_getcolorind(c)
return convert(Int, GR.inqcolorfromrgb(red(c), green(c), blue(c)))
end

gr_set_linecolor(c) = GR.setlinecolorind(gr_getcolorind(_cycle(c, 1)))
gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(_cycle(c, 1)))
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(_cycle(c, 1)))
gr_set_bordercolor(c) = GR.setbordercolorind(gr_getcolorind(_cycle(c, 1)))
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(_cycle(c, 1)))
gr_set_linecolor(c::Colorant) = GR.setlinecolorind(gr_getcolorind(c))
gr_set_fillcolor(c::Colorant) = GR.setfillcolorind(gr_getcolorind(c))
gr_set_markercolor(c::Colorant) = GR.setmarkercolorind(gr_getcolorind(c))
gr_set_bordercolor(c::Colorant) = GR.setbordercolorind(gr_getcolorind(c))
gr_set_textcolor(c::Colorant) = GR.settextcolorind(gr_getcolorind(c))
gr_set_transparency(α::Real) = GR.settransparency(clamp(α, 0, 1))
gr_set_transparency(::Nothing) = GR.settransparency(1)
gr_set_transparency(c, α) = gr_set_transparency(α)
Expand Down Expand Up @@ -518,7 +518,7 @@ function gr_polaraxes(rmin::Real, rmax::Real, sp::Subplot)
# draw radial ticks
yaxis[:showaxis] && for i in eachindex(rtick_values)
r = (rtick_values[i] - rmin) / (rmax - rmin)
(r ≤ 1 && r ≥ 0) && gr_text(GR.wctondc(0.05, r)..., _cycle(rtick_labels, i))
(r ≤ 1 && r ≥ 0) && gr_text(GR.wctondc(0.05, r)..., _getvalue(rtick_labels, i))
end
GR.restorestate()
return nothing
Expand Down Expand Up @@ -1290,7 +1290,7 @@ function gr_add_legend(sp, leg, viewport_area)
1,
min(max_markersize, mfac * msz),
min(max_markersize, mfac * msw),
_cycle(msh, 1),
_getvalue(msh, 1),
)
end

Expand Down Expand Up @@ -2029,8 +2029,8 @@ function gr_draw_segments(series, x, y, z, fillrange, clims)
if is2d && fillrange ≢ nothing
(fc = get_fillcolor(series, clims, i)) |> gr_set_fillcolor
gr_set_fillstyle(get_fillstyle(series, i))
fx = _cycle(x, vcat(rng, reverse(rng)))
fy = vcat(_cycle(fr_from, rng), _cycle(fr_to, reverse(rng)))
fx = _getvalue(x, vcat(rng, reverse(rng)))
fy = vcat(_getvalue(fr_from, rng), _getvalue(RecipesBase.cycle(fr_to), reverse(rng)))
gr_set_transparency(fc, get_fillalpha(series, i))
GR.fillarea(fx, fy)
end
Expand Down Expand Up @@ -2068,18 +2068,18 @@ function gr_draw_markers(
rng = intersect(eachindex(IndexLinear(), x), segment.range)
isempty(rng) && continue
i = segment.attr_index
ms = get_thickness_scaling(series) * _cycle(msize, i)
msw = get_thickness_scaling(series) * _cycle(strokewidth, i)
shape = _cycle(shapes, i)
ms = get_thickness_scaling(series) * _getvalue(msize, i)
msw = get_thickness_scaling(series) * _getvalue(strokewidth, i)
shape = _getvalue(shapes, i)
if !(shape isa Shape)
shape = gr_get_markershape.(shape)
end
for j in rng
gr_draw_marker(
series,
_cycle(x, j),
_cycle(y, j),
_cycle(z, j),
_getvalue(x, j),
_getvalue(y, j),
_getvalue(z, j),
clims,
i,
ms,
Expand Down
6 changes: 3 additions & 3 deletions PlotsBase/ext/GastonExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ function gaston_add_series(plt::Plot{GastonBackend}, series::Series)
if gsp.dims == 2 && z ≡ nothing
for (n, seg) in enumerate(series_segments(series, st; check = true))
i, rng = seg.attr_index, seg.range
fr = _cycle(series[:fillrange], 1:length(x[rng]))
fr = _getattr(series, :fillrange, 1:length(x[rng]))
for sc in gaston_seriesconf!(sp, series, n == 1, i)
push!(curves, Gaston.Curve(x[rng], y[rng], nothing, fr, sc))
end
Expand Down Expand Up @@ -785,8 +785,8 @@ gaston_lc_ls_lw(series::Series, clims, i::Int) = (
)

gaston_mk_ms_mc(series::Series, clims, i::Int) = (
gaston_marker(_cycle(series[:markershape], i), get_markeralpha(series, i)),
0.2_cycle(series[:markersize], i),
gaston_marker(_getattr(series, :markershape, i), get_markeralpha(series, i)),
0.2 * _getattr(series, :markersize, i),
gaston_color(get_markercolor(series, clims, i), get_markeralpha(series, i)),
)

Expand Down
Loading