2
0

Plotting.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Numerics;
  5. using System.Text;
  6. using TRACES = Plotly.Box<Plotly.Types.ITracesProperty>;
  7. namespace SharpGLTF
  8. {
  9. public static class Plotting
  10. {
  11. public static Point2Series ToPointSeries(this IEnumerable<Single> points, string name = null) { return Point2Series.Create(points).WithName(name); }
  12. public static Point2Series ToPointSeries(this IEnumerable<Double> points, string name = null) { return Point2Series.Create(points).WithName(name); }
  13. public static Point2Series ToPointSeries(this IEnumerable<Vector2> points, string name = null) { return Point2Series.Create(points).WithName(name); }
  14. public static Point2Series ToLineSeries(this IEnumerable<Vector2> points, string name = null) { return Point2Series.Create(points, LineType.Continuous).WithName(name); }
  15. public enum LineType
  16. {
  17. Square = 0,
  18. Dot = 1,
  19. Cross = 2,
  20. Star = 3,
  21. Circle = 4,
  22. X = 5,
  23. Square2 = 6,
  24. Triangle = 7,
  25. CircleWithCross = 8,
  26. CircleWithDot = 9,
  27. CHAR_X = 88,
  28. CHAR_Y = 89,
  29. CHAR_Z = 90,
  30. CHAR_W = 87,
  31. Continuous = 65536
  32. }
  33. public struct Point2
  34. {
  35. public Point2(Double x, Double y)
  36. {
  37. this.X = x; this.Y = y;
  38. }
  39. public Point2(Vector2 v)
  40. {
  41. this.X = v.X; this.Y = v.Y;
  42. }
  43. public Double X;
  44. public Double Y;
  45. }
  46. public class Point2Series
  47. {
  48. #region lifecycle
  49. public static Point2Series Create(IEnumerable<float> series, LineType lt = LineType.Continuous)
  50. {
  51. var points = series
  52. .Select((y, x) => (x, y))
  53. .Where(item => item.y.IsFinite())
  54. .Select(item => new Vector2(item.x, item.y));
  55. return Create(points, lt);
  56. }
  57. public static Point2Series Create(IEnumerable<double> series, LineType lt = LineType.Continuous)
  58. {
  59. var points = series
  60. .Select((y, x) => (x, (float)y))
  61. .Where(item => item.Item2.IsFinite())
  62. .Select(item => new Vector2(item.x, item.Item2));
  63. return Create(points, lt);
  64. }
  65. public static Point2Series Create(IEnumerable<Vector2> points, LineType lt = LineType.Dot)
  66. {
  67. points = points.Where(item => item.X.IsFinite() && item.Y.IsFinite());
  68. var ps = new Point2Series();
  69. ps._Points.AddRange(points.Select(item => new Point2(item)));
  70. ps.LineType = lt;
  71. return ps;
  72. }
  73. public Point2Series WithName(string name) { Name = name; return this; }
  74. #endregion
  75. #region data
  76. private readonly List<Point2> _Points = new List<Point2>();
  77. #endregion
  78. #region properties
  79. public string Name { get; set; }
  80. public LineType LineType { get; set; }
  81. #endregion
  82. #region API
  83. public Point2Series WithLineType(LineType t) { LineType = t; return this; }
  84. public void DrawToFile(string filePath)
  85. {
  86. DrawToFile(filePath, this);
  87. }
  88. private TRACES GetTrace()
  89. {
  90. var x = Plotly.Scatter.x(_Points.Select(item => (float)item.X));
  91. var y = Plotly.Scatter.y(_Points.Select(item => (float)item.Y));
  92. var mode = LineType == LineType.Continuous ? Plotly.Scatter.Mode.lines() : Plotly.Scatter.Mode.markers();
  93. var name = Plotly.Scatter.name(this.Name);
  94. return Plotly.Traces.scatter(x, y, mode, name);
  95. }
  96. public static (Point2, Point2) GetBounds(params Point2Series[] series)
  97. {
  98. var xmin = series.SelectMany(item => item._Points).Min(item => item.X);
  99. var xmax = series.SelectMany(item => item._Points).Max(item => item.X);
  100. if (xmin == xmax) { xmin -= 1; xmax += 1; }
  101. var ymin = series.SelectMany(item => item._Points).Min(item => item.Y);
  102. var ymax = series.SelectMany(item => item._Points).Max(item => item.Y);
  103. if (ymin == ymax) { ymin -= 1; ymax += 1; }
  104. return (new Point2(xmin, ymin), new Point2(xmax, ymax));
  105. }
  106. public static void DrawToFile(string filePath, params Point2Series[] series)
  107. {
  108. var traces = series
  109. .Select(item => item.GetTrace())
  110. .ToArray();
  111. var plot = Plotly.Plot.traces(traces);
  112. var chart = new Plotly.Plot(plot);
  113. var html = chart.Render().ToString();
  114. System.IO.File.WriteAllText(filePath, html);
  115. }
  116. #endregion
  117. }
  118. public class Point3Series
  119. {
  120. #region lifecycle
  121. public static Point3Series Create(IEnumerable<Vector3> points)
  122. {
  123. points = points.Where(item => item.X.IsFinite() && item.Y.IsFinite() && item.Z.IsFinite() );
  124. var ps = new Point3Series();
  125. ps._Points.AddRange(points);
  126. return ps;
  127. }
  128. #endregion
  129. #region data
  130. private readonly List<Vector3> _Points = new List<Vector3>();
  131. private char _PointGlyph = '+';
  132. private bool _Lines = false;
  133. #endregion
  134. #region API
  135. public static (Vector3 Min, Vector3 Max) GetBounds(params Point3Series[] series)
  136. {
  137. var xmin = series.SelectMany(item => item._Points).Min(item => item.X);
  138. var xmax = series.SelectMany(item => item._Points).Max(item => item.X);
  139. if (xmin == xmax) { xmin -= 1; xmax += 1; }
  140. var ymin = series.SelectMany(item => item._Points).Min(item => item.Y);
  141. var ymax = series.SelectMany(item => item._Points).Max(item => item.Y);
  142. if (ymin == ymax) { ymin -= 1; ymax += 1; }
  143. var zmin = series.SelectMany(item => item._Points).Min(item => item.Z);
  144. var zmax = series.SelectMany(item => item._Points).Max(item => item.Z);
  145. if (zmin == zmax) { zmin -= 1; zmax += 1; }
  146. return (new Vector3(xmin, ymin,zmin), new Vector3(xmax, ymax, zmax));
  147. }
  148. private TRACES GetTrace()
  149. {
  150. var x = Plotly.Scatter3d.x(_Points.Select(item => (float)item.X));
  151. var y = Plotly.Scatter3d.y(_Points.Select(item => (float)item.Y));
  152. var z = Plotly.Scatter3d.z(_Points.Select(item => (float)item.Z));
  153. return Plotly.Traces.scatter3d(x, y, z);
  154. }
  155. public void DrawToFile(string filePath)
  156. {
  157. DrawToFile(filePath, this);
  158. }
  159. public static void DrawToFile(string filePath, params Point3Series[] series)
  160. {
  161. var traces = series
  162. .Select(item => item.GetTrace())
  163. .ToArray();
  164. var plot = Plotly.Plot.traces(traces);
  165. var chart = new Plotly.Plot(plot);
  166. var html = chart.Render().ToString();
  167. System.IO.File.WriteAllText(filePath, html);
  168. }
  169. #endregion
  170. }
  171. }
  172. public static class PlottingNUnit
  173. {
  174. public static void AttachToCurrentTest(this Plotting.Point2Series points, string fileName)
  175. {
  176. System.Diagnostics.Debug.Assert(fileName.ToUpperInvariant().EndsWith(".HTML"));
  177. NUnit.Framework.AttachmentInfo
  178. .From(fileName)
  179. .WriteObject(f => points.DrawToFile(f));
  180. }
  181. public static void AttachToCurrentTest(this IEnumerable<Plotting.Point2Series> series, string fileName)
  182. {
  183. NUnit.Framework.AttachmentInfo
  184. .From(fileName)
  185. .WriteObject(f => Plotting.Point2Series.DrawToFile(f, series.ToArray()));
  186. }
  187. }
  188. }