using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; using Plotly; using Plotly.Types; using TRACES = Plotly.Box; using VERTEX = SharpGLTF.Geometry.IVertexBuilder; using VERTEXBUILDER = SharpGLTF.Geometry.VertexBuilder; namespace SharpGLTF { public class PlotlyScene { #region data private readonly List _Traces = new List(); private readonly Dictionary _Materials = new Dictionary(); // materials to color mapping #endregion #region API public void AppendTriangles(IEnumerable<(VERTEX A, VERTEX B, VERTEX C, TMaterial Material)> tris, Func materialColorFunc) { var trace = _CreateTrace(tris, materialColorFunc); _Traces.Add(trace); } public void AppendTriangles(IEnumerable<(Vector3 A, Vector3 B, Vector3 C, TMaterial Material)> tris, Func materialColorFunc) { (VERTEX A, VERTEX B, VERTEX C, TMaterial Material) _Convert((Vector3 A, Vector3 B, Vector3 C, TMaterial Material) tri) { var ab = tri.B - tri.A; var ac = tri.C - tri.A; var n = Vector3.Normalize(Vector3.Cross(ab, ac)); var aa = new VERTEXBUILDER((tri.A, n)); var bb = new VERTEXBUILDER((tri.B, n)); var cc = new VERTEXBUILDER((tri.C, n)); return (aa, bb, cc, tri.Material); } var trace = _CreateTrace(tris.Select(_Convert), materialColorFunc); _Traces.Add(trace); } public Box ToPlotProperties() { return Plot.traces(_Traces.ToArray()); } public Plot ToPlot() { var plot = ToPlotProperties(); var layout = _CreateLayoutProperties(); var document = new Plot(plot, layout); return document; } public string ToHtml() { Plot document = ToPlot(); return document.Render().ToString(); } #endregion #region core private static Box _CreateLayoutProperties() { var xaxis = Scene.xaxis(Xaxis.color("red")); var yaxis = Scene.yaxis(Yaxis.color("green")); var zaxis = Scene.zaxis(Zaxis.color("blue")); var camera = Camera.up(Up.x(0), Up.y(1), Up.z(0)); var scene = Layout.scene(Scene.Aspectmode.data(), Scene.camera(camera), xaxis, yaxis, zaxis); return Plot.layout (Layout.autosize(true) // , Layout.width(0) , Layout.height(920) // , Layout.margin(Margin.autoexpand(true)) // , Layout.margin(Margin.pad(5)) // , Layout.margin(Margin.t(5), Margin.b(5)) , scene ); } private TRACES _CreateTrace(IEnumerable<(VERTEX A, VERTEX B, VERTEX C, TMaterial Material)> tris, Func materialColorFunc) { var vrts = new List<(Vector3 p, Vector3 n)>(); // vertex list var vrtm = new Dictionary<(Vector3 p, Vector3 n), int>(); // vertex sharing map var idxs = new List<(int, int, int)>(); // triangle indices var tric = new List(); // face colors int _useSharedVertex(VERTEX v) { var g = v.GetGeometry(); g.TryGetNormal(out Vector3 n); var item = (g.GetPosition(), n); if (vrtm.TryGetValue(item, out int idx)) return idx; idx = vrts.Count; vrts.Add(item); vrtm.Add(item, idx); return idx; } foreach (var (A, B, C, Material) in tris) { if (!_Materials.TryGetValue(Material, out int color)) { _Materials[Material] = color = materialColorFunc(Material); } var ap = _useSharedVertex(A); var bp = _useSharedVertex(B); var cp = _useSharedVertex(C); idxs.Add((ap, bp, cp)); tric.Add(color); } // create a Plotly Mesh3D from the previously filled lists. var mx = Mesh3d.x(vrts.Select(item => item.p.X)); var my = Mesh3d.y(vrts.Select(item => item.p.Y)); var mz = Mesh3d.z(vrts.Select(item => item.p.Z)); var mi = Mesh3d.i(idxs.Select(item => item.Item1).ToArray()); var mj = Mesh3d.j(idxs.Select(item => item.Item2).ToArray()); var mk = Mesh3d.k(idxs.Select(item => item.Item3).ToArray()); var mo = Mesh3d.opacity(1); var mc = Mesh3d.facecolor(tric.ToArray()); return Traces.mesh3d(mx, my, mz, mi, mj, mk, mo, mc); } #endregion } }