plotly / Plotly.NET

interactive graphing library for .NET programming languages :chart_with_upwards_trend:
https://plotly.net
MIT License
663 stars 88 forks source link

Wrong position for 3D Plot with more then one 3D plot in a Grid #413

Closed Nick135 closed 5 months ago

Nick135 commented 1 year ago

Description

Wrong position for 3D plot when more than one 3D plot in a grid. All 3D plots are on top of each other at the position of the last 3D plot. 2D plot works. Also if I mix 2D and 3D plots in a grid, all 2D plot are right and the 3D plots have the problem.

Repro steps

#r "nuget: Plotly.NET, 4.2.0"
#r "nuget: Plotly.NET.Interactive, 4.2.0"
#r "nuget: Plotly.NET.CSharp"

using Plotly.NET.CSharp;

var testData = new[]{1.0,1.1,2.0};

var y = testData.Select(s=>s*-1).ToList();
var s1 = Chart.Scatter3D<double, double, double, string>(
            x: testData,
            y: y,
            z: testData.Select(s=>s*1.2).ToList(),
            mode: Plotly.NET.StyleParam.Mode.Lines_Markers,
            Name: "Plot 1 - 3D"
        );
var s2 = Chart.Point<double, double, string>(
            x: testData,
            y: y,
            Name: "Plot 2 - 2D"
        );
var s3 = Chart.Scatter3D<double, double, double, string>(
            x: testData,
            y: y,
            z: testData.Select(s=>s*2.1).ToList(),
            mode: Plotly.NET.StyleParam.Mode.Lines_Markers,
            Name: "Plot 3 - 3D"
        );

Chart.Grid(new[]{s1,s2,s3}, 3,1).Show();

Expected behavior

Position of 3D plots should be right

Actual behavior

2D plot works. Also if I mix 2D and 3D plots in a grid, all 2D plot are right and the 3D plots have the problem.

8c82410a-48db-497a-870d-3ed65d2c2e33.zip

image

Related information

kMutagene commented 1 year ago

