r-wasm / webr

The statistical language R compiled to WebAssembly via Emscripten, for use in web browsers and Node.
https://docs.r-wasm.org/webr/latest/
Other
857 stars 67 forks source link

Unless `captureGraphics` is turned off, plot output to devices other than the canvas gets swallowed #482

Open christianp opened 1 month ago

christianp commented 1 month ago

I've made an example at https://webr-issue-482-capturegraphics.think.somethingorotherwhatever.com/.

With the automatic canvas graphics capturing left turned on, graphics plotted to other devices such as SVG are lost if the initialisation of the device, the plot call, and dev.off() are in separate calls. The SVG file is created, but never filled.

Is it the case that with captureGraphics: true, a new canvas device is created for each call to Shelter.captureR?

georgestagg commented 1 month ago

Is it the case that with captureGraphics: true, a new canvas device is created for each call to Shelter.captureR?

Yes, currently the following scheme is performed for each call to captureR(), when captureGraphics: true:

So, setting captureGraphics: false means that your svg() device is made the active device in one call and then remains the active device over all the other calls until dev.off().

Do you think this behaviour should be different? I guess it could be made so that a canvas() device is only spun up if there are no other graphics device already on the stack. Is that the behaviour you were expecting?

christianp commented 1 month ago

Yeah, I think that if there's already an active graphics device then the auto-capturing shouldn't interfere with it.

I don't know much about R though, so if people have reasons to have a long stack of graphics devices and would benefit from the current behaviour, I suppose I could do the check myself and set captureGraphics appropriately.

georgestagg commented 1 month ago

It's worth noting that the current behaviour matches that of the {evaluate} package. It has a similar setup when new_device = TRUE (the default) where a new capturing graphics device is started at the beginning of evaluation.

Non-wasm example:

$ R
[...]
> svglite::svglite()
> capture <- evaluate::evaluate("plot(123)")
> dev.off()
null device 
          1 
> q()
Save workspace image? [y/n/c]: n

$ cat Rplot001.svg 
$ wc -c Rplot001.svg 
       0 Rplot001.svg

So for consistency I think I'd like to match that behaviour by default.

Though I think we could maybe add an option to switch to the suggested alternative behaviour for Shelter.captureR().