grantmcdermott / tinyplot

Lightweight extension of the base R graphics system
https://grantmcdermott.com/tinyplot
Apache License 2.0
208 stars 7 forks source link

custom `mfrow` settings are not respected for grouped plots #65

Closed grantmcdermott closed 8 months ago

grantmcdermott commented 11 months ago

Fine without a legend:

library(plot2)

par(mfrow = c(1,2))
plot2(mpg ~ wt, mtcars)
plot2(wt ~ mpg, mtcars)

Not fine with a legend:


par(mfrow = c(1,2))
plot2(mpg ~ wt | cyl, mtcars)

plot2(wt ~ mpg | cyl, mtcars)

Created on 2023-08-15 with reprex v2.0.2

grantmcdermott commented 11 months ago

Stepping through the debugger, the problem appears to stem from the various par(omd = ...) calls in setting up the legend, (e.g. here) which are somehow (?) changing par("mfg") at the same time. (Maybe par("new") too?)

So, it ends up calling two separate plot windows instead of composing them together, which is why we have two right-aligned plots in the reprex above.

I don't really understand why it's doing this and fixing may require more duct tape than is worth... especially if we're going to be passing custom par(mfrow = ..., mfg = ...) settings internally for facets.

zeileis commented 11 months ago

Is it really the par(...)? I thought it was the plot.new() that causes the step to the next panel in mfrow.

grantmcdermott commented 11 months ago

Yeah I'm really puzzled by this behaviour.

I might simply be conflating separate issues, but please free to test on your side. (E.g. Set a browser breakpoint here and run my second example above. If I do so and call par("mfg") immediately before and immediately after par(omd = c(0, 1-w, 0, 1)) I get different results.)

zeileis commented 11 months ago

Interesting, I wasn't aware of that either. If one of the outer par() settings is touched, it seems to reset the plot somehow. Other parameters can be altered. For example, this is ok:

par(mfrow = c(1, 2))
plot(1:5)
par(mar = c(8, 6, 1, 1))
plot(5:1)

But this re-initializes the cycling through the panels:

par(mfrow = c(1, 2))
plot(1:5)
par(omd = c(0, 1, 0, 1))
plot(5:1)

Maybe the idea is that while cycling through the panels, we may only alter those parameters that are relevant within the panel but not across the panels?