Comments (16)
@greimel, it seems like you only need to get the boundingbox and maximum width of the text. I suggest the following basic modification, which allows you to automatically find the text size:
Code
function text_bbox(textstring::AbstractString, textsize::Union{AbstractVector, Number}, font, align, rotation, justification, lineheight)
glyph_collection = Makie.layout_text(
textstring, textsize,
font, align, rotation, justification, lineheight,
RGBAf(0,0,0,0), RGBAf(0,0,0,0), 0f0
)
return Rect2f(Makie.boundingbox(glyph_collection, Point3f(0), Makie.to_rotation(rotation)))
end
# ╔═╡ a5be84d8-d9d5-4c1d-8b2c-7eca3857f2fb
function numbered_graphplot!(ax, g;
minimal=false, extend_limits=automatic,
nlabels = vertices(g),
layout = Spring(), node_color = (:lightgray, 1), node_attr = (;), node_markersize = automatic, node_marker = :rect, node_font = Makie.defaultfont(), node_textsize = 16, kwargs...)
n = nv(g)
# Extract text sizes and layout accordingly
label_sizes = [widths(text_bbox(string(label), node_textsize, node_font, (:center, :center), 0f0, 0, 0)) for label in nlabels]
# label_diags = collect(sqrt.(sum.([label_size .^ 2 for label_size in label_sizes])))
# max_size = maximum(first.(label_sizes))# .+ node_textsize
# We can specify markersize as a Vec2f, which is the eltype of label_sizes.
# Thus, we can explicitly cause the node drawing to be large enough to accomodate
# the marker size.
node_attr = (;
node_attr...,
marker = node_marker, color = node_color,
strokewidth = 1, markersize = node_markersize == automatic ? map(x -> x .+ node_textsize, label_sizes) : node_markersize,
markerspace = :pixel,
)
if minimal
hidedecorations!(ax)
hidespines!(ax)
end
if extend_limits !== automatic
positions = layout(g)
ax.limits[] = extended_box(positions, extend_limits)
else
ax.limits[] = (nothing, nothing, nothing, nothing)
end
#poly!(ax, Circle.(layout(g), 0.07px), color = (:white, 0))
graphplot!(ax, g; node_attr, layout, kwargs...) #node_marker = '1':'5')
text!(ax, string.(nlabels); position=layout(g), font = node_font, textsize = node_textsize, align = (:center, :center))
end
Basically, what this does is it computes the text's bounding box by using Makie's layout utilities, and passes the computed widths to markersize
.
This means that all markers are sized independently.
With this,
let
g = cycle_digraph(15)
numbered_graphplot(g, arrow_size = 15, nlabels = string.(100 .* (1:15)))
end
Setting node_marker=:circle
, the layout is a little tighter, but still theoretically good. There are definitely a lot of ways you can play with the markersize in order to make it look as good as possible.
from graphmakie.jl.
I needed something similar, so I've written code for it. It's only good for one character labels, though. And doesn't cover anything that @hexaeder has thought about above: No interaction, no handling of overlapping nodes, not even proper text boxes.
I thought I share it here, in case somebody finds this useful.
code
# ╔═╡ 0de55944-f737-4a4f-849d-3a925b1eceb3
using Makie: automatic, Figure, Axis, hidedecorations!, hidespines!, text!
# ╔═╡ 7fd33644-b26a-439b-8a63-29535abc1dd3
using GraphMakie: graphplot!
# ╔═╡ c2bf3f66-b017-44ac-844e-cd2350828ddb
using NetworkLayout: Spring
# ╔═╡ 40ef5e6a-5b73-4963-bc71-5faad1948081
using Graphs: vertices, nv
# ╔═╡ 9fbfe652-e31b-4a09-99ed-62db24bc03c5
function numbered_graphplot(g; minimal = true, figure = (;), axis = (;), kwargs...)
fig = Figure(; figure...)
ax = Axis(fig[1,1]; axis...)
numbered_graphplot!(ax, g; minimal, kwargs...)
fig
end
# ╔═╡ a5be84d8-d9d5-4c1d-8b2c-7eca3857f2fb
function numbered_graphplot!(ax, g;
minimal=false, extend_limits=automatic,
nlabels = vertices(g),
layout = Spring(), node_color = (:lightgray, 1), node_attr = (;), kwargs...)
n = nv(g)
node_attr = (; node_attr..., marker = :circle, color = node_color, strokewidth = 1, markersize = n < 10 ? 35 : 45)
if minimal
hidedecorations!(ax)
hidespines!(ax)
end
if extend_limits !== automatic
positions = layout(g)
ax.limits[] = extended_box(positions, extend_limits)
else
ax.limits[] = (nothing, nothing, nothing, nothing)
end
#poly!(ax, Circle.(layout(g), 0.07px), color = (:white, 0))
graphplot!(ax, g; node_attr, layout, kwargs...) #node_marker = '1':'5')
text!(ax, string.(nlabels); position=layout(g), align = (:center, :center))
end
# ╔═╡ 0442bc7c-9c74-4325-9f8f-5f77b169a495
md"""
## Utils
"""
# ╔═╡ da154cf8-0939-4505-a8b7-3cb979a0a371
function extended_extrema(points, margin = 0.05)
min, max = extrema(points)
ε = (max - min) * margin
if ε > 0
return min - ε, max + ε
else
return nothing, nothing
end
end
# ╔═╡ 6cb5f932-ee5c-40db-bed7-ec592532cfff
function extended_box(points, margin = 0.05)
(extended_extrema(first.(points), margin)...,
extended_extrema(last.(points), margin)...)
end
# ╔═╡ c05bf8a6-d5db-43d7-a855-ed1a98d71057
md"""
## Tests
"""
# ╔═╡ 53ac8dd8-26d1-4c8b-ba50-580a7086dbf4
using Graphs: complete_digraph, cycle_graph, cycle_digraph
# ╔═╡ d10d10bf-ae56-480c-b60c-232c0e0ad3ce
using NetworkLayout: Shell
# ╔═╡ fbd2ab88-7f46-4e64-8954-99539a7fb75e
using CairoMakie: DataAspect
# ╔═╡ 5554c7be-7e00-47c1-b1cb-fc11c84acd9f
begin
g = complete_digraph(5)
fig = Figure(font = "CMU")
ax = Axis(fig[1,1], aspect = DataAspect())
#hidedecorations!(ax)
numbered_graphplot!(ax, g; layout = Shell(), arrow_size = 15)
fig
end
# ╔═╡ 3a5e4d4e-204f-44d4-924f-9d85abef79c1
let
g = cycle_digraph(5)
numbered_graphplot(g, arrow_size = 15)
end
# ╔═╡ 700d5452-c2ce-4ab1-aa40-2a8ffaa0f6bf
let
g = cycle_graph(5)
numbered_graphplot(g, nlabels = 'A':'E')
end
from graphmakie.jl.
Yea, just select the maximum width instead of using both :)
from graphmakie.jl.
I think this would be a great addition, even without having all the features. I think there are good reasons for both: extending the current recipe and creating a second recipe.
Extending the recipe comes with a less clear interface. For example, what should happen if you combine nlabels_align = (:top, :center)
with node_size = :text_size
? This means that you really have to know your keywords to achieve the style. Having a new recipe would help in providing sane defaults for this specific kind of graphplot, something like textboxgraphplot(g, nlabels; kwargs...)
could work out of the box.
On the other hand, the different parts of the graph plots need to be more modular in order to play well with different root recipes. I started this by creating a separate edgeplot
recipe which is used internally, but for example the edge labels should move to that recipe as well, otherwise we'll end up with code duplication.
from graphmakie.jl.
@greimel @asinghvi17 are either of you planning on writing a PR for this?
from graphmakie.jl.
@hexaeder This looks very good, doesn't it? Still it doesn't address all of your concerns above.
There are some things which need to be done
- implement some kind of
textbox
recipe which will take the node labels and places them in shapes drawn aspoly
- potentially update some layouts that can handle a "node size" parameter (avoid placing nodes to close to each other which would lead to overlapping polys)
- think about anchors on the textboxes, ie where should the edges start and end
What do you think about this now? Also, what about
Finally i am not sure whether this can be achieved as part of the
graphplot
recipe. Maybe it would be wise to create a new recipe for that.
One could integrate this using existing keywords. E.g.
nlabel_align = :inside
node_size = :text_size
Do would you think about a draft PR that tries to integrate this into GraphMakie?
from graphmakie.jl.
Currently not really, no. Each graph plot is based on a scatter + some connecting lines in GraphMakie. You might want to check out GraphRecipes for that. I think, they have this kind of feature.
However you could use the propertie nlabels_align
on a per-node basis to place the labels on the right sides of the nodes (see Julia AST example). With nlabels_offset
and nlabels_distance
you may further tweak the position of the labels.
from graphmakie.jl.
Thank's for the hints!
Do GraphRecipes also work with Makie? I've only used them with Plots and I didn't find any references to Makie in the documentation.
from graphmakie.jl.
No they don't. It is Plots only...
from graphmakie.jl.
Ok thank's, then I have all the information needed so far!
from graphmakie.jl.
I'll keep this open because eventually I'd like to expand GraphMakie in that direction. This won't happen to soon though.
There are some things which need to be done
- implement some kind of
textbox
recipe which will take the node labels and places them in shapes drawn aspoly
- potentially update some layouts that can handle a "node size" parameter (avoid placing nodes to close to each other which would lead to overlapping polys)
- think about anchors on the textboxes, ie where should the edges start and end
Finally i am not sure whether this can be achieved as part of the graphplot
recipe. Maybe it would be wise to create a new recipe for that.
from graphmakie.jl.
Oh, it would be fantastic, if you could realize these plans. I'm looking forward to it.
from graphmakie.jl.
Ah that looks really good! ... and demonstrates again the need for such solutions.
I'm currently playing around with Plots
and GraphRecipes
, using the TreePlot
recipe for plotting trees. But some variants have serious issues (JuliaPlots/GraphRecipes.jl#172) and even the variants that work have some potential for improvement. E.g. the edges connecting the nodes do not attach exactly to the node boundaries. You can control them a bit using the shorten
parameter. But on some nodes they don't connect at all and on other nodes they reach inside the node box (see: JuliaForMLTutorial; there "ML-Tutorial 3 .." in folder "notebooks" for some examples).
from graphmakie.jl.
@asinghvi17 thanks, that looks great! Is there a way to have proper circles (non-squished) with your way?
from graphmakie.jl.
Phantastic, thanks!
from graphmakie.jl.
See https://discourse.julialang.org/t/textbox-graph-with-networklayout-with-labels-cut-off-using-graphmakie/96566/ on the expanding limits.
from graphmakie.jl.
Related Issues (20)
- curve_distance & curve_distance_usage observable not updating figure
- Precompilation stuck on Julia 1.9 HOT 6
- handle overlapping `nlabels` HOT 6
- node_color and node_marker only accept Symbol HOT 5
- Performant graph plotting and benchmarking HOT 4
- Element-wise `automatic` configuration HOT 1
- Deleting a plot doesn't remove all listeners HOT 2
- Mixing directed and undirected edges HOT 2
- Add padding around graphplot to accommodate node size. HOT 2
- `node_attr` broken using Makie v0.19.11 HOT 1
- Slow start HOT 2
- Setting node size with ilabel
- Improve Reftests HOT 1
- provide convenience function for MetaGraphsNext HOT 7
- KeyError: key :textsize not found when running doc examples HOT 2
- UndefVarError: `mouse_selection` not defined HOT 5
- arrow_shift = :end works poorly with larger edge width HOT 6
- bounding box cuts off nodes HOT 3
- `pick` one `LineSegments` returns odd index HOT 2
- Dynamic Layout + Interaction. HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphmakie.jl.