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

[classlib] Plotter: add multiple concurrent modes #4459

Merged
merged 4 commits into from
Mar 1, 2020

Conversation

mtmccrea
Copy link
Member

@mtmccrea mtmccrea commented Jun 19, 2019

Purpose and Motivation

This PR adds the ability to specify multiple plot modes, as you can with plot colors.
It follows the same implementation as plotColors for consistency. This includes making a plotMode variable local to Plot, and settable through Plotter via -plotModes_.

Implementation Notes to review

  • -plotMode (singular) getter and setter methods are left for backwards compatibility. This also offers syntax that suit the common case of a single plot (-plotMode calls -plotModes internally).
  • In the same way that with plotColors stores all the plot colors internally in an array, plotModes an array of modes, even when it's a single mode. The getter method will return this array, unless there is only one mode or all modes of a multi-channel plot are identical, in which case it just returns the Symbol of the mode. This mirrors the way all modes can be set by a single mode Symbol.

Open questions (Update: resolved):

  • As there are both -plotModes and -plotMode (singular) getters and setters, should this also be the case for -plotColors? Should a -plotColor (singular) getter and setter be added to Plotter?
    • If so, the same question stands for -specs and -domainSpecs get/setters—should their singular counterparts (synonyms essentially) be added?
    • The rationale is that despite supporting multiple specs, colors, modes, etc within a Plotter, a common case is a Plotter that just contains one Plot, for which it makes more semantic sense to say, e.g. myPlotter.domainSpec_([33, 45].asSpec)
  • The argument to -plotColors_ was changed from argColors to colors, which is technically a breaking change, but this method was only recently introduced in [classlib] Plotter: fix domain and superpose behavior. #4082, merged into 3.10, but has the 3.10.3 milestone, so isn't yet in the main distribution.
    • I used the name argColors originally because the the nearby argSpecs argument name, but after a closer look I see that's because there is an instance variable specs so argSpecs was to differentiate it from that variable name. This is not the case with argColors, so I decided to go with the cleaner name.

Types of changes

  • Documentation
  • New feature

To-do list

  • Code is tested
  • All tests are passing
  • Updated documentation
  • This PR is ready for review

@mtmccrea mtmccrea mentioned this pull request Jun 19, 2019
4 tasks
Copy link
Member

@telephon telephon left a comment

Choose a reason for hiding this comment

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

The code is well written.

Are you sure that the semantic differentiation between plotMode and plotModes is worth the trouble? In sclang, we often treat arrays the same as elements, and I can't remember where I've seen a case in which we keep both singular and plural forms spearately as methods.

I'd prefer if plotMode and plotModes were synonyms.

The same goes for plotColors. There is now an instance variable both in Plotter and in Plot.

I think we should briefly get clear on where all the state is kept. Right now it is confusing (it might have been a bit confusing already before).

@telephon
Copy link
Member

Currently, the number of channels (and therefore the number of Plots) within a Plotter can change dynamically. The original reason to keep all the state in arrays in the Plotter was that it can be set once and then the values can change underneath.

a = Plotter.new;

a.plotColors = [Color.green, Color.blue, Color.red];

a.value = Env.perc(0.3, 0.7).discretize;
a.value = Env.perc([0.3, 0.4, 0.8], [0.7, 0.6, 0.2]).discretize;
a.value = [[1, 2], [18, 9]];

Because every Plot has only one channel, the clearest is to either:

  1. keep the specifications (like plotColors) in Plotter, and refer to it from the Plot
  2. or have a separate set of instance variables in Plot and copy the information at its index into the Plot

(2) is more efficient, (1) is easier to understand and maintain. If we don't run into efficiency problems, (2) might be the best (I am sorry if I gave a different recommendation earlier).

@mossheim mossheim added the comp: class library SC class library label Jun 21, 2019
@mtmccrea
Copy link
Member Author

mtmccrea commented Jun 24, 2019

Sorry to drag on this, but I knew it would take some unwinding to get an idea of the scope of problem.

I think we should briefly get clear on where all the state is kept. Right now it is confusing (it might have been a bit confusing already before).

Yea... the state is smeared across both objects pretty well 😕 For the sake of background, I did a deep dive on how the state is managed, which I'll put below, but to keep this discussion on the practical issue of method/variable names...

In sclang, we often treat arrays the same as elements, and I can't remember where I've seen a case in which we keep both singular and plural forms spearately as methods.
I'd prefer if plotMode and plotModes were synonyms.
The same goes for plotColors. There is now an instance variable both in Plotter and in Plot.

This is helpful perspective! To clarify, you'd like to see if specs, domainSpecs, plotModes, and plotColors could all be removed and only the singular form is used?
I'll have a look at how far back the history goes with the plural state vars and methods to see if changing everything to singular would break anything.

