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

Support for subplots #55

Open
objorke opened this issue Aug 14, 2014 · 5 comments
Open

Support for subplots #55

objorke opened this issue Aug 14, 2014 · 5 comments

Comments

@objorke
Copy link
Member

objorke commented Aug 14, 2014

Refactor the plot views to allow a model composed of multiple PlotModels.
The composed model could have a grid layout defined by column widths and row widths.
The children (subplots) should define PlotModel, column, row, colspan, rowspan.

http://matplotlib.org/examples/pylab_examples/subplots_demo.html
http://matplotlib.org/users/gridspec.html
http://se.mathworks.com/help/matlab/ref/subplot.html
http://www.dartmouth.edu/~rc/classes/matlab_graphics/Matlab-subplots.html

@objorke objorke changed the title Boxes around subplots Support for subplots Feb 25, 2015
@objorke objorke removed the CodePlex label Feb 25, 2015
@ndalchau
Copy link

I'm currently assessing the potential to use OxyPlot in some projects. Subplots is important to us. I guess the lack of comments on this issue indicates that there has been no progress on this?

@objorke
Copy link
Member Author

objorke commented Oct 22, 2015

I think some architectural changes are required to support subplots. A model composed of multiple plotmodels should not be difficult to make, but I think this will also some refactoring of the views...

@habinez
Copy link

habinez commented Oct 24, 2015

you can make a multimodel plotViewer using column definitions. In MVVM I was able to create to PlotModel MyPlotModel1 and MyPlotModel1 there I created two grid columns to hold the plots. Below is code snippet for the xaml grid

<Grid>
        <Grid.ColumnDefinitions>
               <ColumnDefinition Width="480*"/>
               <ColumnDefinition Width="480*"/>
        </Grid.ColumnDefinitions>
        <oxy:PlotView Model="{Binding MyPlotModel1}" Grid.Column="1"/>
        <oxy:PlotView Model="{Binding MyPlotModel2}" Grid.Column="2" />
</Grid>

@VisualMelon
Copy link
Contributor

I'd be very interested to help with implementing better sub-plot support.

I've needed something not-dissimilar recently. To that end, I added a public method to PlotModel to render the plot on a given rectangle, rather than filling the whole page (so I could position 2 side-by-side with a thin IPlotModel implementation). I also changes the default rendering mode so that it would render within a rectangle, replacing the width and height it used to work from with a bounding rectangle. These methods also now take a flag which disables the actual rendering, so you can perform all the measuring etc. which could be useful for automatically aligning grids of plots. Code is in my PlotModel Bounds branch, the RenderOnRect method allows me to line-up axes, completely ignoring padding and plot bounds.

