#27866 closed enhancement (fixed)
Introduce graphics insets
Reported by: | egourgoulhon | Owned by: | |
---|---|---|---|
Priority: | major | Milestone: | sage-9.0 |
Component: | graphics | Keywords: | graphics inset |
Cc: | kcrisman, chapoton, dkrenn, jdemeyer, embray | Merged in: | |
Authors: | Eric Gourgoulhon | Reviewers: | Markus Wageringel |
Report Upstream: | N/A | Work issues: | |
Branch: | ed586d3 (Commits, GitHub, GitLab) | Commit: | |
Dependencies: | #27865 | Stopgaps: |
Description (last modified by )
This ticket introduces graphics insets, i.e. it allows to have various Graphics
objects at arbitrary locations and sizes on a single figure. Previously, only graphics arrays, i.e. Graphics
objects arranged in a regular array, were possible.
This is made possible thanks to the generic class MultiGraphics
introduced in #27865.
The user interface is constituted of three new functions:
- the method
Graphics.inset()
, which adds an inset to a given graphics; see this preview example - the method
MultiGraphics.inset()
, which adds an inset to a multi-graphics (e.g. aGraphicsArray
); see this preview example - the function
multi_graphics()
, which creates a figure from a list ofGraphics
objects at user-specified locations; see this preview example
Attachments (4)
Change History (31)
comment:1 Changed 4 years ago by
Authors: | → Eric Gourgoulhon |
---|---|
Branch: | → public/graphics/graphics_insets |
Commit: | → e1776462164bf01ed857eccb06513c276986e633 |
Dependencies: | → #27865 |
comment:2 Changed 4 years ago by
Cc: | kcrisman chapoton dkrenn jdemeyer added |
---|---|
Status: | new → needs_review |
comment:3 Changed 4 years ago by
Cc: | embray added |
---|
comment:4 Changed 4 years ago by
Description: | modified (diff) |
---|
comment:5 Changed 4 years ago by
Milestone: | sage-8.8 → sage-8.9 |
---|
Moving tickets from the Sage 8.8 milestone that have been actively worked on in the last six months to the next release milestone (optimistically).
comment:6 Changed 3 years ago by
Commit: | e1776462164bf01ed857eccb06513c276986e633 → f504d7a7b5e155a419cc590445a54342ac9b439b |
---|
comment:7 Changed 3 years ago by
The two pyflakes errors reported by the patchbot are spurious ones: these imports are really necessary.
comment:8 Changed 3 years ago by
Commit: | f504d7a7b5e155a419cc590445a54342ac9b439b → 3099f48c2560a6d2131def1b830d032e1941419b |
---|
Branch pushed to git repo; I updated commit sha1. New commits:
3099f48 | 27866: fix typos
|
comment:9 follow-up: 10 Changed 3 years ago by
I was wondering what happens if the graphics inset requires a particular aspect ratio, and apparently the aspect ratio is preserved, which is good. However, in the cases I tested I obtained an inset that is much larger than expected. Could you explain the output for g2
and g3
?
sage: g1 = plot(x^3, x, -1, 1, aspect_ratio=1) sage: g2 = g1.inset(circle((0,1000), 1, frame=True, aspect_ratio=1), (.5,.5,.5,.5)) sage: g3 = g1.inset(circle((0,1000), 1, frame=True, aspect_ratio='automatic'), (.5,.5,.5,.5))
Edit: To be more precise, the inset has the correct size relative to the final image, but not in relation to the main graphics.
Changed 3 years ago by
comment:10 follow-up: 11 Changed 3 years ago by
Replying to gh-mwageringel:
Edit: To be more precise, the inset has the correct size relative to the final image, but not in relation to the main graphics.
Actually, this is a feature, not a bug. From the documentation returned by g1.inset?
:
Signature: g1.inset(graphics, pos=None, fontsize=None) Docstring: Add a graphics object as an inset. INPUT: * "graphics" -- the graphics object (instance of "Graphics") to be added as an inset to the current graphics * "pos" -- (default: "None") 4-tuple "(left, bottom, width, height)" specifying the location and size of the inset on the figure, all quantities being in fractions of the figure width and height; if "None", the value "(0.70, 0.68, 0.2, 0.2)" is used * "fontsize" -- (default: "None") integer, font size (in points) for the inset; if "None", the value of 6 points is used, unless "fontsize" has been explicitely set in the construction of "graphics" (in this case, it is not overwritten here) OUTPUT: * instance of "MultiGraphics"
As you can see, the argument pos
specifies the position and size of the inset relative to the figure size. So, with pos=(0.5, 0.5, 0.5, 0.5)
, as in your example g2
, one obtains an inset that has half the figure width and half the figure height. Do you think that the documentation should be improved about this?
comment:11 follow-up: 13 Changed 3 years ago by
Should it be half the width and height of the figure g1
or the figure g2
? It seems to be half the size of g2
, but I would have expected it to be half the size of g1
as well. In other words, g1
appears too small in g2
– I would have expected g1
to fill out the figure g2
.
As far as I understand, the position is computed in terms of the figure size, not the plotting area, so I should not expect the inset to cover the top right quadrant of the plot precisely, but in g2
the positioning of the inset seems a bit too far off. In particular, I would like to understand whether the tick labels count towards the width and height given in pos
or whether these parameters just determine the size of the frame of the inset. From the looks of it, horizontal labels do not count towards the height, while vertical ones seem to add to the width?
comment:12 Changed 3 years ago by
Commit: | 3099f48c2560a6d2131def1b830d032e1941419b → 71dc5c8501a8129816d0ee5ec4768bf3f60b8fc6 |
---|
Changed 3 years ago by
Attachment: | g2_new.png added |
---|
comment:13 Changed 3 years ago by
Replying to gh-mwageringel:
Should it be half the width and height of the figure
g1
or the figureg2
? It seems to be half the size ofg2
, but I would have expected it to be half the size ofg1
as well. In other words,g1
appears too small ing2
– I would have expectedg1
to fill out the figureg2
.
I've changed the rescaling of the base plot when generating the MultiGraphics
object, the previous values were indeed shrinking g1
too much. The new output is in the attached figure g2_new.png
.
As far as I understand, the position is computed in terms of the figure size, not the plotting area, so I should not expect the inset to cover the top right quadrant of the plot precisely, but in
g2
the positioning of the inset seems a bit too far off. In particular, I would like to understand whether the tick labels count towards the width and height given inpos
or whether these parameters just determine the size of the frame of the inset. From the looks of it, horizontal labels do not count towards the height, while vertical ones seem to add to the width?
The position/size given in the argument pos
regard the position/size of the frame box of the inset with respect to the final plotting area (canvas), not with respect to g1
. This is a feature we should keep IMHO, since it matches with Matplotlib conventions, cf. the argument rect
in the method Figure.add_axes
at https://matplotlib.org/api/_as_gen/matplotlib.figure.Figure.html.
comment:14 Changed 3 years ago by
Commit: | 71dc5c8501a8129816d0ee5ec4768bf3f60b8fc6 → d34b69fafa50c3b879b189fd413a4867321f919c |
---|
Branch pushed to git repo; I updated commit sha1. New commits:
d34b69f | Restore file permissions on build/pkgs/flint/spkg-check
|
comment:15 Changed 3 years ago by
For some reason, yesterday's commit (comment:12) contained a spurious change of permissions on the file build/pkgs/flint/spkg-check
. The above commit (comment:14) corrects this.
Changed 3 years ago by
comment:16 follow-up: 19 Changed 3 years ago by
Reviewers: | → Markus Wageringel |
---|---|
Status: | needs_review → needs_work |
There still seems to be an inconsistency between vertical and horizontal positioning. For example, in g4
the blue squares touch each other in one direction, but not the other. I am not sure what the preferred behaviour is in this case, but it should be the same in both directions.
g1 = plot(x^3, x, -1, 1, aspect_ratio=1, color='red', frame=True) g1.axes_color('red') c = circle((0,1000), 1, frame=True, aspect_ratio=1, transparent=True) c.axes_color('blue') g4 = g1.inset(c, (0,0,.5,.5)).inset(c, (0,.5,.5,.5)).inset(c, (.5,0,.5,.5)).inset(c, (.5,.5,.5,.5))
Moreover, some of the type checks testing for lists and tuples seem a bit restrictive. Especially this one is not necessary, as the implementation will work for any iterable and iterator, so the type check should just be removed:
if not isinstance(graphics_list, (list, tuple)): raise TypeError("graphics_list must be a list or a tuple, " "not {}".format(graphics_list))
I am not sure why the following change is needed. Would it be better to test for the converse, i.e. if not isinstance...
of the types you want to exclude? Or are Graphics
and GraphicsArray
the only types that support tight_layout
? Also, there is a similar change in multigraphics.py that only tests for isinstance(self, GraphicsArray)
. If you think it is preferable to keep this as is, that would also be fine be me.
- # tight_layout adjusts the *subplot* parameters so ticks aren't - # cut off, etc. - figure.tight_layout() + if isinstance(graphics, (sage.plot.graphics.Graphics, + sage.plot.multigraphics.GraphicsArray)): + # for Graphics and GraphicsArray, tight_layout adjusts the + # *subplot* parameters so ticks aren't cut off, etc. + figure.tight_layout()
Other than that, everything looks good to me.
comment:17 Changed 3 years ago by
Commit: | d34b69fafa50c3b879b189fd413a4867321f919c → 2600bd730cca8d0f61bbe063a0b6562149e0100a |
---|
Branch pushed to git repo; I updated commit sha1. New commits:
2600bd7 | Fix merge conflict of graphics insets branch with Sage 9.0.beta2
|
comment:18 Changed 3 years ago by
Commit: | 2600bd730cca8d0f61bbe063a0b6562149e0100a → ed586d3135daa95881bcac5c55766c77fc5bddc8 |
---|
Branch pushed to git repo; I updated commit sha1. New commits:
ed586d3 | Remove restrictive type check in MultiGraphics
|
Changed 3 years ago by
Attachment: | g4_aspect_ratio1.png added |
---|
comment:19 Changed 3 years ago by
Sorry for the delay in replying...
Replying to gh-mwageringel:
There still seems to be an inconsistency between vertical and horizontal positioning. For example, in
g4
the blue squares touch each other in one direction, but not the other. I am not sure what the preferred behaviour is in this case, but it should be the same in both directions.
Actually this is not a bug but results from a clash between the default aspect ratio of the whole figure, which is 4/3, and the aspect ratio 1 of the subplots. If you ask for an aspect ratio of 1 for the whole figure, then everything is OK:
show(g4, figsize=(5, 5))
leads to
Moreover, some of the type checks testing for lists and tuples seem a bit restrictive. Especially this one is not necessary, as the implementation will work for any iterable and iterator, so the type check should just be removed:
Indeed! Thanks for pointing this. The type check has been removed in the above commit.
I am not sure why the following change is needed. Would it be better to test for the converse, i.e.
if not isinstance...
of the types you want to exclude? Or areGraphics
andGraphicsArray
the only types that supporttight_layout
?
Yes, I think so.
Also, there is a similar change in multigraphics.py that only tests for
isinstance(self, GraphicsArray)
. If you think it is preferable to keep this as is, that would also be fine be me.
Yes I think it is preferable to keep the tests as they are, since only GraphicsArray
requires the tight layout.
comment:20 Changed 3 years ago by
Status: | needs_work → needs_review |
---|
comment:21 follow-up: 23 Changed 3 years ago by
Status: | needs_review → positive_review |
---|
Ok, thanks for the explanations. I am setting this to positive then.
comment:22 Changed 3 years ago by
Milestone: | sage-8.9 → sage-9.0 |
---|
comment:23 Changed 3 years ago by
Replying to gh-mwageringel:
Ok, thanks for the explanations. I am setting this to positive then.
Thanks for the review!
comment:24 Changed 3 years ago by
Branch: | public/graphics/graphics_insets → ed586d3135daa95881bcac5c55766c77fc5bddc8 |
---|---|
Resolution: | → fixed |
Status: | positive_review → closed |
comment:25 Changed 3 years ago by
Commit: | ed586d3135daa95881bcac5c55766c77fc5bddc8 |
---|
This is causing two test failures on Arch when compiling against system libraries (haven't tried on sage-the-distro)
********************************************************************** File "/usr/lib/python3.7/site-packages/sage/plot/multigraphics.py", line 1297, in sage.plot.multigraphics.GraphicsArray.position Failed example: G.position(0) # tol 1.0e-13 Expected: (0.028906249999999998, 0.038541666666666696, 0.45390624999999996, 0.9229166666666667) Got: (0.023437500000000003, 0.03415488992713045, 0.4627803348994754, 0.9345951100728696) Tolerance exceeded in 4 of 4: 0.028906249999999998 vs 0.023437500000000003, tolerance 2e-1 > 1e-13 0.038541666666666696 vs 0.03415488992713045, tolerance 2e-1 > 1e-13 0.45390624999999996 vs 0.4627803348994754, tolerance 2e-2 > 1e-13 0.9229166666666667 vs 0.9345951100728696, tolerance 2e-2 > 1e-13 ********************************************************************** File "/usr/lib/python3.7/site-packages/sage/plot/multigraphics.py", line 1302, in sage.plot.multigraphics.GraphicsArray.position Failed example: G.position(1) # tol 1.0e-13 Expected: (0.5171874999999999, 0.038541666666666696, 0.45390624999999996, 0.9229166666666667) Got: (0.5136230468749999, 0.19293222169724827, 0.46278033489947534, 0.617040446532634) Tolerance exceeded in 4 of 4: 0.5171874999999999 vs 0.5136230468749999, tolerance 7e-3 > 1e-13 0.038541666666666696 vs 0.19293222169724827, tolerance 5e0 > 1e-13 0.45390624999999996 vs 0.46278033489947534, tolerance 2e-2 > 1e-13 0.9229166666666667 vs 0.617040446532634, tolerance 4e-1 > 1e-13 ********************************************************************** 1 item had failures: 2 of 6 in sage.plot.multigraphics.GraphicsArray.position [192 tests, 2 failures, 15.63 s]
Last 10 new commits:
Small cleaning in MultiGraphics
Fix some doctests after the improved computation of nrows and ncols in graphics_array
Some cleaning in MultiGraphics.matplotlib
Minor cleaning in GraphicsArray
Change index range in GraphicsArray._add_subplot()
First draft of multi_graphics
Add method inset() to Graphics and MultiGraphics
Add method position in MultiGraphics and GraphicsArray
Add transparency of individual graphics in MultiGraphics + more doctests
Add documentation to MultiGraphics