State storage in Plot vs. Plotter

Here are some of the state variables that have some aspects of being shared between the two (omitting states which should clearly live in one instead of the other, like superpose in Plotter).

state Plotter Plot Note
value x x constant in Plotter, changes with superpose in Plot
spec . x Plot only: singular, stored as an array
specs x . Plotter only: plural, stored as an array
domainSpec . x Plot only: singular, stored as an array
domainSpecs x . Plotter only: plural, stored as an array
plotMode x x Both, stored as an array
plotModes x . Plotter only, get/setter method acting on plotMode
plotColor . x Plot only: singular, stored as an array
plotColors x . Plotter only: plural, stored as an array
resolution x . Plotter only
domain x . Plotter only

Further, all these parameters belong exclusively to Plot

  • font, fontColor, gridColorX, gridColorY, backgroundColor, gridOnX, gridOnY, labelX, labelY

So if you want to set any of those, you need to access Plotter.plots[n], setting each one if superpose == false. IMO, because Plot doesn't seem to be intended to be accessed directly, it would be desirable to be able to change all these parameters through Plotter.

The original reason to keep all the state in arrays in the Plotter was that it can be set once and then the values can change underneath.

This seems sensible, but the state is also stored in Plot. Depending on the state of superpose, the data/value is either the same object as that in Plotter or a flopped copy:

p = 4.collect({15.collect({rrand(0,1.0)})}).plot;
p.plotColors_(4.collect{Color.rand}); p.refresh;

p.superpose = false;
p.value[0] === p.plots[0].value // true
p.superpose = true;
p.value[0] === p.plots[0].value // false

Storing the same data multiple ways also happens in Plotter

p.superpose = false;
p.data === p.value; // true
p.superpose = true;
p.data === p.value; // false

As an aside, I'd imagine this is pretty inefficient for large datasets.

Because every Plot has only one channel

This is also complicated by superpose:

p = 4.collect({15.collect({rrand(0,1.0)})}).plot;
p.plotColors_(4.collect{Color.rand}); p.refresh;

// observe Plotter's value wrt superpose
p.superpose = true;
p.value.shape
// -> [4, 15]
p.superpose = false;
p.value.shape
// -> [4, 15] // remains constant when superposed

// observe Plot's value wrt superpose
p.superpose = false;
p.plots 
// -> [ a Plot, a Plot, a Plot, a Plot ] // four plots
x = p.plots[0].value // value at first plot
x.shape 
// -> [15]  // first channel of data in the first plot

p.superpose = true;
p.plots 
// -> [ a Plot ] // one plots
x = p.plots[0].value // value at first plot
x.shape 
// -> [15, 4] // all data stored in EACH plot, and multichannel data flopped (therefor copied) when superposed

Because Plot is actually at times one channel, and at other times many channels, I would think that Plot should be a lot simpler and just do the work of drawing the data stored in Plotter, and request it's state from Plotter as well, based on its index. I think this would look like a combination of both 1. and 2. you suggested above. But that also means migrating all those state vars that exist solely in Plot to Plotter and having a consistent array storage for each of them. Better yet, if each Plot were truly always one channel and simply drawn in separate overlaid UserViews when superposed.

In any case, I'm not sure what the larger overhaul would look like exactly, but that seems beyond the scope of this PR.

@telephon
Copy link
Member

Thank you very much indeed for this careful analysis. If I remember correctly, the state in the Plot is mainly meant as optimization, but this may not be required.

I'd suggest that you simplify this PR a little (no new plural forms for now, or only plural, whatever seems better), and then you could propose a refactoring as you have sketched above already. Would that be a good way to proceed? Alternatively, if you don't have the time to do this, you can also make an issue ticket with the proposed refactoring in this thread.

@telephon
Copy link
Member

This is helpful perspective! To clarify, you'd like to see if specs, domainSpecs, plotModes, and plotColors could all be removed and only the singular form is used?

I would use singular terms everywhere, and only add plural ones for backward compatibility where they are plural now.

I'll have a look at how far back the history goes with the plural state vars and methods to see if changing everything to singular would break anything.

We can then see if we deprecate the plural forms, but I don't mind to let them in if commented. The plotter is just a tool, so we don't build up much secondary code that depends on it. At the same time, probably there are a lot of snippets around which may break for people.

@mtmccrea mtmccrea force-pushed the topic/plotter-plotmodes branch from 0d93b35 to aa930c6 Compare July 1, 2019 01:36
@mtmccrea
Copy link
Member Author

mtmccrea commented Jul 1, 2019

I would use singular terms everywhere, and only add plural ones for backward compatibility where they are plural now.

OK, I've made these changes so that there are no plural method additions.