The main problems I ran into, which will surely be problems for any manner of SubPlots:

  • Most of the logic to arrange things is internal, that a good solution will have to be part of the main assembly
  • No cross-plot axis support (it's forbidden); however, this can be worked around to some extent, and arguably doesn't make much sense (similarly for other plot elements)
  • No means to share a legend between PlotModels (this does make sense)
  • WinForms PlotView etc. all want a PlotModel: this means that some sort of MultiPlotModel is a non-starter; a new interface would be required to decouple everything from PlotModel, and that interface couldn't make any sense with interactive PlotViews (e.g. what is the PlotArea of a multi-plot?). This might suggest a different interaction interface, which would require per-platform support (not a problem for PDF/SVG export, because they take the minimal IPlotModel interface). This is surely the hardest problem to solve.

Summary of stuff I did:

  • Added a BoundingBox convenience function to OxyRect
  • Added PlotBounds rect to PlotModel, which does the old job of Width and Height (which are no longer used)
  • Extracted RenderInRect from RenderOverride in PlotModel, which uses all the old logic and code, but updated to support PlotBounds
  • Added RenderOnRect (derived from RenderOverride and helpers), which takes the plot area as a parameter, and builds everything else around it (i.e. instead of computing margins and compressing the plot area, we just know the plot area and the margins are incidental); this ignores the Padding property.
  • Patched ImageAnnotation to support the PlotBounds because it's in the main assembly, but haven't touched anything else that depends on PlotModel.Width and Height

The main differences between RenderOnRect and just messing about with margins are the positioning of the title and the fact that RenderOnRect completely ignores padding and margins.

I don't expect anyone wants this code to end up in Develop, but it might be useful for somebody out there.

Example code and a picture can be found in this gist.

@VisualMelon
Copy link
Contributor

More musings...

A possible start?

A good start might be to incorporate the PlotBounds concept from above (i.e. replace or completement IPlotModel.Render(IRenderContext rc, double width, double height) with IPlotModel.Render(IRenderContext rc, OcyRect plotBounds)), so that when a PlotModel is plotted it can be positioned (without having to wrap RenderContexts), and to provide a method to 'PerformLayout' the plot model (i.e. do everything that RenderOverride does, but without the last step of actually rendering). (This would still depends on an IRenderContext for measuring text and such; maybe that dependency could be removed, allowing a total separation of these concerns).

This would make it fairly trivial to write a 'multi-plot' layer on top, which can measure plots and fix the margins as appropriate if they need to line up. This layer itself could be an IPlotModel, so it could be dropped straight when exporting plots (i.e. those that depend only upon IPlotModel). Interactive plots present a problem, because IPlotViews need a PlotModel

The work has already been done in my aforementioned PlotModel Bounds branch, so I don't beleive there is any technical difficulty in the core library. I'd be happy to 'own' this task, and to do sort out the implementation.

The question remains whether IPlotModel should adopt this interface or not. I'd be inclined to say 'yes', but we don't want to necessarily require every exporter to observe it, so keeping the existing width & height method would be worth it from a backward-compabaitility point of view.

I think these would be valuable changes, because they alone would go a long way to enabling collections of non-interactive plots, and pulling apart the layout and rendering code would be valuable for a few reasons beyond maintainability.

Interactive Multi-Plots

I don't believe it makes any sense (nor would be feasible) to change the job of PlotModel, or indeed IPlotModel. Rather than trying to coerce PlotModel into doing the job of multiple PlotModels when attached to an IPlotView, I'd be strongly inclined to create a new IMultiPlotInterface which represents a collection of plots (and their associated plot-bounds), and an IMultiPlotView interface. Interactive UI implementations would have to provide a class implementing IMultiPlotView which handles an IMultiPlotModel, arranging the sub-plots accordingly. (The names are just what I'm sketching on paper, I'm not attached to them).

This sounds unpleasant, because it means UI platforms have to provide 2 UI components: one for PlotModel and one for IMultiPlotModel. However, it allows IMultiPlotModel to perform layout-work, which is nice, and a 'null' MultiPlotModel wrapping a single PlotModel means that new code could depend only on the Multi-Plot components by wrapping lone PlotModels if they want. I'd also imagine the IMultiPlotModel interface and IMultiPlotView implementations would be very thin, as they could defer the non-layout work to PlotModels and PlotViews.

This only allows a single-layer of nesting: you can't place sub-plots within sub-plots. Perhaps, though, this makes sense, and any hierarchical structure should be handled by the particular IMultiPlotModel implementation, and isn't something the MultiPlotView need care about.

I don't imagine IMultiPlotModel extending IPlotModel, since its job is layout, not rendering. A class could be provided which exposes an IMultiPlotModel as an IPlotModel, which can be used to wrap IMultiPlotModels for export.

The ability to position and 'measure' PlotModels (i.e. the suggested start) will be valuable for quick-and-dirty implementations of IMultiPlotModel, which will want to measure and align margins. Making more of the internals of PlotModel's layout methods available might enable a finer-level control (e.g. calling AdjustPlotMargins across multiple plots in order to align all the margins would be much tidier than measuring them all, and then messing with their margins thereafter). Decoupling the layout and render code is a necessary first step for this sort of thing.

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

No branches or pull requests

4 participants