3dscatter plot png export doesn't keep the current view

I have in my Dash app a 3d scatter plot in a modal, and I want to export it using the modebar “Download plot as a png” button.
When I first generate the plot in a modal it works fine and the exported image is exactly what I am seeing (same zoom level and orientation).
However, when I copy the figure from the first dcc.Graph to another one in a second modal, the export option now always renders the view from the first figure, even when I change the zoom or orientation.

Here is a quick video example:

So it works fine in the first modal, but when I use the “Format graph” link that leads to the second modal, the exported view is still the one from the first modal.
However, I can change the font size, toggle the legend, toggle the grid, etc. with the menu in the second modal’s footer and those changes will be visible in the exported graph.

So it really only is the view changes that are not registered after the graph is created in the second modal, changing the figure layout parameters does work.

In fact, I don’t know if it is important, but when I change the layout parameters via the footer menu, the plot’s view resets to the default view (the one exported).
Could that indicate something?
Here is an example where I change the marker size:

I did try to recreate the bug with a minimal working example, but everything seems to work fine, even if I use the same callback structure and the exact same figure and config…

from dash import Dash, dcc, Input, Output, State, no_update
import plotly.graph_objects as go
import dash_bootstrap_components as dbc

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

figure = go.Figure()
figure.add_scatter3d(x=[1,2,3], y=[1,2,3], z=[1,2,3])

config = {
    'scrollZoom': True, 
    'toImageButtonOptions': {
        'format': 'png', 'scale': 2}, 
    'editable': True

app.layout = dbc.Container([
        dbc.ModalHeader("Modal 1"),
        dcc.Graph(id="graph_1", figure=figure, config=config),
        dbc.ModalFooter(dbc.Button("Open modal 2", id="toggle_btn"))
    ], is_open=True),
        dbc.ModalHeader("Modal 2"),
    ], id="modal_2", size="xl"),

@app.callback(Output("modal_2", "is_open"),
              Output("graph_2", "figure"),
              Output("graph_2", "config"),
              Input("toggle_btn", "n_clicks"),
              State("graph_1", "figure"),
              State("graph_1", "config"))
def open_modal_2_and_add_graph(n_clicks, fig, config):
    if not n_clicks:
        return False, no_update, no_update
    return True, fig, config

if __name__ == "__main__":
    app.run(port=8054, debug=True)

Would anyone have an idea as to why this happens? I sort of need this function to work because the default view usually is not ideal, and changing it beforehand in the first modal is not exactly intuitive.

Thanks a lot


I had a similar problem for a different use case scenario.
It worked fine for the first use of the callback, and then it stopped.

I found out that, for some reason, the “camera” information from the figure just disappeared.
I didn’t find “why” it was happening.

But to solve it, I wrote the camera info when the figure is created.
Something like " fig[“layout”][“scene”][“camera”]= {put initial camera parameters here}

Thanks! It did help a little.
I have set the default camera eye property when creating my figure in the second modal. I can now rotate the camera and download the figure with the updated rotated view, but only if I update my figure’s layout using my options such as “toggle grid”.
So it seems the camera angle is only registering after a layout update.
The “uirevision” property is also needed to prevent the layout update from resetting the camera.