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

Add subplot macro for basic subplots #34

Merged
merged 11 commits into from
Dec 20, 2018
Merged

Conversation

Vindaar
Copy link
Member

@Vindaar Vindaar commented Dec 18, 2018

This adds basic subplot support via the new subplots macro.

A basic implementation for that was quite easy, but then I realized that for real subplots it might be justified to have Plot[T] objects as a basis with different T. Therefore I introduced a PlotJson object, which is basically the Plot[T] object albeit with the two fields already converted to JsonNodes. This way we can merge the two Plot[T] of potentially different data types together.
The main part of the creation of subplots is then the (currently pretty static and hacky) combine proc. Since the signature of combine isn't very nice and in my opinion the user doesn't need to know what's needed to create a subplot, I started to write a macro. In the end it's a lot of lines for a single output line of the macro, but well. It's there now. :P
Usage looks like this (assuming we have a plt1: Plot[T], plt2: Plot[U], layout: Layout):

let subplt = subplots:
  baseLayout: layout
  plot:
    plt1
    left: 0.0
    bottom: 0.0
    width: 0.45
    height: 1.0
    # alternatively use right, top instead of width, height
    # single letters also supported, e.g. l == left
  plot:
    plt2
    # or just write a concise tuple, here the
    (0.55, 0.0, 0.45, 1.0)

So it consists of a layout object to be used as the base layout for the whole plot and then several plot blocks. Each plot block then just takes a Plot[T] as the first line and then a description of the plot location. Either as a nameless tuple of the type:
(left, bottom, width, height)
in relative coordinates of the canvas in [0, 1]. Or one can specifically set each of the fields, one per line. That way one may also use right, top instead of width, height (for good measure instead of :, = is also supported, and so are just first letters for each location, i.e. l, b, ...).

Useful sideffect of PlotJson

As a side effect, since PlotJson is now a valid type for show and saveImage one can thus easily extend plotly "on the fly" if a feature isn't available. So for instance if we have some

let plt: Plot[T] = getSomePlot()

and we wish to use a feature that's not in nim-plotly: for example to set the length of the ticks. In plotly this is done via ticklen (https://plot.ly/javascript/reference/#layout-yaxis-ticklen). Now we can just do:

let pltJson = plt.toPlotJson
pltJson["xaxis"]["ticklen"] = 10 # twice as long as default
pltJson.show()

and all should work.
In theory one can also just copy paste some plotly.js code and create a JsonNode via the %* macro and with little change one can even create a plot that way. If this nim-lang/Nim#10037 PR would be merged that would become almost trivial.
Not very useful in general, but for quick prototyping of unavailable features might come in handy.

Basically `PlotJson` is just a `Plot[T]` object, where the fields of
it have been converted to `JsonNodes`. This allows for much easier
handling of:
- multiple traces of different data types
- extension of `PlotJson` by manually adding json fields, which are
not yet supported by nim plotly and still being able to hand the
resulting `PlotJson` to the `show` or `saveImage` procs
# given in relative coordinates of the plot [0, 1] canvas
Domain* = tuple
left, bottom, width, height: float
DomainAlt* = tuple
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you document DomainAlt? I'm not sure what it is/does.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DomainAlt simply refers to the alternate representation of a domain, which uses right and top instead of width and height. I simply introduced these as two named types to clear up the proc signatures.
https://github.com/brentp/nim-plotly/blob/6a897fa35ad1291da1c43140bd81655feed1563f/src/plotly/plotly_subplots.nim#L4-L15
then is used to convert DomainAlt to a normal Domain. I should maybe add a comment to the DomainAlt line though (and now that I look at the code for convertDomain, use Domain in the type check there. Will do both tomorrow.

@brentp
Copy link
Collaborator

brentp commented Dec 19, 2018

this is really neat! Looks good to me after documenting DomainAlt.

@Vindaar
Copy link
Member Author

Vindaar commented Dec 19, 2018

Will add a line about DomainAlt, replace the explicit tuple... in convertDomain and add a few lines of explanations for the macro usage in the example tomorrow. :)

@brentp
Copy link
Collaborator

brentp commented Dec 19, 2018

cool! if you specify subplots without explicit domains, does plotly give you a reasonable default (multi) plot?

@Vindaar
Copy link
Member Author

Vindaar commented Dec 19, 2018

To be honest, I have no idea as I haven't tried it. But I do know that an alternative to explicit domain setting is the grid field (as in example 1 here: https://plot.ly/javascript/subplots/). If plotly doesn't use a good default, a grid might be a good alternative to use from our end as a default.
I'll give it a try.

@Vindaar
Copy link
Member Author

Vindaar commented Dec 20, 2018

Ok, all done now. Add the ability to set a grid aside from manually setting domains (though mixing isn't supported. No clue if plotly even itself supports that).
This is done via a grid block in the macro:

grid:
  rows: 2
  columns: 1

where either is optional. If only one is set, the other is assumed to be 1.
If domains nor a grid definition is given, we now default to a grid, which fits all plots, favoring more columns than rows.

@brentp brentp merged commit 47cca31 into SciNim:master Dec 20, 2018
@brentp
Copy link
Collaborator

brentp commented Dec 20, 2018

thanks! I actually have been needing this.

@Vindaar Vindaar mentioned this pull request Jan 30, 2019
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

Successfully merging this pull request may close these issues.

2 participants