-
Notifications
You must be signed in to change notification settings - Fork 251
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
Matplotlib revamp (again) with proper offsetting and scaling with aspect ratios and zoom/pan #669
Conversation
This is now working as expected and actually ended up being the dreaded option 3, i.e. extending matplotlib's plotting functionality. Briefly:
Remaining items on the todo list:
Overall I'm quite proud of this, it touches pretty deep into matplotlib and it was not trivial to achieve. |
I now:
This is getting quite close to review I think. |
Phew, lots of changes here, give me some time to review it :) Is it okay to start the review now or are you still working on it? |
Please start reviewing, there's no rush to get this in quickly so take your time.
The biggest limitation is that `Collections` are efficient for large graphs but don't currently support arbitrary vertex decorations - e.g. pie charts. But it would not be terribly hard, on the basis of this PR, to create a new `Collection` type supporting that.
…On Mon, Jun 5, 2023, at 02:51, Tamás Nepusz wrote:
Phew, lots of changes here, give me some time to review it :) Is it
okay to start the review now or are you still working on it?
—
Reply to this email directly, view it on GitHub
<#669 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAJFEAALDQR64AUZCE2RFHLXJS4JNANCNFSM6AAAAAAYGBHW64>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
class EdgeCollection(PatchCollection): | ||
def __init__(self, *args, **kwargs): | ||
kwargs["match_original"] = True | ||
self._visual_vertices = kwargs.pop("visual_vertices", None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and equivalent in the other collections, there is another way to do this, i.e. passing the parent GraphArtist
straight as an attribute of this class, and grab individual properties when needed
Ping 😉 happy to do some explaining here or via email if you guys prefer, or I can merge and deal with the resulting mayhem afterwards 😉 |
Finally had some time to go through the PR this weekend. Fixed a typo, but otherwise it looks good to me based on my limited understanding of Matplotlib's internals. The reference images need to be updated after I've merged So, let me know when the reference images are updated, and if all tests pass, I'll open a |
sounds good, thank you. I just switched to a new laptop and half the toolchain is missing :-P so it's going to take me a few days. I'll also try to integrate the avoiding loops if not too messy. edit: and since this is for 0.11, I will also return the |
I pushed a version of the loops logic in 97f120c, here's how it looks like: It uses 3-quarter circles for wedges > 90 degrees and cubic Bezier for smaller wedges. Probably a little overkill, but looks decent to me. ATM the loop size scales with vertex size, do you guys think that's ok? Alternatively, I could:
Any preferences? |
Looks nice, but if these are directed graphs, the loops should have arrowheads as well. |
I thought about it, but making the curved arc/Bezier look good despite a potentially big arrowhead is tricky:
Since we don't support "partially directed" graphs, I thought this makes for a cleaner design without any ambiguity. In terms of scaling, I'm leaning towards making an option with default around 20-30, that way users can tweak it if they need very specific situations. |
I think that is a very good default. When the vertex size increases, the loop size must increase for loops to be visible. It would be nice for loop sizes to be adjustable independently for each loop separately, but that's a nice to have, not a must. It has some applications, for example visualizing edge weights by thickness. It's nicer (though not a must) for very thick loops to be larger. This would e.g. improve Fig 1b here. If you do implement per-loop size adjustment, it's still a question what the value should represent from the choices you listed (size relative to individual vertex, to figure size, or fixed unit). The applications I can think of would benefit from making this independent of vertex size. But when sizing is left to be computed automatically, it's really nice to make it scale with vertex size ... I don't have answer for you. Regarding arrowheads I have seen visualizations where arrows did not have arrows in directed graphs, but very rarely. In my experience it is much more common to add the arrows. I think that there should be a way to add the arrows even if you decide not to do it by default. Many people will insist that their publication figures should have arrows on the loops. This includes myself: I want those arrows for publication figures. For exploratory analysis of course it does not matter.
Personally I don't like the circular loops, but this is subjective, so I did not comment. With arrows, it's nice to have Bezier curves as the arrowhead should point more towards the vertex. What I don't understand in your example images is why the loops with arrows are not symmetric. Is this a restriction of matplotlib? This is what it looks like with Mathematica: ![]() ![]() An interesting feature of Mathematica that makes this feasbile is arrowhead offsetting. The Bezier curves are defined starting at the centerpoint of each vertex, but arrowheads are offset by the vertex radius. I don't know if this is easy with matplotlib. Even if it is, it's not a full solution as vertex shapes may be different from circular. And then it gets really complicated. ![]() ![]() I don't think we can afford to make all this possible (??) but it's good to be aware of how far it's possible to go in principle ... |
If everyone wants it, we should add them 😅 In your second, third and fourth figure look at the bottom left arrow: clearly not pointing towards the center of the vertex. Perhaps people will not be bothered by that? For the scaling with vertex, what about we keep a special value, say the string "vertex", to mean scaling with vertex size? |
I added arrows to directed loops in 7131d2e, result below: What do you think? I tried to add the "vertex" special argument, but those attributes are type-checked so that failed. I therefore implemented an alternative logic: when |
Yay @iosonofabio , this looks awesome! |
Thanks, I think we are slowly getting there. @ntamas do you think the negative number "magic" is ok? |
I really like this version.
No comment from me on the interface, but I think it's really great that both absolute and relative sizing are possible. EDIT: Just for comparison, whenever a size or coordinate is needed, in Mathematica I can write:
Is there something similar in matplotlib? Would it be Pythonic to define a wrapper class for integers? |
This is definitely not the subject of this PR, but after looking at too many network plots today, I wanted to make two suggestions:
IMO these should be low priority, but they're worth considering. |
Not quite happy with it; I like the idea of a |
I like the custom units, but matplotlib implements transformations in quite a different way so I'm thinking of a way to do it that does not involve rewriting half of mpl's code. The current customization of container artists and subclassing collections is already out there in terms of patching basic functionality. |
Hi guys, I've been thinking about this. At this point in time, I am swamped by other things so I don't have enough bandwidth to flesh out a new system of coordinates within igraph plotting. I guess option B (keep negative numbers for now) is the best I can do for now. If any of you wants to take over I don't mind, otherwise I'll try to polish the few remaining things so tests pass and then we can merge into |
Ok, I merged again from I'd say it's time to merge this, because I won't have contiguous time in the coming months and might end up forgetting the logic before it's merged. @ntamas: |
Fast-forwarded |
Awesome. I'll reach out to the matplotlib folks to see if the coordinate systems we mentioned might be something they are interested in from their end. Thank you |
@iosonofabio I'm going to merge this but can you also summarize the changes in the |
did this go into develop or main? |
oooh dammit! thanks! I'll fix this |
|
Okay, mischief managed. |
I edited the CHANGELOG. It's kind of a mess to list everything, feels like writing a will. Does it look reasonable? |
Yes, seems good, thank you! |
There have been a few requests centered around the ability to combine data units and pixels/figure units in matplotlib plots.
tl;dr: I thought this would be more messy but once I started playing with
matplotlib
internals I found them amenable to reason - I think. Good news.That is not needed in Cairo because it produces static images, therefore the conversion between figure and data units is fixed at the beginning. In matplotlib, however, plots are interactive, therefore the transformation needs callbacks.
This PR aims at extending matplotlib with a subclass of
Collection
, to be used for edges, that plots the lines/arrows with awareness of the vertex marker bounding boxes, both at the beginning and as they are updated later on.At the moment, a working stub of the class is there. The logic is correct as verified on my laptop, but it does not work for directed edges (arrows), just undirected (lines). To include directed edges, we currently have separate
Artist
s for the arrow shaft and head, which is not ideal. It would probably be wise to combine those into a single artist and go from there.Next steps:
Collection
to subclass bestPatch
in generalsin
/cos
instead of the rectangularBbox
for the vertex markerEdgeCollection
, to be triggered at each redrawBug/peculiarity note:
PatchCollection.get_paths()[i].vertices
directly leads to weird bugs in the edge drawer since the vertex builder has not changed -> edges do not know how big vertices have become. Changing the paths does trigger a "stale" on thePatchCollection
artist, hence a redraw. Ideally, we would have a hook callback that then triggers a redraw of theEdgeCollection
, but that sounds messy.Collection
artists can be tricky, but it's now implemented via a callback mechanism.NOTE: in a previous discussion #665, two solutions were mentioned. This is closer to solution #2, but we try hard to keep it as general as possible to enable later additions (e.g. pie charts in place etc). Pie charts would not work ATM in this PR, but a slight adaptation (
Patch
es instead of straightPath
s), which is planned for this PR itself, would make that quite easy to do.This will still take a few weeks to sort out, as it needs to be done with care.