Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provide convenience function for MetaGraphsNext #164

Open
rapus95 opened this issue Dec 4, 2023 · 7 comments
Open

provide convenience function for MetaGraphsNext #164

rapus95 opened this issue Dec 4, 2023 · 7 comments

Comments

@rapus95
Copy link

rapus95 commented Dec 4, 2023

MetaGraphsNext is the typestable successor of MetaGraphs and it would be nice, if there would be some convenience function to be able to automatically plot vertex labels, edge labels, and so on. Or, if it's out of scope of the module (due to dependency management), have some help to write the needed functions and have them available in this issue.

@hexaeder
Copy link
Collaborator

hexaeder commented Dec 4, 2023

Reasonable feature request! I think it could be rather challenging to find useful defaults, though. Metagraphs can represent basically everything; the desired look of the graph depends on the specific use case a lot. What kind of convenience functions could there be besides streamlining

graphplot(g; nlabels=repr.(labels(g)))

Since the look of the graph plot depends mainly on keyword arguments, I think there are 3 ways one could achieve that kind of convenience functions:

specific recipe

One could overload graphplot for MetaGraph objects. This would basically create a new plot recipe just for MetaGraphs.

Pros:

  • "cleanest" one-liners possible (i.e., mgraphplot(myMetaGraph) might suffice in simple cases)

Cons:

  • more annoying documentation-wise because the new recipe would essentially duplicate most of the old one but overwrite some specific defaults.
  • less flexible? I.e. if the nodelabel type is a symbol :foo. Should it be displayed as foo or :foo? This kind of decision would be baked into the GraphMakie recipe code then.

"preprocessing" function

I used this pattern extensively in my own work based on GraphMakie: write some helper function that helps generating the specific keyword arguments as a named tuple:

function gpargs(mg::MetaGraph)
    # do clever stuff here
    return (; nlabels=..., elabels=...)
end

fig, ax, p = graphplot(myMetaGraph; gpargs(myMetaGraph)...)

Pros:

  • The code could live in a package extension; not sure if in GraphMakie or MetaGraphsNext.
  • rather flexible because all kw arguments could still be overwritten easily (if the same keyword appears multiple times, Julia will use the last one).

Cons:

  • less elegant

extraction functions

Alternatively, one could do something similar but on a keyword base:

fig, ax, p = graphplot(myMetaGraph; elabels=extract_elabels(myMetaGraph),
                                    nlabels=extract_nlabels(myMetaGraph), ...)

Pros:

  • most flexible

Cons:

  • most verbose

Did you have something similar in mind, or did you think of something completely different?

@rapus95
Copy link
Author

rapus95 commented Dec 4, 2023

On THIS constructor we get the following information:

  • vertices_description is a vector of pairs label => data (the code of a vertex will correspond to its rank in the list)
  • edges_description is a vector of pairs (label1, label2) => data

Following that, for me the most sensible defaults would be to have anything that's named "data" show on hovering the corresponding element, and have the label value be rendered into the node (ideally while allowing to provide transformer functions, defaults: labeltransformer=identity, datatransformer=identity).

If one needs to show the data as the nodelabel (for example in non-interactive cases/static exports) one can simply supply labeltransformer = x->g[x] since MetaGraph g provides the data if supplied with the label.

EDIT: regarding your 3 ways, I really like all of them while I prefer the first one (own recipe). and having preprocessing as the 2nd.

@hexaeder
Copy link
Collaborator

hexaeder commented Dec 5, 2023

If it should be interactive, i.e. only showing the data on hover, the problem is a bit more complicated because this requires registering interactions, which are a property of the axis rather than the plot content and thus cannot be defined within the recipe, which is a known limitation of the current recipe system in Makie. For that, the solution would be to define something along the lines of

function showDataOnHover(p; labeltransformer, datatransformer)
    # do something usefull
    return someHoverHandler(...)
end

fig, ax, p = graphplot(mymetagraph; ilabels=mylabeltransformer.(nodelabels(mymetagraph))
register_interaction!(ax, :datahover, showDataOnHover(p; datatransformer=...))

@rapus95
Copy link
Author

rapus95 commented Dec 5, 2023

Wouldn't it be possible to fully hook into the recipe system and call register_interaction/define process_interaction within the full-recipe-plot!-function?

Going that route: https://docs.makie.org/stable/explanations/recipes/#full_recipes_with_the_recipe_macro

and calling the necessary functions within the

function plot!(myplot::MyPlot)
    # normal plotting code, building on any previously defined recipes
    # or atomic plotting operations, and adding to the combined `myplot`:
    register_interaction!(myplot, :graphhover) do
        #some hover landling
    end 
    lines!(myplot, rand(10), color = myplot.plot_color)
    plot!(myplot, myplot.x, myplot.y)
    myplot
end

(adapted from the url above)

@hexaeder
Copy link
Collaborator

hexaeder commented Dec 6, 2023

The problem is that there is no reference to the axis object within the plot!(p::GraphPlot) function. p is just a reference to the plot, while the interaction needs to be registered with the axis. The plot object does not know anything about the axis in which it is displayed.

@rapus95
Copy link
Author

rapus95 commented Dec 6, 2023

Would it be possible to register interaction handlers on the plot and then hook into the code which assigns the plot to an axis and make it also extract the interaction handlers, re-registering it on the axis?
something like

function plot!(myplot::MayPlot)
  register!(myplot, :on_axis_assign) do ax
    register_interaction!(ax, :graphhover) do
        #some hover landling
    end
  end
end

@hexaeder
Copy link
Collaborator

hexaeder commented Dec 7, 2023

In theory yes, but that's a feature that is not yet provided by Makie. As a workaround, it is also always possible to write your own recipie-like functions, which create entire figures with complex interactions instead of a single plot:

function visualize_graph(mg::MetaGraph)
    fig = Figure()
    # plot, register callbacks and so on
    return fig
end

Nevertheless, I think regarding this issue the first step right now would be to create this kind of show-data-on-hower interaction. If you need help with that, you can provide me a small MWE including the construction and basic plotting of a MetaGraph and I can try to define an interaction constructor for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants