using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; using TRACES = Plotly.Box; namespace SharpGLTF { public static class Plotting { public static Point2Series ToPointSeries(this IEnumerable points, string name = null) { return Point2Series.Create(points).WithName(name); } public static Point2Series ToPointSeries(this IEnumerable points, string name = null) { return Point2Series.Create(points).WithName(name); } public static Point2Series ToPointSeries(this IEnumerable points, string name = null) { return Point2Series.Create(points).WithName(name); } public static Point2Series ToLineSeries(this IEnumerable points, string name = null) { return Point2Series.Create(points, LineType.Continuous).WithName(name); } public enum LineType { Square = 0, Dot = 1, Cross = 2, Star = 3, Circle = 4, X = 5, Square2 = 6, Triangle = 7, CircleWithCross = 8, CircleWithDot = 9, CHAR_X = 88, CHAR_Y = 89, CHAR_Z = 90, CHAR_W = 87, Continuous = 65536 } public struct Point2 { public Point2(Double x, Double y) { this.X = x; this.Y = y; } public Point2(Vector2 v) { this.X = v.X; this.Y = v.Y; } public Double X; public Double Y; } public class Point2Series { #region lifecycle public static Point2Series Create(IEnumerable series, LineType lt = LineType.Continuous) { var points = series .Select((y, x) => (x, y)) .Where(item => item.y.IsFinite()) .Select(item => new Vector2(item.x, item.y)); return Create(points, lt); } public static Point2Series Create(IEnumerable series, LineType lt = LineType.Continuous) { var points = series .Select((y, x) => (x, (float)y)) .Where(item => item.Item2.IsFinite()) .Select(item => new Vector2(item.x, item.Item2)); return Create(points, lt); } public static Point2Series Create(IEnumerable points, LineType lt = LineType.Dot) { points = points.Where(item => item.X.IsFinite() && item.Y.IsFinite()); var ps = new Point2Series(); ps._Points.AddRange(points.Select(item => new Point2(item))); ps.LineType = lt; return ps; } public Point2Series WithName(string name) { Name = name; return this; } #endregion #region data private readonly List _Points = new List(); #endregion #region properties public string Name { get; set; } public LineType LineType { get; set; } #endregion #region API public Point2Series WithLineType(LineType t) { LineType = t; return this; } public void DrawToFile(string filePath) { DrawToFile(filePath, this); } private TRACES GetTrace() { var x = Plotly.Scatter.x(_Points.Select(item => (float)item.X)); var y = Plotly.Scatter.y(_Points.Select(item => (float)item.Y)); var mode = LineType == LineType.Continuous ? Plotly.Scatter.Mode.lines() : Plotly.Scatter.Mode.markers(); var name = Plotly.Scatter.name(this.Name); return Plotly.Traces.scatter(x, y, mode, name); } public static (Point2, Point2) GetBounds(params Point2Series[] series) { var xmin = series.SelectMany(item => item._Points).Min(item => item.X); var xmax = series.SelectMany(item => item._Points).Max(item => item.X); if (xmin == xmax) { xmin -= 1; xmax += 1; } var ymin = series.SelectMany(item => item._Points).Min(item => item.Y); var ymax = series.SelectMany(item => item._Points).Max(item => item.Y); if (ymin == ymax) { ymin -= 1; ymax += 1; } return (new Point2(xmin, ymin), new Point2(xmax, ymax)); } public static void DrawToFile(string filePath, params Point2Series[] series) { var traces = series .Select(item => item.GetTrace()) .ToArray(); var plot = Plotly.Plot.traces(traces); var chart = new Plotly.Plot(plot); var html = chart.Render().ToString(); System.IO.File.WriteAllText(filePath, html); } #endregion } public class Point3Series { #region lifecycle public static Point3Series Create(IEnumerable points) { points = points.Where(item => item.X.IsFinite() && item.Y.IsFinite() && item.Z.IsFinite() ); var ps = new Point3Series(); ps._Points.AddRange(points); return ps; } #endregion #region data private readonly List _Points = new List(); private char _PointGlyph = '+'; private bool _Lines = false; #endregion #region API public static (Vector3 Min, Vector3 Max) GetBounds(params Point3Series[] series) { var xmin = series.SelectMany(item => item._Points).Min(item => item.X); var xmax = series.SelectMany(item => item._Points).Max(item => item.X); if (xmin == xmax) { xmin -= 1; xmax += 1; } var ymin = series.SelectMany(item => item._Points).Min(item => item.Y); var ymax = series.SelectMany(item => item._Points).Max(item => item.Y); if (ymin == ymax) { ymin -= 1; ymax += 1; } var zmin = series.SelectMany(item => item._Points).Min(item => item.Z); var zmax = series.SelectMany(item => item._Points).Max(item => item.Z); if (zmin == zmax) { zmin -= 1; zmax += 1; } return (new Vector3(xmin, ymin,zmin), new Vector3(xmax, ymax, zmax)); } private TRACES GetTrace() { var x = Plotly.Scatter3d.x(_Points.Select(item => (float)item.X)); var y = Plotly.Scatter3d.y(_Points.Select(item => (float)item.Y)); var z = Plotly.Scatter3d.z(_Points.Select(item => (float)item.Z)); return Plotly.Traces.scatter3d(x, y, z); } public void DrawToFile(string filePath) { DrawToFile(filePath, this); } public static void DrawToFile(string filePath, params Point3Series[] series) { var traces = series .Select(item => item.GetTrace()) .ToArray(); var plot = Plotly.Plot.traces(traces); var chart = new Plotly.Plot(plot); var html = chart.Render().ToString(); System.IO.File.WriteAllText(filePath, html); } #endregion } } public static class PlottingNUnit { public static void AttachToCurrentTest(this Plotting.Point2Series points, string fileName) { System.Diagnostics.Debug.Assert(fileName.ToUpperInvariant().EndsWith(".HTML")); NUnit.Framework.AttachmentInfo .From(fileName) .WriteObject(f => points.DrawToFile(f)); } public static void AttachToCurrentTest(this IEnumerable series, string fileName) { NUnit.Framework.AttachmentInfo .From(fileName) .WriteObject(f => Plotting.Point2Series.DrawToFile(f, series.ToArray())); } } }