Hey @Nick135, thanks for reporting this. I can confirm this as a regression that seems to occur somewhere between 3.0 and 4.0. It seems to be specific for adding multiple 3D charts to a grid, as a complex example with many different trace types works (see https://plotly.net/chart-layout/multiple-charts.html#Using-subplots-of-different-trace-types-in-a-grid). Adding a second 3D chart breaks the grid in that example.

For now, a quick workaround would be falling back to pre 4.0 versions like this:

#r "nuget: Plotly.NET, 3.0.0"
#r "nuget: Plotly.NET.CSharp, 0.8.0"
#r "nuget: Plotly.NET.Interactive, 3.0.0"

image

Nick135 commented 1 year ago

When I add WithXAxisStyle to both 3D Plots, I also get the problem with the v3.0.

#r "nuget: Plotly.NET, 3.0.0"
#r "nuget: Plotly.NET.CSharp, 0.8.0"
#r "nuget: Plotly.NET.Interactive, 3.0.0"

using Plotly.NET.CSharp;

var testData = new[]{1.0,1.1,2.0};

var y = testData.Select(s=>s*-1).ToList();
var s1 = Chart.Scatter3D<double, double, double, string>(
            x: testData,
            y: y,
            z: testData.Select(s=>s*1.2).ToList(),
            mode: Plotly.NET.StyleParam.Mode.Lines_Markers,
            Name: "Plot 1 - 3D"
        )
        .WithXAxisStyle<string, string, string>(TitleText: "x1", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(1))
        .WithYAxisStyle<string, string, string>(TitleText: "y1", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(1));
var s2 = Chart.Point<double, double, string>(
            x: testData,
            y: y,
            Name: "Plot 2 - 2D"
        );
var s3 = Chart.Scatter3D<double, double, double, string>(
            x: testData,
            y: y,
            z: testData.Select(s=>s*2.1).ToList(),
            mode: Plotly.NET.StyleParam.Mode.Lines_Markers,
            Name: "Plot 3 - 3D"
        )
        .WithXAxisStyle<string, string, string>(TitleText: "x2", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(1))
        .WithYAxisStyle<string, string, string>(TitleText: "y2", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(1));

Chart.Grid(new[]{s1,s2,s3}, 3,1).Show();

image

bvenn commented 1 year ago

Setting unique Axis ids seems to fix this behaviour.

Details ```csharp #r "nuget: Plotly.NET, 3.0.0" #r "nuget: Plotly.NET.CSharp, 0.8.0" #r "nuget: Plotly.NET.Interactive, 3.0.0" using Plotly.NET.CSharp; var testData = new[]{1.0,1.1,2.0}; var y = testData.Select(s=>s*-1).ToList(); var s1 = Chart.Scatter3D( x: testData, y: y, z: testData.Select(s=>s*1.2).ToList(), mode: Plotly.NET.StyleParam.Mode.Lines_Markers, Name: "Plot 1 - 3D" ) .WithXAxisStyle(TitleText: "x1", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(1)) .WithYAxisStyle(TitleText: "y1", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(1)); var s2 = Chart.Point( x: testData, y: y, Name: "Plot 2 - 2D" ); var s3 = Chart.Scatter3D( x: testData, y: y, z: testData.Select(s=>s*2.1).ToList(), mode: Plotly.NET.StyleParam.Mode.Lines_Markers, Name: "Plot 3 - 3D" ) .WithXAxisStyle(TitleText: "x2", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(2)) .WithYAxisStyle(TitleText: "y2", Id: Plotly.NET.StyleParam.SubPlotId.Scene.NewScene(2)); Chart.Grid(new[]{s1,s2,s3}, 3,1).Show(); ```

image

Nick135 commented 1 year ago

@bvenn: It looks like it works, but when you see the second 3D Plot, the "TitleText" is not set.

image

kMutagene commented 1 year ago

The root of this bug must be the assignment of new scene ids to the existing charts. The internal logic of Chart.Grid combines all charts and adds new unique axis, scene, ternary, etc. positions and ids. Here is the match case for 3D charts:

https://github.com/plotly/Plotly.NET/blob/11456b42a8875d3ddc6142e2f02129de229e5320/src/Plotly.NET/ChartAPI/Chart.fs#L3055-L3067

so setting a scene id other than 1 here will cause the issue of the contained axis styles not being added to the new scene, the same is true for all other chart types btw. This is by design, checking for any possible scene id (or axis id etc.) adds gigantic overhead to an already very complicated and hard to maintain function.

However, i cannot find striking differences in the code when e.g. comparing with the match case for Ternary traces;

https://github.com/plotly/Plotly.NET/blob/11456b42a8875d3ddc6142e2f02129de229e5320/src/Plotly.NET/ChartAPI/Chart.fs#L3133-L3148

which works fine:

#r "nuget: Plotly.NET, 4.2.0"
#r "nuget: Plotly.NET.Interactive, 4.2.0"
#r "nuget: Plotly.NET.CSharp"

using Plotly.NET.CSharp;

var testData = new[]{1.0,1.1,2.0};

var y = testData.Select(s=>s*-1).ToList();
var s1 = Chart.ScatterTernary<double, double, double, double, string>(
            A: testData,
            B: y,
            C: testData.Select(s=>s*1.2).ToList(),
            Mode: Plotly.NET.StyleParam.Mode.Lines_Markers,
            Name: "Plot 1 - Ternary"
        );
var s2 = Chart.Point<double, double, string>(
            x: testData,
            y: y,
            Name: "Plot 2 - 2D"
        );
var s3 = Chart.ScatterTernary<double, double, double, double, string>(
            A: testData,
            B: y,
            C: testData.Select(s=>s*1.2).ToList(),
            Mode: Plotly.NET.StyleParam.Mode.Lines_Markers,
            Name: "Plot 3 - Ternary"
        );

var g = Chart.Grid(new[]{s1,s2,s3}, 3,1);

g

image

kMutagene commented 1 year ago

I found the core issue in the output at least. The row number for the scenes is incorrect, while it is correct in the ternary example:

with Chart.Scatter3D:

"scene":{
  "camera":{"projection":{"type":"perspective"}},
  "domain":{"row":2,"column":0} <-- row should be 0
},
"scene3":{
  "camera":{"projection":{"type":"perspective"}},
  "domain":{"row":2,"column":0}
}

with Chart.ScatterTernary:

"ternary":{
  "domain":{"row":0,"column":0}
},
"ternary3":{
  "domain":{"row":2,"column":0}
},

which is really weird because at least to my eyes the code looks identical.

kMutagene commented 1 year ago

found the underlying issue, there are some fixes necessary that are deep in the low-level APIs, see #415 for details. This will be potentially backwards-incompatible and therefore be in v5.