I've also modified plotColors to use the singular, because this still lives in 3.10.3 (introduced in #4082) and hasn't been released. That means then that this PR would also need to go out with 3.10.3 to make sure the plural plotColors doesn't go out. I'm not sure where we are on the 3.10.3 release stage, is this manageable?

I also updated docs to reflect these changes.

I'd suggest that you simplify this PR a little (no new plural forms for now, or only plural, whatever seems better), and then you could propose a refactoring as you have sketched above already. Would that be a good way to proceed? Alternatively, if you don't have the time to do this, you can also make an issue ticket with the proposed refactoring in this thread.

I think this is a good way forward. I'll put together an issue that documents the points to be addressed in a future refactor.

Thanks, Julian!

@mtmccrea
Copy link
Member Author

mtmccrea commented Jul 1, 2019

Also note that I left the arguments to both plotMode_ and plotColor_ as plural.

	plotColor_ { |colors|
	plotMode_ { |modes|

This seemed OK to me to suggest that the argument need not be a single mode/color. But I'm OK changing those arg names to be singular if you think it's better.

@mtmccrea
Copy link
Member Author

mtmccrea commented Jul 8, 2019

Hi @telephon, just checking in to see if you'd like any further changes with this PR. Thanks!

Copy link
Member

@telephon telephon left a comment

Choose a reason for hiding this comment

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

This looks ok to me now.

Copy link
Member

@dyfer dyfer left a comment

Choose a reason for hiding this comment

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

I'm having a weird problem testing this.
Something in domainSpecs is sometimes breaking, but I can't pinpoint it. Reproducer:

a = { (0..100) }.dup(2).plot;
a.domainSpecs = [-6, 10.6].asSpec; a.refresh; // ERROR: Message '@' not understood.
//but these are fine:
a.domainSpecs = [-6, 10.7].asSpec; a.refresh; 
a.domainSpecs = [-5, 10.6].asSpec; a.refresh;

Can't find where the problem is? Some kind of rounding issue?

EDIT: I tested with a different build and this error went away, it must've been a weird interaction between the SC version I was testing this on (just swapped class library). This PR looks good now to me (aside from a minor change below). I think we need to wait for #4678 to go in, merge 3.10 into develop and then rebase this to resolve conflicts.

HelpSource/Classes/Plotter.schelp Outdated Show resolved Hide resolved
@dyfer dyfer self-assigned this Dec 9, 2019
@dyfer dyfer mentioned this pull request Dec 15, 2019
4 tasks
@mtmccrea mtmccrea force-pushed the topic/plotter-plotmodes branch from aa930c6 to ac1b556 Compare December 21, 2019 09:58
@dyfer
Copy link
Member

dyfer commented Dec 21, 2019

PLEASE DON'T MERGE YET

I think we need to wait for #4678 to go in, merge 3.10 into develop and then rebase this to resolve conflicts.

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

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

marking as changes requested to avoid accidental merge, see above comment

@mossheim
Copy link
Contributor

please rebase after #4678

@mtmccrea mtmccrea force-pushed the topic/plotter-plotmodes branch from 9ad5e80 to ccd7d86 Compare January 30, 2020 20:21
@mtmccrea
Copy link
Member Author

OK, it's been rebased and I think the conflicts have been resolved properly.

@dyfer

@dyfer
Copy link
Member

dyfer commented Jan 30, 2020

Looks like this PR now includes sc-el updates (?). I'm not sure why/how that happens, submodules are still quite a mystery to me.

@brianlheim since you marked it as changes requested (to block merging before conflict resolution) I think it would be great if you gave it a green light. The only remaining question is whether anything needs to be done about sc-el...

edit: maybe this needs git submodule update --recursive?

@mtmccrea mtmccrea force-pushed the topic/plotter-plotmodes branch from ccd7d86 to 540d1b7 Compare January 31, 2020 15:24
@mtmccrea
Copy link
Member Author

Looks like this PR now includes sc-el updates (?).

Thanks for catching that @dyfer. I rewound and removed those sneaky submodule changes. It actually made the commit history more concise anyway.

@dyfer
Copy link
Member

dyfer commented Feb 13, 2020

AFAICT this is ready to be merged.

This PR has been reviewed before by @telephon and myself and was waiting to be rebased after #4678 got in, which has indeed happened. @brianlheim would you mind giving this a green light?

@mossheim
Copy link
Contributor

@dyfer sorry for the delay. i've simply dismissed my review, would prefer others to manage this PR. i don't have much interest in it and seems other people already know it better :)

@dyfer dyfer merged commit b03eed4 into supercollider:develop Mar 1, 2020
@dyfer dyfer mentioned this pull request Mar 19, 2020
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp: class library SC class library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants