├── .gitattributes ├── .gitignore ├── Examples ├── .gitignore ├── Examples.csproj ├── IExample.cs ├── Images │ └── ExampleSin.png ├── Plot2D │ ├── ExampleArс.cs │ ├── ExampleHistogram.cs │ ├── ExamplePoint2D.cs │ ├── ExampleSin.cs │ ├── ExampleTimeAxis.cs │ └── ExampleVisibility.cs ├── Program.cs └── Properties │ └── launchSettings.json ├── LICENSE ├── MatplotlibCS.nuspec ├── MatplotlibCS.sln ├── MatplotlibCS ├── Axes.cs ├── Figure.cs ├── Grid.cs ├── MatplotlibCS.cs ├── MatplotlibCS.csproj ├── PlotItems │ ├── Annotation.cs │ ├── Arc.cs │ ├── Enums.cs │ ├── Histogram.cs │ ├── Hline.cs │ ├── IHealthCheck.cs │ ├── Line2D.cs │ ├── PlotItem.cs │ ├── Point2D.cs │ ├── Text.cs │ └── Vline.cs └── Python │ ├── annotations.py │ ├── arc.py │ ├── helpers.py │ ├── histogram.py │ ├── hline.py │ ├── line_2d.py │ ├── matplotlib_cs.py │ ├── subplot.py │ ├── task.py │ └── vline.py ├── README.md ├── appveyor.yml ├── build.cake └── build.ps1 /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml 213 | MatplotlibCS/Python/.idea/ 214 | 215 | 216 | #CAKE 217 | tools 218 | MatplotlibCS/Python/__pycache__/ 219 | *.pyc 220 | out/ 221 | 222 | -------------------------------------------------------------------------------- /Examples/.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /bin/Debug/netcoreapp2.1 6 | /obj 7 | -------------------------------------------------------------------------------- /Examples/Examples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | ..\out 10 | 11 | 12 | 13 | ..\out 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Examples/IExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Examples 6 | { 7 | public interface IExample 8 | { 9 | void Run(string pythonExePath, string dasPlotPyPath); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/Images/ExampleSin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ITGlobal/MatplotlibCS/229b140ef0b514da3bc2a1fc774be59c79c8f305/Examples/Images/ExampleSin.png -------------------------------------------------------------------------------- /Examples/Plot2D/ExampleArс.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MatplotlibCS; 7 | using MatplotlibCS.PlotItems; 8 | 9 | namespace Examples.Plot2D 10 | { 11 | class ExampleArс : IExample 12 | { 13 | /// 14 | /// Chart of sin 15 | /// 16 | public void Run(string pythonExePath, string dasPlotPyPath) 17 | { 18 | #region create test data 19 | 20 | var X = new double[2] {-1, 1}; 21 | var Y = new double[2] {-1, 1}; 22 | 23 | #endregion 24 | 25 | #region create plot 26 | 27 | // init engine with right paths 28 | var matplotlibCs = new MatplotlibCS.MatplotlibCS(pythonExePath, dasPlotPyPath); 29 | 30 | var figure = new Figure(1, 1) 31 | { 32 | FileName = "ExampleArc.png", 33 | OnlySaveImage = true, 34 | Width = 1080, 35 | DPI = 150, 36 | Subplots = 37 | { 38 | new Axes(1, "The X axis", "The Y axis") 39 | { 40 | Title = "Arc, Annotations", 41 | LegendLocation = LegendLocation.LowerLeft, 42 | Grid = new Grid() 43 | { 44 | MinorAlpha = 0.2, 45 | MajorAlpha = 1.0, 46 | }, 47 | PlotItems = 48 | { 49 | new Line2D("45") 50 | { 51 | X = new List() {-1, 1}, 52 | Y = new List() {-1, 1}, 53 | Color = Color.Blue 54 | }, 55 | new Line2D("90") 56 | { 57 | X = new List() {0, 0}, 58 | Y = new List() {-1, 1}, 59 | Color = Color.Magenta 60 | }, 61 | new Line2D("145") 62 | { 63 | X = new List() {-1, 1}, 64 | Y = new List() {1, -1}, 65 | Color = Color.Green 66 | }, 67 | 68 | new Arc("45 arc", 0, 0, 0.5, 0.5, 0, 0, 45) {Color = Color.Blue}, 69 | new Arc("90 arc", 0, 0, 0.6, 0.6, 0, 0, 90) {Color = Color.Magenta}, 70 | new Arc("135 arc", 0, 0, 0.7, 0.7, 0, 0, 135) {Color = Color.Green}, 71 | 72 | new Text("ant1","45", 0.1, 0.04) {Alpha = 0.4}, 73 | new Annotation("ant2","90", 0.2, 0.6, 0.11, 0.26) {Alpha = 0.5}, 74 | new Annotation("ant3","135", -0.2, 0.6, -0.08, 0.32), 75 | 76 | new Hline("0", 0, -1, 1) {LineStyle = LineStyle.Solid, Color = Color.Black} 77 | } 78 | } 79 | } 80 | }; 81 | 82 | var t = matplotlibCs.BuildFigure(figure); 83 | t.Wait(); 84 | 85 | #endregion 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Examples/Plot2D/ExampleHistogram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MatplotlibCS; 7 | using MatplotlibCS.PlotItems; 8 | 9 | namespace Examples.Plot2D 10 | { 11 | class ExampleHistogram : IExample 12 | { 13 | /// 14 | /// Chart of sin 15 | /// 16 | public void Run(string pythonExePath, string dasPlotPyPath) 17 | { 18 | #region create test data 19 | 20 | const int N = 1000; 21 | var X = new double[N]; 22 | var rnd = new Random(); 23 | for (var i = 0; i < N; i++) 24 | X[i] = (rnd.NextDouble()-0.5)*10.0; 25 | 26 | #endregion 27 | 28 | #region create plot 29 | 30 | // init engine with right paths 31 | var matplotlibCs = new MatplotlibCS.MatplotlibCS(pythonExePath, dasPlotPyPath); 32 | 33 | var figure = new Figure(2, 2) 34 | { 35 | FileName = "ExampleHistogram.png", 36 | OnlySaveImage = true, 37 | DPI = 150, 38 | Subplots = 39 | { 40 | new Axes(1, "The X axis", "The Y axis") 41 | { 42 | Title = "Hist 1", 43 | ShowLegend = false, 44 | PlotItems = 45 | { 46 | new Histogram("Sin") 47 | { 48 | Y = X.ToList(), 49 | } 50 | } 51 | }, 52 | new Axes(2, "The X axis", "The Y axis") 53 | { 54 | Title = "Hist 2", 55 | PlotItems = 56 | { 57 | new Histogram("Sin") 58 | { 59 | Y = X.ToList(), 60 | Color = Color.Black, 61 | Alpha = 0.5, 62 | Range = new []{-1, 1.0} 63 | } 64 | } 65 | }, 66 | new Axes(3, "The X axis", "The Y axis") 67 | { 68 | Title = "Hist 3", 69 | PlotItems = 70 | { 71 | new Histogram("Sin") 72 | { 73 | Y = X.ToList(), 74 | Orientation = HistogramOrientation.Horizontal 75 | } 76 | } 77 | }, 78 | new Axes(4, "The X axis", "The Y axis") 79 | { 80 | Title = "Hist 4", 81 | PlotItems = 82 | { 83 | new Histogram("Sin") 84 | { 85 | Y = X.ToList(), 86 | Bins = 10, 87 | Color = Color.Red, 88 | Alpha = 0.5, 89 | Range = new []{-1, 1.0}, 90 | Normed = true 91 | } 92 | } 93 | }, 94 | 95 | } 96 | }; 97 | 98 | var t = matplotlibCs.BuildFigure(figure); 99 | t.Wait(); 100 | 101 | #endregion 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Examples/Plot2D/ExamplePoint2D.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MatplotlibCS; 7 | using MatplotlibCS.PlotItems; 8 | 9 | namespace Examples.Plot2D 10 | { 11 | class ExamplePoint2D : IExample 12 | { 13 | /// 14 | /// Chart of sin 15 | /// 16 | public void Run(string pythonExePath, string dasPlotPyPath) 17 | { 18 | // init engine with right paths 19 | var matplotlibCs = new MatplotlibCS.MatplotlibCS(pythonExePath, dasPlotPyPath); 20 | 21 | var figure = new Figure(1, 1) 22 | { 23 | FileName = "ExamplePoint2D.png", 24 | OnlySaveImage = true, 25 | DPI = 150, 26 | Subplots = 27 | { 28 | new Axes(1, "The X axis", "The Y axis") 29 | { 30 | Title = "2D points", 31 | Grid = new Grid() 32 | { 33 | XLim = new double[] {-5, 15}, 34 | YLim = new double[] {-5, 15} 35 | }, 36 | PlotItems = 37 | { 38 | new Point2D("Point 1", 0, 0) 39 | { 40 | MarkerEdgeColor = Color.Black, 41 | MarkerFaceColor = Color.Cyan, 42 | MarkerSize = 15, 43 | MarkerEdgeWidth = 2, 44 | }, 45 | new Point2D("Point 2", 10, 10) 46 | { 47 | Color = Color.Red, 48 | MarkerSize = 15, 49 | MarkerEdgeWidth = 3, 50 | Marker = Marker.Vline 51 | }, 52 | new Line2D("Line 1") 53 | { 54 | X = new List() {-2,8.0}, 55 | Y = new List() {0,10.0}, 56 | Color = Color.Green, 57 | MarkerEdgeColor = Color.Blue, 58 | MarkerFaceColor = Color.None, 59 | MarkerEdgeWidth = 3, 60 | Marker = Marker.Circle, 61 | MarkerSize = 10 62 | } 63 | } 64 | } 65 | } 66 | }; 67 | 68 | var t = matplotlibCs.BuildFigure(figure); 69 | t.Wait(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Examples/Plot2D/ExampleSin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MatplotlibCS; 7 | using MatplotlibCS.PlotItems; 8 | 9 | namespace Examples.Plot2D 10 | { 11 | class ExampleSin : IExample 12 | { 13 | /// 14 | /// Chart of sin 15 | /// 16 | public void Run(string pythonExePath, string dasPlotPyPath) 17 | { 18 | #region create test data 19 | 20 | const int N = 100; 21 | var X = new double[N]; 22 | var Y1 = new double[N]; 23 | var Y2 = new double[N]; 24 | var x = 0.0; 25 | const double h = 2 * Math.PI / N; 26 | var rnd = new Random(); 27 | for (var i = 0; i < N; i++) 28 | { 29 | var y = Math.Sin(x); 30 | X[i] = x; 31 | Y1[i] = y; 32 | 33 | y = Math.Sin(2 * x); 34 | Y2[i] = y + rnd.NextDouble() / 10.0; 35 | 36 | x += h; 37 | } 38 | 39 | #endregion 40 | 41 | #region create plot 42 | 43 | // init engine with right paths 44 | var matplotlibCs = new MatplotlibCS.MatplotlibCS(pythonExePath, dasPlotPyPath); 45 | 46 | var figure = new Figure(1, 1) 47 | { 48 | FileName = "ExampleSin.png", 49 | OnlySaveImage = true, 50 | DPI = 150, 51 | Subplots = 52 | { 53 | new Axes(1, "The X axis", "The Y axis") 54 | { 55 | Title = "Sin(x), Sin(2x), VLines, HLines, Annotations", 56 | LegendBorder = false, 57 | Grid = new Grid() 58 | { 59 | MinorAlpha = 0.2, 60 | MajorAlpha = 1.0, 61 | XMajorTicks = new[] {0.0, 7.6, 0.5, 1.75}, 62 | YMajorTicks = new[] {-1, 2.5, 0.25, 0.125}, 63 | XMinorTicks = new[] {0.0, 7.25, 0.25, 1.125}, 64 | YMinorTicks = new[] {-1, 2.5, 0.125, 1.025} 65 | }, 66 | PlotItems = 67 | { 68 | new Line2D("Sin") 69 | { 70 | X = X.Cast().ToList(), 71 | Y = Y1.ToList(), 72 | LineStyle = LineStyle.Dashed 73 | }, 74 | 75 | new Line2D("Sin 2x") 76 | { 77 | X = X.Cast().ToList(), 78 | Y = Y2.ToList(), 79 | LineStyle = LineStyle.Solid, 80 | LineWidth = 0.5f, 81 | Color = "r", 82 | Markevery = 5, 83 | MarkerSize = 10, 84 | Marker = Marker.Circle, 85 | ShowLegend = false 86 | }, 87 | 88 | new Text("ant1", "Text annotation", 4.5, 0.76) 89 | { 90 | FontSize = 17 91 | }, 92 | 93 | new Annotation("ant2","Arrow text annotation", 0.5, -0.7, 3, 0) 94 | { 95 | Color = "#44ff88", 96 | ArrowStyle = ArrowStyle.Both, 97 | LineWidth = 3, 98 | }, 99 | 100 | new Vline("vert line", new object[] {3.0}, -1, 1), 101 | new Hline("hrzt line", new[] {0.1, 0.25, 0.375}, 0, 5) {LineStyle = LineStyle.Dashed, Color = Color.Magenta} 102 | } 103 | } 104 | 105 | } 106 | }; 107 | 108 | var t = matplotlibCs.BuildFigure(figure); 109 | t.Wait(); 110 | 111 | #endregion 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Examples/Plot2D/ExampleTimeAxis.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MatplotlibCS; 7 | using MatplotlibCS.PlotItems; 8 | 9 | namespace Examples.Plot2D 10 | { 11 | class ExampleTimeAxis : IExample 12 | { 13 | /// 14 | /// Chart of sin 15 | /// 16 | public void Run(string pythonExePath, string dasPlotPyPath) 17 | { 18 | #region create test data 19 | 20 | const int N = 10; 21 | var X = new double[N]; 22 | var Y1 = new double[N]; 23 | var Y2 = new double[N]; 24 | var x = 0.0; 25 | const double h = 2 * Math.PI / N; 26 | var rnd = new Random(); 27 | for (var i = 0; i < N; i++) 28 | { 29 | var y = Math.Sin(x); 30 | X[i] = x; 31 | Y1[i] = y; 32 | 33 | y = Math.Sin(2 * x); 34 | Y2[i] = y + rnd.NextDouble() / 10.0; 35 | 36 | x += h; 37 | } 38 | 39 | #endregion 40 | 41 | #region create plot 42 | 43 | // init engine with right paths 44 | var matplotlibCs = new MatplotlibCS.MatplotlibCS(pythonExePath, dasPlotPyPath); 45 | 46 | var timeTicks = new List(); 47 | timeTicks.Add(DateTime.Now); 48 | 49 | var xMajorTicks = new List(); 50 | xMajorTicks.Add(0); 51 | for (var i = 1; i < N; i++) 52 | { 53 | xMajorTicks.Add(i); 54 | timeTicks.Add(timeTicks[i - 1] + new TimeSpan(0, rnd.Next(1, 10), 0, 0)); 55 | } 56 | 57 | var figure = new Figure(1, 1) 58 | { 59 | FileName = "ExampleTimeAxis.png", 60 | OnlySaveImage = true, 61 | DPI = 150, 62 | Subplots = 63 | { 64 | new Axes(1, "Time", "Value") 65 | { 66 | Title = "Time Axis Example", 67 | LegendBorder = false, 68 | Grid = new Grid() 69 | { 70 | MinorAlpha = 0.2, 71 | MajorAlpha = 1.0, 72 | XTimeTicks = timeTicks.Cast().ToList(), 73 | TimeTickFormat = TimeTickFormat.HHMMSS, 74 | RegularTimeAxis = true, 75 | YMajorTicks = new[] {-1, 2.5, 0.25, 0.125}, 76 | XMinorTicks = new[] {0.0, 7.25, 0.25, 1.125}, 77 | YMinorTicks = new[] {-1, 2.5, 0.125, 1.025}, 78 | XTickFontSize = 8, 79 | XTickRotation = 30 80 | }, 81 | PlotItems = 82 | { 83 | new Line2D("Data") 84 | { 85 | //X = timeTicks.Cast().ToList(), 86 | X=xMajorTicks.Cast().ToList(), 87 | Y = Y1.ToList(), 88 | LineStyle = LineStyle.Dashed, 89 | Marker = Marker.Circle, 90 | MarkerSize = 5 91 | }, 92 | //new Vline("vl", new object[] {DateTime.Now.AddHours(10)},-1,1), 93 | new Vline("vl", new object[] {5},-1,1) 94 | } 95 | } 96 | 97 | } 98 | }; 99 | 100 | var t = matplotlibCs.BuildFigure(figure); 101 | t.Wait(); 102 | 103 | #endregion 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Examples/Plot2D/ExampleVisibility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MatplotlibCS; 7 | using MatplotlibCS.PlotItems; 8 | 9 | namespace Examples.Plot2D 10 | { 11 | class ExampleVisibility : IExample 12 | { 13 | /// 14 | /// Chart of sin 15 | /// 16 | public void Run(string pythonExePath, string dasPlotPyPath) 17 | { 18 | // init engine with right paths 19 | var matplotlibCs = new MatplotlibCS.MatplotlibCS(pythonExePath, dasPlotPyPath); 20 | 21 | var figure = new Figure(1, 1) 22 | { 23 | FileName = "ExampleVisibility1.png", 24 | OnlySaveImage = true, 25 | DPI = 150, 26 | Subplots = 27 | { 28 | new Axes(1, "The X axis", "The Y axis") 29 | { 30 | Title = "Visibility", 31 | PlotItems = 32 | { 33 | new Line2D("Line 1") 34 | { 35 | X = new List() {-1, 1}, 36 | Y = new List() {-1, 1}, 37 | LineStyle = LineStyle.Dashed, 38 | Color = Color.Blue 39 | }, 40 | new Line2D("Line 2") 41 | { 42 | X = new List() {-1, 1}, 43 | Y = new List() {-2, 2}, 44 | LineStyle = LineStyle.Solid, 45 | Color = Color.Red, 46 | IsVisible = false // set this line invisible for first image 47 | }, 48 | new Point2D("plus", 0.5, -0.5) 49 | { 50 | Marker = Marker.Circle, 51 | MarkerSize = 10, 52 | Color = Color.Green, 53 | LineWidth = 2 54 | }, 55 | } 56 | } 57 | } 58 | }; 59 | 60 | var t = matplotlibCs.BuildFigure(figure); 61 | t.Wait(); 62 | 63 | figure.Subplots[0]["Line 2"].IsVisible = true; // now turn one line2 vivibility and build the second image with new name 64 | figure.FileName = "ExampleVisibility2.png"; 65 | t = matplotlibCs.BuildFigure(figure); 66 | t.Wait(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Examples/Program.cs: -------------------------------------------------------------------------------- 1 | using Examples.Plot2D; 2 | using System; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Examples 7 | { 8 | /// 9 | /// Sample programm which creates several charts and save them in png 10 | /// 11 | class Program 12 | { 13 | /// 14 | /// Path to python.exe, must be initialized in Main 15 | /// 16 | private static string _pythonExePath; 17 | 18 | /// 19 | /// Path to dasPlot.py, script which finally builds plots 20 | /// 21 | private static string _matplotlibPyPath; 22 | 23 | static void Main(string[] args) 24 | { 25 | if (args.Length != 3) 26 | { 27 | Console.WriteLine("You must specify path to python.exe and to matplotlib_cs.py in command line arguments"); 28 | Console.ReadKey(); 29 | return; 30 | } 31 | 32 | _pythonExePath = args[1]; 33 | _matplotlibPyPath = args[2]; 34 | 35 | var assembly = Assembly.GetEntryAssembly(); 36 | foreach (Type type in assembly.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IExample)))) 37 | { 38 | if (args.Length > 0 && !args.Contains(type.Name)) 39 | continue; 40 | 41 | Console.WriteLine($"{DateTime.UtcNow} Starting {type.Name}"); 42 | 43 | var example = (IExample)Activator.CreateInstance(type); 44 | 45 | try 46 | { 47 | example.Run(_pythonExePath, _matplotlibPyPath); 48 | } 49 | catch (Exception ex) 50 | { 51 | Console.WriteLine(ex); 52 | } 53 | 54 | Console.WriteLine($"{DateTime.UtcNow} Completed {type.Name}"); 55 | } 56 | 57 | Console.ReadLine(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Examples/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Examples": { 4 | "commandName": "Project", 5 | "commandLineArgs": "ExamplePoint2D c:\\Users\\pavel\\.conda\\envs\\Python\\python.exe c:\\Sources\\opensource\\MatplotlibCS\\MatplotlibCS\\Python\\matplotlib_cs.py" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 IT Global LLC (http://www.itglobal.ru) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MatplotlibCS.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MatplotlibCS 5 | $version$ 6 | MatplotlibCS 7 | Pavel Koryakin 8 | Pavel Koryakin 9 | http://www.opensource.org/licenses/MIT 10 | https://github.com/ITGlobal/MatplotlibCS 11 | false 12 | C# wrapper which allows to easily build charts using Python Matplotlib 13 | Stable version with ability to plot 2D lines and histograms. 14 | Copyright 2016 15 | c# matplotlib plot chart charting 16 | 17 | 18 | 19 | 20 | C# wrapper which allows to easily build charts using Python Matplotlib 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /MatplotlibCS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2000 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F5FBDD60-0FF8-44D8-BE5C-97927C2361A6}" 7 | ProjectSection(SolutionItems) = preProject 8 | README.md = README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatplotlibCS", "MatplotlibCS\MatplotlibCS.csproj", "{83C39133-FFC0-4099-8660-E62624D690E7}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{2E76703C-B85E-4601-96BB-6DE62F5F6754}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {83C39133-FFC0-4099-8660-E62624D690E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {83C39133-FFC0-4099-8660-E62624D690E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {83C39133-FFC0-4099-8660-E62624D690E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {83C39133-FFC0-4099-8660-E62624D690E7}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {2E76703C-B85E-4601-96BB-6DE62F5F6754}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {2E76703C-B85E-4601-96BB-6DE62F5F6754}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {2E76703C-B85E-4601-96BB-6DE62F5F6754}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {2E76703C-B85E-4601-96BB-6DE62F5F6754}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {360741BB-E7C0-4F90-9A19-85D4CF5EA50C} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /MatplotlibCS/Axes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MatplotlibCS.PlotItems; 5 | using Newtonsoft.Json; 6 | 7 | namespace MatplotlibCS 8 | { 9 | /// 10 | [JsonObject(Title = "axes")] 11 | public class Axes : IHealthCheck 12 | { 13 | #region .ctor 14 | 15 | /// 16 | /// Конструктор 17 | /// 18 | /// 19 | /// Заголовок оси x 20 | /// Заголовок оси y 21 | public Axes(int index = 1, string xtitle = "", string ytitle = "") 22 | { 23 | this.Index = index; 24 | this.XTitle = xtitle; 25 | this.YTitle = ytitle; 26 | Grid = new Grid(); 27 | PlotItems = new List(); 28 | } 29 | #endregion 30 | 31 | #region Properties 32 | 33 | /// 34 | /// Заголовок осей 35 | /// 36 | [JsonProperty(PropertyName = "title", DefaultValueHandling = DefaultValueHandling.Include)] 37 | public string Title { get; set; } 38 | 39 | /// 40 | /// Подпись к оси X 41 | /// 42 | [JsonProperty(PropertyName = "xtitle")] 43 | public string XTitle { get; set; } 44 | 45 | /// 46 | /// Подпись к оси Y 47 | /// 48 | [JsonProperty(PropertyName = "ytitle")] 49 | public string YTitle { get; set; } 50 | 51 | /// 52 | /// Индекс subplot-а на figure 53 | /// 54 | [JsonProperty(PropertyName = "index")] 55 | public int Index { get; set; } = 1; 56 | 57 | /// 58 | /// Plot grid settings 59 | /// 60 | [JsonProperty(PropertyName = "grid")] 61 | public Grid Grid { get; set; } 62 | 63 | [JsonProperty(PropertyName = "show_legend")] 64 | public bool ShowLegend { get; set; } = true; 65 | 66 | [JsonProperty(PropertyName = "legend_location")] 67 | public LegendLocation LegendLocation { get; set; } = LegendLocation.Best; 68 | 69 | [JsonProperty(PropertyName = "frameon")] 70 | public bool LegendBorder { get; set; } = true; 71 | 72 | /// 73 | /// Lines and other plot items. Never add items to this list directly, only set the list itself. For adding/removing 74 | /// items use AddItem/RemoveItem methods 75 | /// 76 | [JsonProperty(PropertyName = "__items__")] 77 | public List PlotItems { get; set; } 78 | 79 | #endregion 80 | 81 | #region Indexers 82 | 83 | /// 84 | /// Returnts first plot item by its name 85 | /// 86 | /// Name of an item 87 | /// 88 | public PlotItem this[string name] 89 | { 90 | get { return PlotItems.FirstOrDefault(_ => _.Name == name); } 91 | } 92 | 93 | #endregion 94 | 95 | public void HealthCheck() 96 | { 97 | Grid.HealthCheck(); 98 | 99 | foreach (var plotItem in PlotItems) 100 | { 101 | plotItem.HealthCheck(); 102 | } 103 | } 104 | } 105 | 106 | /// 107 | /// Defines where a legend will be located on a chart 108 | /// 109 | public enum LegendLocation 110 | { 111 | Best, 112 | UpperRight, 113 | UpperLeft, 114 | LowerLeft, 115 | LowerRight, 116 | Right, 117 | CenterLeft, 118 | CenterRight, 119 | LowerCenter, 120 | UpperCenter, 121 | Center 122 | } 123 | } 124 | 125 | -------------------------------------------------------------------------------- /MatplotlibCS/Figure.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using MatplotlibCS.PlotItems; 4 | using Newtonsoft.Json; 5 | 6 | namespace MatplotlibCS 7 | { 8 | /// 9 | /// Class desribing a figure to be build 10 | /// 11 | [JsonObject(Title = "figure")] 12 | public class Figure : IHealthCheck 13 | { 14 | #region ctor 15 | 16 | /// 17 | /// .ctor 18 | /// 19 | /// Number of rows in subplots grid 20 | /// Number of columns in subplots grid 21 | public Figure(int rows = 1, int columns = 1) 22 | { 23 | Rows = rows; 24 | Columns = columns; 25 | Subplots = new List(); 26 | } 27 | 28 | #endregion 29 | 30 | #region Properties 31 | 32 | /// 33 | /// Number of rows in subplots grid 34 | /// 35 | [JsonProperty(PropertyName = "rows")] 36 | public int Rows { get; set; } = 1; 37 | 38 | /// 39 | /// Number of columns in subplots grid 40 | /// 41 | [JsonProperty(PropertyName = "columns")] 42 | public int Columns { get; set; } = 1; 43 | 44 | /// 45 | /// Figuree subplots 46 | /// 47 | [JsonProperty(PropertyName = "__subplots__")] 48 | public List Subplots { get; set; } 49 | 50 | /// 51 | /// Name or full path of the file where to save result 52 | /// 53 | [JsonProperty(PropertyName = "filename")] 54 | public string FileName { get; set; } 55 | 56 | /// 57 | /// If true, matplotlib window won't be shown, only image will be saved to disk 58 | /// 59 | [JsonProperty(PropertyName = "onlySaveImage")] 60 | public bool OnlySaveImage { get; set; } 61 | 62 | /// 63 | /// Dots per inch 64 | /// 65 | [JsonProperty(PropertyName = "dpi")] 66 | public int DPI { get; set; } = 300; 67 | 68 | /// 69 | /// Image width 70 | /// 71 | [JsonProperty(PropertyName = "w")] 72 | public int Width { get; set; } = 1920; 73 | 74 | /// 75 | /// Image height 76 | /// 77 | [JsonProperty(PropertyName = "h")] 78 | public int Height { get; set; } = 1080; 79 | 80 | #endregion 81 | 82 | public void HealthCheck() 83 | { 84 | foreach (var subplot in Subplots) 85 | { 86 | subplot.HealthCheck(); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /MatplotlibCS/Grid.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MatplotlibCS.PlotItems; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Converters; 9 | 10 | namespace MatplotlibCS 11 | { 12 | /// 13 | /// Class describes a grid settings on a plot 14 | /// 15 | public class Grid : IHealthCheck 16 | { 17 | #region Fields 18 | 19 | /// 20 | /// Internal time ticks string representation 21 | /// 22 | private List _x; 23 | 24 | #endregion 25 | 26 | /// 27 | /// Which grids to show on a plot 28 | /// 29 | [JsonProperty(PropertyName = "which")] 30 | [JsonConverter(typeof(StringEnumConverter))] 31 | public GridWhich Which { get; set; } = GridWhich.Both; 32 | 33 | [JsonProperty(PropertyName = "x_lim")] 34 | public double[] XLim { get; set; } 35 | 36 | [JsonProperty(PropertyName = "y_lim")] 37 | public double[] YLim { get; set; } 38 | 39 | [JsonProperty(PropertyName = "minor_alpha")] 40 | public double MinorAlpha { get; set; } = 0.3; 41 | 42 | [JsonProperty(PropertyName = "major_alpha")] 43 | public double MajorAlpha { get; set; } = 0.7; 44 | 45 | [JsonProperty(PropertyName = "on")] 46 | public string On { get; set; } = "on"; 47 | 48 | [JsonProperty(PropertyName = "x_major_ticks")] 49 | public double[] XMajorTicks { get; set; } 50 | 51 | [JsonProperty(PropertyName = "y_major_ticks")] 52 | public double[] YMajorTicks { get; set; } 53 | 54 | [JsonProperty(PropertyName = "x_minor_ticks")] 55 | public double[] XMinorTicks { get; set; } 56 | 57 | [JsonProperty(PropertyName = "y_minor_ticks")] 58 | public double[] YMinorTicks { get; set; } 59 | 60 | [JsonProperty(PropertyName = "x_time_ticks")] 61 | public List XTimeTicks 62 | { 63 | get 64 | { 65 | return _x; 66 | } 67 | set 68 | { 69 | if (value == null || value.Count == 0) 70 | { 71 | _x.Clear(); 72 | return; 73 | } 74 | 75 | var firstItem = value[0]; 76 | 77 | if (firstItem is DateTime) 78 | { 79 | if(_x == null) 80 | _x = new List(); 81 | 82 | _x.Clear(); 83 | foreach (var item in value) 84 | _x.Add(((DateTime)item).ToString("yyyy-MM-ddTHH:mm:ss,ffffff")); 85 | } 86 | else 87 | throw new ArgumentException("XTimeTicks must be set with List only"); 88 | } 89 | } 90 | 91 | [JsonProperty(PropertyName = "time_ticks_format")] 92 | public TimeTickFormat TimeTickFormat { get; set; } = TimeTickFormat.DateAndTime; 93 | 94 | [JsonProperty(PropertyName = "regular_time_axis")] 95 | public bool RegularTimeAxis { get; set; } = false; 96 | 97 | [JsonProperty(PropertyName = "x_tick_fontsize")] 98 | public double XTickFontSize { get; set; } 99 | 100 | [JsonProperty(PropertyName = "x_tick_rotation")] 101 | public double XTickRotation { get; set; } 102 | 103 | public void HealthCheck() 104 | { 105 | //if(XTimeTicks!=null && XTimeTicks.Count>0 && XMajorTicks!=null && XMajorTicks.Length>0 && XMajorTicks.Length!= XTimeTicks.Count) 106 | // throw new HealthCheckException("Grid.XTimeTicks and Grid.XMajorTicks must be of the same leangth"); 107 | } 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /MatplotlibCS/MatplotlibCS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Newtonsoft.Json; 12 | using Newtonsoft.Json.Converters; 13 | using NLog; 14 | 15 | namespace MatplotlibCS 16 | { 17 | /// 18 | /// Обёртка над питоновским скриптом построения графиков 19 | /// 20 | // ReSharper disable once InconsistentNaming 21 | public class MatplotlibCS 22 | { 23 | #region Fields 24 | 25 | private ILogger _log; 26 | 27 | /// 28 | /// Пусть к интерпрететору питона 29 | /// 30 | private string _pythonExePath; 31 | 32 | /// 33 | /// Путь к скрипту dasPlot.py 34 | /// 35 | private string _dasPlotPyPath; 36 | 37 | /// 38 | /// Путь директории, в которой хранятся временные json-файлы, через которые передаются параметры задачи 39 | /// 40 | private string _jsonTempPath; 41 | 42 | /// 43 | /// Python web service URL 44 | /// 45 | private string _serviceUrlCheckAliveMethod = "http://127.0.0.1:57123/"; 46 | 47 | /// 48 | /// Python web service URL 49 | /// 50 | private string _serviceUrlPlotMethod = "http://127.0.0.1:57123/plot"; 51 | 52 | /// 53 | /// Kill web service url 54 | /// 55 | private string _serviceUrlKillMethod = "http://127.0.0.1:57123/kill"; 56 | 57 | #endregion 58 | 59 | #region .ctor 60 | 61 | /// 62 | /// Обёртка над python скриптом, строящим matplotlib графики 63 | /// 64 | /// Путь python.exe 65 | /// Путь dasPlot.py 66 | /// Опциональный путь директории, в которой хранятся временные json файлы, через которые передаются данные 67 | public MatplotlibCS(string pythonExePath, string dasPlotPyPath, string jsonTempPath = "c:\\MatplotlibCS") 68 | { 69 | _pythonExePath = pythonExePath; 70 | _dasPlotPyPath = dasPlotPyPath; 71 | _jsonTempPath = jsonTempPath; 72 | _log = LogManager.GetLogger(typeof(MatplotlibCS).Name); 73 | 74 | if (!Directory.Exists(_jsonTempPath)) 75 | Directory.CreateDirectory(_jsonTempPath); 76 | } 77 | 78 | #endregion 79 | 80 | #region Public methods 81 | 82 | /// 83 | /// Выполняет задачу построения графиков 84 | /// 85 | /// Описание задачи 86 | public async Task BuildFigure(Figure task) 87 | { 88 | task.HealthCheck(); 89 | 90 | try 91 | { 92 | LaunchPythonWebService(); 93 | 94 | if (!Path.IsPathRooted(task.FileName)) 95 | task.FileName = Path.Combine(_jsonTempPath, task.FileName); 96 | 97 | JsonConvert.DefaultSettings = (() => 98 | { 99 | var settings = new JsonSerializerSettings(); 100 | settings.Converters.Add(new StringEnumConverter { CamelCaseText = true }); 101 | return settings; 102 | }); 103 | 104 | var serializer = new JsonSerializer() { StringEscapeHandling = StringEscapeHandling.EscapeHtml }; 105 | var sb = new StringBuilder(); 106 | using (var writer = new StringWriter(sb)) 107 | { 108 | serializer.Serialize(writer, task); 109 | } 110 | 111 | var json = sb.ToString(); 112 | using (var client = new HttpClient()) 113 | { 114 | var content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json"); 115 | var response = await client.PostAsync(_serviceUrlPlotMethod, content); 116 | var responseString = await response.Content.ReadAsStringAsync(); 117 | } 118 | } 119 | catch (Exception ex) 120 | { 121 | _log.Fatal($"Error while building figure: {ex.Message}\n{ex.StackTrace}"); 122 | } 123 | } 124 | 125 | #endregion 126 | 127 | #region Private methods 128 | 129 | /// 130 | /// Check if python web service is alive and if no, launches it 131 | /// 132 | private void LaunchPythonWebService() 133 | { 134 | if (!CheckIfWebServiceIsUpAndRunning()) 135 | { 136 | var psi = new ProcessStartInfo(_pythonExePath, _dasPlotPyPath) 137 | { 138 | RedirectStandardOutput = true, 139 | RedirectStandardError = true, 140 | RedirectStandardInput = true, 141 | UseShellExecute = false, 142 | CreateNoWindow = true, 143 | WindowStyle = ProcessWindowStyle.Hidden 144 | }; 145 | 146 | _log.Info($"Starting python process {_pythonExePath}, {_dasPlotPyPath}"); 147 | var pythonProcess = Process.Start(psi); 148 | 149 | // when starting python process, it's better to wait for some time to ensure, that 150 | // web service started 151 | Thread.Sleep(2000); 152 | } 153 | } 154 | 155 | /// 156 | /// Check if python web service is alive 157 | /// 158 | /// true if service is up and running 159 | private bool CheckIfWebServiceIsUpAndRunning() 160 | { 161 | try 162 | { 163 | _log.Info("Check if python web-service is already running"); 164 | //Creating the HttpWebRequest 165 | var request = WebRequest.Create(_serviceUrlCheckAliveMethod) as HttpWebRequest; 166 | //Setting the Request method HEAD, you can also use GET too. 167 | request.Method = "GET"; 168 | //Getting the Web Response. 169 | var response = request.GetResponse() as HttpWebResponse; 170 | //Returns TRUE if the Status code == 200 171 | response.Close(); 172 | 173 | _log.Info($"Service response status is {response.StatusCode}"); 174 | 175 | return (response.StatusCode == HttpStatusCode.OK); 176 | } 177 | catch (Exception ex) 178 | { 179 | _log.Info("Python web-service wasn't found"); 180 | //Any exception will returns false. 181 | return false; 182 | } 183 | } 184 | 185 | /// 186 | /// Возвращает новый путь, по которому можно сохранить json задачи 187 | /// 188 | /// 189 | private string GetNewJsonPath() 190 | { 191 | if (!Directory.Exists(_jsonTempPath)) 192 | { 193 | Directory.CreateDirectory(_jsonTempPath); 194 | } 195 | 196 | return Path.Combine(_jsonTempPath, $"task-{DateTime.Now:HHmmssfff}.json"); 197 | } 198 | 199 | #endregion 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /MatplotlibCS/MatplotlibCS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | ..\out 9 | 10 | 11 | 12 | ..\out 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | PreserveNewest 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | PreserveNewest 43 | 44 | 45 | PreserveNewest 46 | 47 | 48 | PreserveNewest 49 | 50 | 51 | PreserveNewest 52 | 53 | 54 | PreserveNewest 55 | 56 | 57 | PreserveNewest 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Annotation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Converters; 9 | 10 | namespace MatplotlibCS.PlotItems 11 | { 12 | /// 13 | /// Text with an arrow on a plot 14 | /// 15 | public class Annotation : PlotItem 16 | { 17 | public Annotation(string name, string text, double textX, double textY, double arrowX, double arrowY) : base(name) 18 | { 19 | this.Text = text; 20 | TextLeftCornerX = textX; 21 | TextLeftCornerY = textY; 22 | ArrowX = arrowX; 23 | ArrowY = arrowY; 24 | } 25 | 26 | [JsonProperty(PropertyName = "text")] 27 | public string Text { get; set; } 28 | 29 | [JsonProperty(PropertyName = "text_x")] 30 | public double TextLeftCornerX { get; set; } 31 | 32 | [JsonProperty(PropertyName = "text_y")] 33 | public double TextLeftCornerY { get; set; } 34 | 35 | [JsonProperty(PropertyName = "arrow_x")] 36 | public double ArrowX { get; set; } 37 | 38 | [JsonProperty(PropertyName = "arrow_y")] 39 | public double ArrowY { get; set; } 40 | 41 | [JsonProperty(PropertyName = "fontSize")] 42 | public double FontSize { get; set; } = 12; 43 | 44 | [JsonProperty(PropertyName = "color")] 45 | public Color Color { get; set; } = Color.Black; 46 | 47 | /// 48 | /// Color transparency 49 | /// 50 | [JsonProperty(PropertyName = "alpha")] 51 | public double Alpha { get; set; } = 1; 52 | 53 | [JsonProperty(PropertyName = "arrow_style")] 54 | [JsonConverter(typeof(StringEnumConverter))] 55 | public ArrowStyle ArrowStyle { get; set; } = ArrowStyle.End; 56 | 57 | [JsonProperty(PropertyName = "lineWidth")] 58 | public float LineWidth { get; set; } = 1; 59 | } 60 | 61 | public enum ArrowStyle 62 | { 63 | [EnumMember(Value = "<-")] 64 | Begin, 65 | 66 | [EnumMember(Value = "->")] 67 | End, 68 | 69 | [EnumMember(Value = "<->")] 70 | Both 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Arc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | 8 | namespace MatplotlibCS.PlotItems 9 | { 10 | /// 11 | /// An arc, segment of an ellipse 12 | /// 13 | public class Arc : Line2D 14 | { 15 | /// 16 | /// .ctor 17 | /// 18 | /// Center x coord 19 | /// Center y coord 20 | /// Width along x axis 21 | /// Height along y axis 22 | /// rotation in degrees (anti-clockwise) 23 | /// starting angle of the arc in degrees 24 | /// ending angle of the arc in degrees 25 | public Arc(string name, double x, double y, double width, double height, double angle, double theta1, double theta2):base(name) 26 | { 27 | X = x; 28 | Y = y; 29 | Height = height; 30 | Width = width; 31 | Angle = angle; 32 | Theta1 = theta1; 33 | Theta2 = theta2; 34 | ShowLegend = false; 35 | } 36 | 37 | /// 38 | /// Center x coord 39 | /// 40 | [JsonProperty(PropertyName = "x")] 41 | public double X { get; set; } 42 | 43 | /// 44 | /// Center y coord 45 | /// 46 | [JsonProperty(PropertyName = "y")] 47 | public double Y { get; set; } 48 | 49 | /// 50 | /// Width along x axis 51 | /// 52 | [JsonProperty(PropertyName = "width")] 53 | public double Width { get; set; } 54 | 55 | /// 56 | /// Height along y axis 57 | /// 58 | [JsonProperty(PropertyName = "height")] 59 | public double Height { get; set; } 60 | 61 | /// 62 | /// rotation in degrees (anti-clockwise) 63 | /// 64 | [JsonProperty(PropertyName = "angle")] 65 | public double Angle { get; set; } 66 | 67 | /// 68 | /// starting angle of the arc in degrees 69 | /// 70 | [JsonProperty(PropertyName = "theta1")] 71 | public double Theta1 { get; set; } 72 | 73 | /// 74 | /// ending angle of the arc in degrees 75 | /// 76 | [JsonProperty(PropertyName = "theta2")] 77 | public double Theta2 { get; set; } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | 9 | namespace MatplotlibCS.PlotItems 10 | { 11 | /// 12 | /// [ '-' | '--' | '-.' | ':' | 'steps' | ...] 13 | /// 14 | public enum LineStyle 15 | { 16 | [EnumMember(Value = "-")] 17 | Solid, 18 | 19 | [EnumMember(Value = "--")] 20 | Dashed, 21 | 22 | [EnumMember(Value = "steps")] 23 | Steps, 24 | 25 | [EnumMember(Value = ":")] 26 | Dotted, 27 | 28 | [EnumMember(Value = "-.")] 29 | DashDotted 30 | } 31 | 32 | /// 33 | /// Marker type 34 | /// 35 | public enum Marker 36 | { 37 | [EnumMember(Value = "")] 38 | None, 39 | 40 | [EnumMember(Value = ".")] 41 | Point, 42 | 43 | [EnumMember(Value = ",")] 44 | Pixel, 45 | 46 | [EnumMember(Value = "o")] 47 | Circle, 48 | 49 | [EnumMember(Value = "v")] 50 | TriangleDown, 51 | 52 | [EnumMember(Value = "^")] 53 | TriangleUp, 54 | 55 | [EnumMember(Value = "<")] 56 | TriangleLeft, 57 | 58 | [EnumMember(Value = ">")] 59 | TriangleRight, 60 | 61 | [EnumMember(Value = "1")] 62 | TriDown, 63 | 64 | [EnumMember(Value = "2")] 65 | TriUp, 66 | 67 | [EnumMember(Value = "3")] 68 | TriLeft, 69 | 70 | [EnumMember(Value = "4")] 71 | TriRight, 72 | 73 | [EnumMember(Value = "8")] 74 | Octagon, 75 | 76 | [EnumMember(Value = "s")] 77 | Square, 78 | 79 | [EnumMember(Value = "p")] 80 | Pentagon, 81 | 82 | [EnumMember(Value = "*")] 83 | Star, 84 | 85 | [EnumMember(Value = "h")] 86 | Hexagon1, 87 | 88 | [EnumMember(Value = "+")] 89 | Plus, 90 | 91 | [EnumMember(Value = "x")] 92 | X, 93 | 94 | [EnumMember(Value = "D")] 95 | Diamond, 96 | 97 | [EnumMember(Value = "|")] 98 | Vline, 99 | 100 | [EnumMember(Value = "_")] 101 | Hline, 102 | 103 | [EnumMember(Value = "TICKLEFT")] 104 | Tickleft, 105 | 106 | [EnumMember(Value = "TICKRIGHT")] 107 | Tickright, 108 | 109 | [EnumMember(Value = "TICKUP")] 110 | Tickup, 111 | 112 | [EnumMember(Value = "TICKDOWN")] 113 | Tickdown, 114 | 115 | [EnumMember(Value = "CARETLEFT")] 116 | Caretleft, 117 | 118 | [EnumMember(Value = "CARETRIGHT")] 119 | Caretright, 120 | 121 | [EnumMember(Value = "CARETUP")] 122 | Caretup, 123 | 124 | [EnumMember(Value = "CARETDOWN")] 125 | Caretdown 126 | } 127 | 128 | /// 129 | /// Colors 130 | /// 131 | public class Color 132 | { 133 | [JsonProperty(PropertyName = "value")] 134 | public string Value { get; private set; } 135 | 136 | public Color(string hexColor) 137 | { 138 | Value = hexColor; 139 | } 140 | 141 | public static implicit operator Color(string hexColor) 142 | { 143 | return new Color(hexColor); 144 | } 145 | 146 | public static Color Blue = new Color("b"); 147 | 148 | public static Color Black = new Color("k"); 149 | 150 | public static Color Red = new Color("r"); 151 | 152 | public static Color Yellow = new Color("y"); 153 | 154 | public static Color Cyan = new Color("c"); 155 | 156 | public static Color Green = new Color("g"); 157 | 158 | public static Color White = new Color("w"); 159 | 160 | public static Color Magenta = new Color("m"); 161 | 162 | public static Color None = new Color("None"); 163 | } 164 | 165 | public class TimeTickFormat 166 | { 167 | [JsonProperty(PropertyName = "value")] 168 | public string Value { get; private set; } 169 | 170 | public TimeTickFormat(string formatString) 171 | { 172 | Value = formatString; 173 | } 174 | 175 | public static implicit operator TimeTickFormat(string formatString) 176 | { 177 | return new TimeTickFormat(formatString); 178 | } 179 | 180 | public static TimeTickFormat HHMMSS = new TimeTickFormat("%H:%M:%S"); 181 | 182 | public static TimeTickFormat HHMM = new TimeTickFormat("%H:%M"); 183 | 184 | public static TimeTickFormat DateOnly = new TimeTickFormat("%Y-%m-%d"); 185 | 186 | public static TimeTickFormat DateAndTime = new TimeTickFormat("%Y-%m-%d %H:%M:%S"); 187 | 188 | } 189 | 190 | /// 191 | /// Which grid lines to show 192 | /// 193 | public enum GridWhich 194 | { 195 | /// 196 | /// Show both grids 197 | /// 198 | [EnumMember(Value = "both")] 199 | Both, 200 | 201 | /// 202 | /// Show only minor grid 203 | /// 204 | [EnumMember(Value = "minor")] 205 | Minor, 206 | 207 | /// 208 | /// Show only major grid 209 | /// 210 | [EnumMember(Value = "major")] 211 | Major 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Histogram.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.Serialization; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Converters; 5 | 6 | namespace MatplotlibCS.PlotItems 7 | { 8 | /// 9 | /// Class describes a histogram 10 | /// 11 | public class Histogram : PlotItem 12 | { 13 | #region Properties 14 | 15 | /// 16 | /// Data values 17 | /// 18 | [JsonProperty(PropertyName = "y")] 19 | public List Y { get; set; } 20 | 21 | [JsonProperty(PropertyName = "bins")] 22 | public int Bins { get; set; } = 50; 23 | 24 | [JsonProperty(PropertyName = "normed")] 25 | public bool Normed { get; set; } = false; 26 | 27 | [JsonProperty(PropertyName = "orientation")] 28 | [JsonConverter(typeof(StringEnumConverter))] 29 | public HistogramOrientation Orientation { get; set; } = HistogramOrientation.Vertical; 30 | 31 | [JsonProperty(PropertyName = "color")] 32 | public Color Color { get; set; } = Color.Blue; 33 | 34 | [JsonProperty(PropertyName = "range")] 35 | public double[] Range { get; set; } 36 | 37 | [JsonProperty(PropertyName = "cumulative")] 38 | public bool Cumulative { get; set; } 39 | 40 | [JsonProperty(PropertyName = "histtype")] 41 | [JsonConverter(typeof(StringEnumConverter))] 42 | public Histtype Histtype { get; set; } 43 | 44 | [JsonProperty(PropertyName = "alpha")] 45 | public double Alpha { get; set; } = 1; 46 | 47 | #endregion 48 | 49 | #region .ctor 50 | 51 | public Histogram(string name) : base(name) { } 52 | 53 | #endregion 54 | } 55 | 56 | public enum HistogramOrientation 57 | { 58 | [EnumMember(Value = "vertical")] 59 | Vertical, 60 | 61 | [EnumMember(Value = "horizontal")] 62 | Horizontal 63 | } 64 | 65 | public enum Histtype 66 | { 67 | [EnumMember(Value = "bar")] 68 | Bar, 69 | 70 | [EnumMember(Value = "barstacked")] 71 | Barstacked, 72 | 73 | [EnumMember(Value = "step")] 74 | Step, 75 | 76 | [EnumMember(Value = "stepfilled")] 77 | Stepfilled 78 | } 79 | } 80 | 81 | /* 82 | weights=None 83 | bottom=None 84 | histtype='bar' 85 | align='mid' 86 | rwidth=None 87 | log=False 88 | label=None 89 | stacked=False 90 | hold=None 91 | data=None 92 | */ 93 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Hline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | 8 | namespace MatplotlibCS.PlotItems 9 | { 10 | public class Hline : Line2D 11 | { 12 | /// 13 | /// Y coord of a line 14 | /// 15 | [JsonProperty(PropertyName = "y")] 16 | public double[] Y { get; set; } 17 | 18 | [JsonProperty(PropertyName = "xmin")] 19 | public double XMin { get; set; } 20 | 21 | [JsonProperty(PropertyName = "xmax")] 22 | public double XMax { get; set; } 23 | 24 | public Hline(string name, double[] y, double xmin, double xmax) : base(name) 25 | { 26 | Y = y; 27 | XMin = xmin; 28 | XMax = xmax; 29 | ShowLegend = false; 30 | } 31 | 32 | public Hline(string name, double y, double xmin, double xmax) : this(name, new[] { y }, xmin, xmax) 33 | { 34 | ShowLegend = false; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/IHealthCheck.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace MatplotlibCS.PlotItems 9 | { 10 | public interface IHealthCheck 11 | { 12 | void HealthCheck(); 13 | } 14 | 15 | [Serializable] 16 | public class HealthCheckException : Exception 17 | { 18 | public HealthCheckException() 19 | { 20 | } 21 | 22 | public HealthCheckException(string message) : base(message) 23 | { 24 | } 25 | 26 | public HealthCheckException(string message, Exception inner) : base(message, inner) 27 | { 28 | } 29 | 30 | protected HealthCheckException( 31 | SerializationInfo info, 32 | StreamingContext context) : base(info, context) 33 | { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Line2D.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Converters; 7 | 8 | namespace MatplotlibCS.PlotItems 9 | { 10 | /// 11 | /// Описание линии графика 12 | /// 13 | [JsonObject(Title = "line")] 14 | public class Line2D : PlotItem 15 | { 16 | #region Fields 17 | 18 | /// 19 | /// Internal X values string representation 20 | /// 21 | private List _x = new List(); 22 | 23 | #endregion 24 | 25 | #region ctor 26 | 27 | /// 28 | /// Конструктор 29 | /// 30 | /// Название линии 31 | /// 32 | public Line2D(string name) : base(name) 33 | { 34 | X = new List(); 35 | Y = new List(); 36 | } 37 | 38 | #endregion 39 | 40 | #region Properties 41 | 42 | /// 43 | /// Line color 44 | /// 45 | [JsonProperty(PropertyName = "color")] 46 | public Color Color { get; set; } = Color.Black; 47 | 48 | /// 49 | /// Color of markers edge 50 | /// 51 | [JsonProperty(PropertyName = "markeredgecolor")] 52 | public Color MarkerEdgeColor { get; set; } = Color.Black; 53 | 54 | /// 55 | /// Marker's background 56 | /// 57 | [JsonProperty(PropertyName = "markerfacecolor")] 58 | public Color MarkerFaceColor { get; set; } 59 | 60 | /// 61 | /// Color transparency 62 | /// 63 | [JsonProperty(PropertyName = "alpha")] 64 | public double Alpha { get; set; } = 1; 65 | 66 | /// 67 | /// Маркер точки 68 | /// 69 | [JsonProperty(PropertyName = "marker")] 70 | [JsonConverter(typeof(StringEnumConverter))] 71 | public Marker Marker { get; set; } = Marker.None; 72 | 73 | /// 74 | /// Размер маркера 75 | /// 76 | [JsonProperty(PropertyName = "markerSize")] 77 | public float MarkerSize { get; set; } = 1; 78 | 79 | /// 80 | /// Width of line 81 | /// 82 | [JsonProperty(PropertyName = "lineWidth")] 83 | public float LineWidth { get; set; } = 1; 84 | 85 | /// 86 | /// Width of markers edge 87 | /// 88 | [JsonProperty(PropertyName = "markeredgewidth")] 89 | public float MarkerEdgeWidth { get; set; } = 1; 90 | 91 | /// 92 | /// Маркер точки 93 | /// 94 | [JsonProperty(PropertyName = "lineStyle")] 95 | [JsonConverter(typeof(StringEnumConverter))] 96 | public LineStyle LineStyle { get; set; } = LineStyle.Solid; 97 | 98 | /// 99 | /// e.g., if Markevery=5, every 5-th marker will be plotted. 100 | /// 101 | [JsonProperty(PropertyName = "markevery")] 102 | public int Markevery { get; set; } = 1; 103 | 104 | /// 105 | /// Данные для графика, аргумент 106 | /// 107 | [JsonProperty(PropertyName = "x")] 108 | public List X 109 | { 110 | get 111 | { 112 | return _x.ToList(); 113 | } 114 | set 115 | { 116 | if (value == null || value.Count == 0) 117 | { 118 | _x.Clear(); 119 | return; 120 | } 121 | 122 | var firstItem = value[0]; 123 | 124 | if (firstItem is DateTime) 125 | { 126 | _x.Clear(); 127 | foreach (var item in value) 128 | _x.Add(((DateTime) item).ToString("yyyy-MM-ddTHH:mm:ss,ffffff")); 129 | } 130 | else if (firstItem is double || firstItem is int || firstItem is long || firstItem is float) 131 | { 132 | _x.Clear(); 133 | foreach (var item in value) 134 | _x.Add(item); 135 | } 136 | } 137 | } 138 | 139 | /// 140 | /// Данные для графика, значение 141 | /// 142 | [JsonProperty(PropertyName = "y")] 143 | public List Y { get; set; } 144 | 145 | #endregion 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/PlotItem.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace MatplotlibCS.PlotItems 4 | { 5 | /// 6 | /// Базовый класс для элементов, которые отрисовываются на графике 7 | /// 8 | public abstract class PlotItem : IHealthCheck 9 | { 10 | #region Properties 11 | 12 | /// 13 | /// Name of the type of the plot item (line, histogram, text etc 14 | /// 15 | [JsonProperty(PropertyName = "type")] 16 | public string Type => GetType().Name; 17 | 18 | /// 19 | /// Unique (within axes) name of a plot item 20 | /// 21 | [JsonProperty(PropertyName = "name")] 22 | public string Name { get; set; } 23 | 24 | /// 25 | /// Optional fields which can be used to filter plot items. For example, one can precreate all plot items and then save several images with 26 | /// different visible items, be setting IsVisible property 27 | /// 28 | public string Tags { get; set; } = ""; 29 | 30 | /// 31 | /// If false, item will not be drawn 32 | /// 33 | [JsonProperty(PropertyName = "is_visible")] 34 | public bool IsVisible { get; set; } = true; 35 | 36 | /// 37 | /// Whether to show this item in legend 38 | /// 39 | [JsonProperty(PropertyName = "show_legend")] 40 | public bool ShowLegend { get; set; } = true; 41 | 42 | #endregion 43 | 44 | #region .ctor 45 | 46 | /// 47 | /// .ctor 48 | /// 49 | /// Unique name of a plot item 50 | protected PlotItem(string name) 51 | { 52 | Name = name; 53 | } 54 | 55 | #endregion 56 | 57 | public virtual void HealthCheck() 58 | { 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Point2D.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Converters; 4 | 5 | namespace MatplotlibCS.PlotItems 6 | { 7 | /// 8 | /// 2D point on a chart 9 | /// 10 | [JsonObject(Title = "point")] 11 | public class Point2D : Line2D 12 | { 13 | #region ctor 14 | 15 | /// 16 | /// Конструктор 17 | /// 18 | /// Название линии 19 | /// 20 | public Point2D(string name, double x, double y) : base(name) 21 | { 22 | X = new List {x}; 23 | Y = new List {y}; 24 | MarkerSize = 5; 25 | Marker=Marker.Circle; 26 | LineStyle=LineStyle.Solid; 27 | LineWidth = 1; 28 | } 29 | 30 | #endregion 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Text.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Converters; 8 | 9 | namespace MatplotlibCS.PlotItems 10 | { 11 | /// 12 | /// Text label on a plot 13 | /// 14 | public class Text : PlotItem 15 | { 16 | public Text(string name, string text, double x, double y) : base(name) 17 | { 18 | this.X = x; 19 | this.Y = y; 20 | this.Value = text; 21 | } 22 | 23 | [JsonProperty(PropertyName = "text")] 24 | public string Value { get; set; } 25 | 26 | [JsonProperty(PropertyName = "x")] 27 | public double X { get; set; } 28 | 29 | [JsonProperty(PropertyName = "y")] 30 | public double Y { get; set; } 31 | 32 | [JsonProperty(PropertyName = "fontSize")] 33 | public double FontSize { get; set; } = 12; 34 | 35 | [JsonProperty(PropertyName = "color")] 36 | public Color Color { get; set; } = Color.Black; 37 | 38 | /// 39 | /// Color transparency 40 | /// 41 | [JsonProperty(PropertyName = "alpha")] 42 | public double Alpha { get; set; } = 1; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /MatplotlibCS/PlotItems/Vline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | 8 | namespace MatplotlibCS.PlotItems 9 | { 10 | /// 11 | /// Vertical line 12 | /// 13 | public class Vline : Line2D 14 | { 15 | [JsonProperty(PropertyName = "ymin")] 16 | public double YMin { get; set; } 17 | 18 | [JsonProperty(PropertyName = "ymax")] 19 | public double YMax { get; set; } 20 | 21 | public Vline(string name, object[] x, double ymin, double ymax) : base(name) 22 | { 23 | X = x.ToList(); 24 | YMin = ymin; 25 | YMax = ymax; 26 | ShowLegend = false; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/annotations.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | from datetime import datetime 3 | from helpers import if_string_convert_to_datetime 4 | 5 | 6 | class Text: 7 | def __init__(self, jsonDict): 8 | self.fontSize = 12 9 | self.color = 'k' 10 | self.alpha = 1 11 | self.__dict__ = jsonDict 12 | self.x = if_string_convert_to_datetime(self.x) 13 | 14 | def plot(self, axes): 15 | plot.text(self.x, 16 | self.y, 17 | self.text, 18 | size=self.fontSize, 19 | alpha=self.alpha, 20 | color=self.color["value"]) 21 | 22 | 23 | class Annotation: 24 | def __init__(self, jsonDict): 25 | self.fontSize = 12 26 | self.color = 'k' 27 | self.alpha = 1 28 | self.__dict__ = jsonDict 29 | self.x = if_string_convert_to_datetime(self.arrow_x) 30 | self.x = if_string_convert_to_datetime(self.text_x) 31 | 32 | def plot(self, axes): 33 | plot.annotate(self.text, 34 | xy=(self.arrow_x, self.arrow_y), 35 | xycoords='data', 36 | xytext=(self.text_x, self.text_y), 37 | textcoords='data', 38 | color=self.color["value"], 39 | size=self.fontSize, 40 | alpha=self.alpha, 41 | arrowprops=dict( 42 | linewidth=self.lineWidth, 43 | color=self.color["value"], 44 | alpha=self.alpha, 45 | arrowstyle=self.arrow_style, 46 | connectionstyle='arc3,rad=0', 47 | ) 48 | ) 49 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/arc.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | import matplotlib.patches as patches 3 | 4 | from line_2d import Line2D 5 | 6 | 7 | class Arc(Line2D): 8 | def __init__(self, jsonDict): 9 | self.__dict__ = jsonDict 10 | self.check_if_x_is_time() 11 | 12 | def plot(self, axes): 13 | label = self.name if self.show_legend else "" 14 | a = patches.Arc((self.x, self.y), 15 | self.width, 16 | self.height, 17 | angle=self.angle, 18 | theta1=self.theta1, 19 | theta2=self.theta2, 20 | color=self.color["value"], 21 | linewidth=self.lineWidth, 22 | alpha=self.alpha, 23 | label=label) 24 | axes.add_patch(a) 25 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/helpers.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | def if_string_convert_to_datetime(x): 5 | ''' 6 | Checks if x is a string, then trying to convert it to datetime and return the result. 7 | If x is not a string, just returns the x itself 8 | :param self: 9 | :return: 10 | ''' 11 | 12 | if isinstance(x, list): 13 | X = [] 14 | for _x in x: 15 | X.append(if_string_convert_to_datetime(_x)) 16 | return X 17 | 18 | if isinstance(x, str): 19 | # return datetime.strptime(x[0:19] + x[27:30] + x[31:33], '%Y-%m-%dT%H:%M:%S%z') 20 | # .net yyyy-MM-ddTHH:mm:ss,fff,zzz 21 | return datetime.strptime(x, '%Y-%m-%dT%H:%M:%S,%f') 22 | return x 23 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/histogram.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | from matplotlib.ticker import FormatStrFormatter 3 | 4 | 5 | class Histogram: 6 | def __init__(self, jsonDict): 7 | self.__dict__ = jsonDict 8 | 9 | def plot(self, axes): 10 | label = self.name if self.show_legend else "" 11 | counts, bins, patches = plot.hist( 12 | self.y, 13 | self.bins, 14 | color=self.color["value"], 15 | normed=self.normed, 16 | orientation=self.orientation, 17 | range=self.range, 18 | cumulative=self.cumulative, 19 | histtype=self.histtype, 20 | alpha=self.alpha, 21 | label=label) 22 | axes.set_xticks(bins) 23 | # axes.xaxis.set_major_formatter(FormatStrFormatter('%f')) 24 | 25 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/hline.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | from line_2d import Line2D 3 | from helpers import if_string_convert_to_datetime 4 | 5 | class Hline(Line2D): 6 | def __init__(self, jsonDict): 7 | self.__dict__ = jsonDict 8 | self.xmin = if_string_convert_to_datetime(self.xmin) 9 | self.xmax = if_string_convert_to_datetime(self.xmax) 10 | 11 | def plot(self, axes): 12 | label = self.name if self.show_legend else "" 13 | plot.hlines(self.y, 14 | self.xmin, 15 | self.xmax, 16 | color=self.color["value"], 17 | lw=self.lineWidth, 18 | linestyle=self.lineStyle, 19 | alpha=self.alpha, 20 | label=label) 21 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/line_2d.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | from datetime import datetime 3 | import matplotlib.dates as mdates 4 | from helpers import if_string_convert_to_datetime 5 | 6 | class Line2D: 7 | def __init__(self, jsonDict): 8 | self.__dict__ = jsonDict 9 | self.check_if_x_is_time() 10 | 11 | def check_if_x_is_time(self): 12 | if isinstance(self.x, str) or (isinstance(self.x, list) and isinstance(self.x[0], str)): 13 | self.x = if_string_convert_to_datetime(self.x) 14 | 15 | def plot(self, axes): 16 | label = self.name if self.show_legend else "" 17 | markeredgecolor = self.color["value"] if self.markeredgecolor is None else self.markeredgecolor["value"] 18 | markerfacecolor = self.color["value"] if self.markerfacecolor is None else self.markerfacecolor["value"] 19 | plot.plot(self.x, 20 | self.y, 21 | color=self.color["value"], 22 | marker=self.marker, 23 | markeredgecolor=markeredgecolor, 24 | markeredgewidth=self.markeredgewidth, 25 | markerfacecolor=markerfacecolor, 26 | lw=self.lineWidth, 27 | ms=self.markerSize, 28 | ls=self.lineStyle, 29 | markevery=self.markevery, 30 | alpha=self.alpha, 31 | label=label) 32 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/matplotlib_cs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import sys, getopt 4 | import matplotlib.pyplot as plot 5 | import matplotlib.patches as patches 6 | import numpy as np 7 | from matplotlib import rc 8 | from flask import Flask, url_for, request, json, Response, abort, jsonify 9 | from task import Task 10 | import datetime 11 | import matplotlib.dates as mdates 12 | from helpers import if_string_convert_to_datetime 13 | import matplotlib.ticker as ticker 14 | 15 | # Script builds a matplotlib figure based on information, passed to it through json file. 16 | # Path to the JSON must be passed in first command line argument. 17 | 18 | # a trick to enable text labels in cyrillic 19 | rc('font', **{'sans-serif': 'Arial','family': 'sans-serif'}) 20 | 21 | app = Flask(__name__) 22 | 23 | def main(args): 24 | host = "127.0.0.1" 25 | app.run(host=host, port=57123) 26 | 27 | @app.route('/', methods=['POST', 'GET']) 28 | def api_check_alive(): 29 | return '', 200 30 | 31 | @app.route('/plot', methods=['POST', 'GET']) 32 | def api_plot(): 33 | json_raw = request.json 34 | task = json.loads(json_raw) 35 | t = Task(task) 36 | 37 | fig = plot.figure(figsize=[t.w/(1.0*t.dpi), t.h/(1.0*t.dpi)]) 38 | 39 | # precreate, make basic settings and save subplots 40 | subplots = {} 41 | subplot_index = 1 42 | for i in range(0, t.rows): 43 | for j in range(0, t.columns): 44 | axes = fig.add_subplot(t.rows, t.columns, subplot_index) 45 | subplots[subplot_index] = axes 46 | # set_grid(axes) 47 | subplot_index += 1 48 | 49 | # draw items on each subplot 50 | for subplot in t.subplots: 51 | axes = subplots[subplot.index] 52 | plot.sca(axes) 53 | set_titles(subplot) 54 | for item in subplot.items: 55 | if item.is_visible == True: 56 | item.plot(axes) 57 | if subplot.show_legend: 58 | plot.legend(loc=subplot.legend_location, frameon=subplot.frameon) 59 | 60 | set_grid(fig, axes, subplot.grid) 61 | 62 | plot.tight_layout() 63 | 64 | save_figure_to_file(task) 65 | 66 | if not task["onlySaveImage"]: 67 | plot.show() 68 | 69 | plot.close("all") 70 | return '', 200 71 | 72 | @app.route('/kill', methods=['POST', 'GET']) 73 | def api_kill(): 74 | raise RuntimeError('Stop web service') 75 | 76 | def save_figure_to_file(task): 77 | """ 78 | Saves figure content to the file if it's path is provided 79 | 80 | :type task: dict 81 | :param task: Deserialized json with task description 82 | 83 | :return: 84 | """ 85 | if "filename" in task and task["filename"] is not None: 86 | print ("Saving figure to file {0}".format(task["filename"])) 87 | plot.savefig(task["filename"], dpi=task["dpi"]) 88 | 89 | def set_grid(fig, axes, grid): 90 | """ 91 | Setup axes grid 92 | :param axes: 93 | :return: 94 | """ 95 | axes.grid(which=grid.which) 96 | axes.grid(which='minor', alpha=grid.minor_alpha) 97 | axes.grid(which='major', alpha=grid.major_alpha) 98 | axes.grid(grid.on) 99 | 100 | if grid.x_lim is not None: 101 | axes.set_xlim(grid.x_lim[0], grid.x_lim[1]) 102 | 103 | if grid.y_lim is not None: 104 | axes.set_ylim(grid.y_lim[0], grid.y_lim[1]) 105 | 106 | # if time ticks defined 107 | if grid.x_time_ticks is not None and len(grid.x_time_ticks) > 0: 108 | timeTicks = [] 109 | for stringTick in grid.x_time_ticks: 110 | timeTick = if_string_convert_to_datetime(stringTick) 111 | timeTicks.append(timeTick) 112 | 113 | # distance between nodes will be equal 114 | if grid.regular_time_axis: 115 | n = len(grid.x_time_ticks) 116 | 117 | def format_date(x, pos=None): 118 | thisind = np.clip(int(x + 0.5), 0, n - 1) 119 | return timeTicks[thisind].strftime(grid.time_ticks_format['value']) 120 | 121 | axes.xaxis.set_major_formatter(ticker.FuncFormatter(format_date)) 122 | 123 | # else distance between time nodes will be as is 124 | else: 125 | formatter = mdates.DateFormatter(grid.time_ticks_format['value']) 126 | axes.xaxis.set_major_formatter(formatter) 127 | axes.set_xticks(timeTicks) 128 | 129 | # if no time ticks defined 130 | elif grid.x_major_ticks is not None: 131 | axes.set_xticks(build_ticks_list(grid.x_major_ticks)) 132 | if grid.x_minor_ticks is not None: 133 | axes.set_xticks(build_ticks_list(grid.x_minor_ticks), minor=True) 134 | 135 | if grid.y_major_ticks is not None: 136 | axes.set_yticks(build_ticks_list(grid.y_major_ticks)) 137 | 138 | if grid.y_minor_ticks is not None: 139 | axes.set_yticks(build_ticks_list(grid.y_minor_ticks), minor=True) 140 | 141 | # set font and rotation 142 | labels = axes.get_xticklabels() 143 | 144 | if grid.x_tick_fontsize is not None and grid.x_tick_fontsize!=0: 145 | plot.setp(labels, fontsize=grid.x_tick_fontsize) 146 | 147 | if grid.x_tick_rotation is not None and grid.x_tick_rotation!=0: 148 | plot.setp(labels, rotation=grid.x_tick_rotation) 149 | 150 | 151 | def build_ticks_list(lims): 152 | ''' 153 | First two values in lims are the minimum and the maximum values, while third value is a step. 154 | Values starting from 4 are additional values which will be added to the grid 155 | :param lims: 156 | :return: 157 | ''' 158 | ticks = np.arange(lims[0], lims[1] + lims[2], lims[2]) 159 | if len(lims) > 3: 160 | for i in range(3, len(lims)): 161 | ticks = np.append(ticks, [lims[i]]) 162 | return ticks 163 | 164 | def set_titles(task): 165 | """ 166 | Setup subplot X and Y axis titles 167 | 168 | :param task: 169 | :return: 170 | """ 171 | plot.title(u"{0}".format(task.title)) 172 | plot.xlabel(task.xtitle) 173 | plot.ylabel(task.ytitle) 174 | 175 | # entry point 176 | if __name__ == "__main__": 177 | main(sys.argv[1:]) 178 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/subplot.py: -------------------------------------------------------------------------------- 1 | from annotations import Text, Annotation 2 | from arc import Arc 3 | from histogram import Histogram 4 | from hline import Hline 5 | from line_2d import Line2D 6 | from vline import Vline 7 | 8 | 9 | class Subplot: 10 | def __init__(self, jsonDict): 11 | self.index = 1 12 | self.title = "" 13 | self.xtitle = "" 14 | self.ytitle = "" 15 | 16 | self.__dict__ = jsonDict 17 | 18 | self.items = [] 19 | if "__items__" in jsonDict: 20 | for item in jsonDict["__items__"]: 21 | if item["type"] == "Line2D": 22 | self.items.append(Line2D(item)) 23 | elif item["type"] == "Point2D": 24 | self.items.append(Line2D(item)) 25 | elif item["type"] == "Histogram": 26 | self.items.append(Histogram(item)) 27 | elif item["type"] == "Text": 28 | self.items.append(Text(item)) 29 | elif item["type"] == "Annotation": 30 | self.items.append(Annotation(item)) 31 | elif item["type"] == "Hline": 32 | self.items.append(Hline(item)) 33 | elif item["type"] == "Vline": 34 | self.items.append(Vline(item)) 35 | elif item["type"] == "Arc": 36 | self.items.append(Arc(item)) 37 | 38 | if "grid" in jsonDict: 39 | self.grid = Grid(jsonDict["grid"]) 40 | else: 41 | self.grid = Grid() 42 | 43 | 44 | class Grid: 45 | def __init__(self, jsonDict): 46 | self.which = 'both' 47 | self.minor_alfa = 0.4 48 | self.major_alfa = 0.5 49 | self.on = 'on' 50 | self.__dict__ = jsonDict -------------------------------------------------------------------------------- /MatplotlibCS/Python/task.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | 3 | from line_2d import Line2D 4 | from subplot import Subplot 5 | 6 | 7 | class Task: 8 | def __init__(self, jsonDict): 9 | # set default values for most important properties 10 | self.dpi = 300 11 | self.w = 1920 12 | self.h = 1080 13 | self.rows = 1 14 | self.columns = 1 15 | 16 | # initialize object from json, received from c# process 17 | self.__dict__ = jsonDict 18 | 19 | self.subplots = [] 20 | if "__subplots__" in jsonDict: 21 | for subplotJson in jsonDict["__subplots__"]: 22 | self.subplots.append(Subplot(subplotJson)) 23 | -------------------------------------------------------------------------------- /MatplotlibCS/Python/vline.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | 3 | from line_2d import Line2D 4 | 5 | 6 | class Vline(Line2D): 7 | def __init__(self, jsonDict): 8 | self.__dict__ = jsonDict 9 | self.check_if_x_is_time() 10 | 11 | def plot(self, axes): 12 | label = self.name if self.show_legend else "" 13 | plot.vlines(self.x, 14 | self.ymin, 15 | self.ymax, 16 | color=self.color["value"], 17 | lw=self.lineWidth, 18 | linestyle=self.lineStyle, 19 | alpha=self.alpha, 20 | label=label) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MatplotlibCS 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/9kwdn26qx7oh6mho/branch/master?svg=true)](https://ci.appveyor.com/project/itgloballlc/matplotlibcs/branch/master) 4 | [![NuGet](https://img.shields.io/nuget/v/MatplotlibCS.svg?maxAge=2592000)](https://www.nuget.org/packages/MatplotlibCS/) 5 | 6 | A tiny library for utilizing Matplotlib Python charting library from C#. The general process is this: 7 | 8 | ## How to install 9 | ``` 10 | PS D:\MatplotlibCS> pip install flask 11 | PS D:\MatplotlibCS> python -mpip install matplotlib 12 | ``` 13 | ``` 14 | PM> Install-Package MatplotlibCS 15 | ``` 16 | ## Basic usage 17 | 18 | 1. You create an instance of the class Figure and initialize it's properties with relative data. This instance describes everything you want to see on the figure. 19 | 2. You initialize MatplotlibCS class instance. You need to specify a path to python.exe and matplotlib_cs.py in constructor. 20 | 3. Call MatplotlibCS instance BuildFigure method to plot the figure. 21 | 4. Run 22 | ``` 23 | PS D:\MatplotlibCS> dotnet .\Examples\bin\Debug\netcoreapp2.1\Examples.dll python .\MatplotlibCS\Python\matplotlib_cs.py 24 | ``` 25 | ## Examples 26 | ![ExampleSin](http://i.imgur.com/SXUEFCT.png) 27 | 28 | ```csharp 29 | // Init engine with right paths 30 | var matplotlibCs = new MatplotlibCS.MatplotlibCS(pythonExePath, dasPlotPyPath); 31 | 32 | var figure = new Figure(1, 1) 33 | { 34 | FileName = "ExampleSin.png", 35 | OnlySaveImage = true, 36 | DPI = 150, 37 | Subplots = 38 | { 39 | new Axes(1, "The X axis", "The Y axis") 40 | { 41 | Title = "Sin(x), Sin(2x), VLines, HLines, Annotations", 42 | Grid = new Grid() 43 | { 44 | MinorAlpha = 0.2, 45 | MajorAlpha = 1.0, 46 | XMajorTicks = new[] {0.0, 7.6, 0.5}, 47 | YMajorTicks = new[] {-1, 2.5, 0.25}, 48 | XMinorTicks = new[] {0.0, 7.25, 0.25}, 49 | YMinorTicks = new[] {-1, 2.5, 0.125} 50 | }, 51 | PlotItems = 52 | { 53 | new Line2D("Sin") 54 | { 55 | X = X.ToList(), 56 | Y = Y1.ToList(), 57 | LineStyle = LineStyle.Dashed 58 | }, 59 | 60 | new Line2D("Sin 2x") 61 | { 62 | X = X.ToList(), 63 | Y = Y2.ToList(), 64 | LineStyle = LineStyle.Solid, 65 | LineWidth = 0.5f, 66 | Color = Color.Green, 67 | Markevery = 5, 68 | MarkerSize = 10, 69 | Marker = Marker.Circle 70 | }, 71 | 72 | new Text("Text annotation", 4.5, 0.76) 73 | { 74 | FontSize = 17 75 | }, 76 | 77 | new Annotation("Arrow text annotation", 0.5, -0.7, 3, 0) 78 | { 79 | Color = Color.Blue 80 | }, 81 | 82 | new Vline("vert line", 3.0, -1, 1), 83 | new Hline("hrzt line", new[] {0.1, 0.25, 0.375}, 0, 5) {LineStyle = LineStyle.Dashed, Color = Color.Magenta} 84 | } 85 | } 86 | 87 | } 88 | }; 89 | 90 | var t = matplotlibCs.BuildFigure(figure); 91 | t.Wait(); 92 | ``` -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | configuration: Release 3 | branches: 4 | only: 5 | - master 6 | build: 7 | verbosity: minimal 8 | cache: 9 | - 'C:\projects\MatplotlibCS\tools' 10 | - 'C:\projects\MatplotlibCS\packages' 11 | build_script: 12 | - ps: '& ./build.ps1' 13 | test: off 14 | artifacts: 15 | - path: artifacts\**\*.* 16 | deploy: 17 | - provider: NuGet 18 | api_key: 19 | secure: r5vF3QENzP28+dU6gvmD7Sqbdt5Ti43yhuSye3O7TJxKT+6vuponJ7tVwkGkotH7 20 | skip_symbols: true -------------------------------------------------------------------------------- /build.cake: -------------------------------------------------------------------------------- 1 | #addin "nuget:?package=NuGet.Core&version=2.8.6" 2 | #addin "Cake.ExtendedNuGet" 3 | 4 | var TARGET = Argument("target", "default"); 5 | var CONFIGURATION = Argument("configuration", "Release"); 6 | 7 | var OUT_DIR = Directory("out"); 8 | var ARTIFACTS_DIR = Directory("artifacts"); 9 | var SOLUTION = File("MatplotlibCS.sln"); 10 | var VERSION = "1.0.0"; 11 | 12 | TaskSetup(context => 13 | { 14 | if(AppVeyor.IsRunningOnAppVeyor) 15 | { 16 | AppVeyor.AddInformationalMessage("Starting task {0}", context.Task.Name); 17 | } 18 | }); 19 | 20 | TaskTeardown(_ => { }); 21 | 22 | 23 | Task("init") 24 | .Does(() => 25 | { 26 | foreach(var dir in new []{ OUT_DIR, ARTIFACTS_DIR }) 27 | { 28 | if(!DirectoryExists(dir)) 29 | { 30 | Information("Creating directory {0}", dir); 31 | CreateDirectory(dir); 32 | } 33 | } 34 | }); 35 | 36 | Task("clean") 37 | .IsDependentOn("init") 38 | .Does(() => 39 | { 40 | foreach(var dir in 41 | new DirectoryPath[] { OUT_DIR, ARTIFACTS_DIR } 42 | .Concat(GetDirectories("./**/bin")) 43 | .Concat(GetDirectories("./**/obj"))) 44 | { 45 | Information("Cleaning directory {0}", dir); 46 | CleanDirectory(dir); 47 | } 48 | }); 49 | 50 | Task("restore") 51 | .IsDependentOn("init") 52 | .Does(() => 53 | { 54 | DotNetCoreRestore(); 55 | }); 56 | 57 | Task("version") 58 | .IsDependentOn("init") 59 | .Does(() => 60 | { 61 | if(AppVeyor.IsRunningOnAppVeyor) 62 | { 63 | VERSION = AppVeyor.Environment.Build.Version; 64 | } 65 | else 66 | { 67 | Warning("Will not use build number since build is not running in AppVeyor"); 68 | } 69 | Information("Version is {0}", VERSION); 70 | }); 71 | 72 | Task("compile") 73 | .IsDependentOn("clean") 74 | .IsDependentOn("restore") 75 | .IsDependentOn("version") 76 | .Does(() => 77 | { 78 | var settings = new MSBuildSettings 79 | { 80 | Configuration = CONFIGURATION 81 | }; 82 | settings.WithProperty("OutputPath", new string[]{ MakeAbsolute(OUT_DIR).FullPath }); 83 | // MSBuild(SOLUTION, settings); 84 | DotNetCoreBuild("."); 85 | }); 86 | 87 | Task("pack") 88 | .IsDependentOn("version") 89 | .IsDependentOn("compile") 90 | .Does(() => 91 | { 92 | var settings = new NuGetPackSettings 93 | { 94 | Version = VERSION, 95 | OutputDirectory = ARTIFACTS_DIR 96 | }; 97 | NuGetPack("./MatplotlibCS.nuspec", settings); 98 | }); 99 | 100 | Task("default").IsDependentOn("pack"); 101 | 102 | RunTarget(TARGET); -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # This is the Cake bootstrapper script for PowerShell. 3 | # This file was downloaded from https://github.com/cake-build/resources 4 | # Feel free to change this file to fit your needs. 5 | ########################################################################## 6 | 7 | <# 8 | 9 | .SYNOPSIS 10 | This is a Powershell script to bootstrap a Cake build. 11 | 12 | .DESCRIPTION 13 | This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) 14 | and execute your Cake build script with the parameters you provide. 15 | 16 | .PARAMETER Script 17 | The build script to execute. 18 | .PARAMETER Target 19 | The build script target to run. 20 | .PARAMETER Configuration 21 | The build configuration to use. 22 | .PARAMETER Verbosity 23 | Specifies the amount of information to be displayed. 24 | .PARAMETER Experimental 25 | Tells Cake to use the latest Roslyn release. 26 | .PARAMETER WhatIf 27 | Performs a dry run of the build script. 28 | No tasks will be executed. 29 | .PARAMETER Mono 30 | Tells Cake to use the Mono scripting engine. 31 | .PARAMETER SkipToolPackageRestore 32 | Skips restoring of packages. 33 | .PARAMETER ScriptArgs 34 | Remaining arguments are added here. 35 | 36 | .LINK 37 | http://cakebuild.net 38 | 39 | #> 40 | 41 | [CmdletBinding()] 42 | Param( 43 | [Parameter(Position=0,Mandatory=$false)] 44 | [string]$Target = "default", 45 | [ValidateSet("Release", "Debug")] 46 | [string]$Configuration = "Release", 47 | [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] 48 | [string]$Verbosity = "Verbose", 49 | [Alias("DryRun","Noop")] 50 | [switch]$WhatIf, 51 | [switch]$Mono, 52 | [switch]$SkipToolPackageRestore, 53 | [Parameter(Position=1,Mandatory=$false,ValueFromRemainingArguments=$true)] 54 | [string[]]$ScriptArgs 55 | ) 56 | 57 | $Script = "build.cake" 58 | 59 | [Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null 60 | function MD5HashFile([string] $filePath) 61 | { 62 | if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) 63 | { 64 | return $null 65 | } 66 | 67 | [System.IO.Stream] $file = $null; 68 | [System.Security.Cryptography.MD5] $md5 = $null; 69 | try 70 | { 71 | $md5 = [System.Security.Cryptography.MD5]::Create() 72 | $file = [System.IO.File]::OpenRead($filePath) 73 | return [System.BitConverter]::ToString($md5.ComputeHash($file)) 74 | } 75 | finally 76 | { 77 | if ($file -ne $null) 78 | { 79 | $file.Dispose() 80 | } 81 | } 82 | } 83 | 84 | Write-Host "Preparing to run build script..." 85 | 86 | if(!$PSScriptRoot){ 87 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 88 | } 89 | 90 | $TOOLS_DIR = Join-Path $PSScriptRoot "tools" 91 | $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" 92 | $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" 93 | $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" 94 | $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" 95 | $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" 96 | 97 | # Should we use mono? 98 | $UseMono = ""; 99 | if($Mono.IsPresent) { 100 | Write-Verbose -Message "Using the Mono based scripting engine." 101 | $UseMono = "-mono" 102 | } 103 | 104 | # Should we use the new Roslyn? 105 | $UseExperimental = ""; 106 | if(!($Mono.IsPresent)) { 107 | Write-Verbose -Message "Using experimental version of Roslyn." 108 | $UseExperimental = "-experimental" 109 | } 110 | 111 | # Is this a dry run? 112 | $UseDryRun = ""; 113 | if($WhatIf.IsPresent) { 114 | $UseDryRun = "-dryrun" 115 | } 116 | 117 | # Make sure tools folder exists 118 | if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { 119 | Write-Verbose -Message "Creating tools directory..." 120 | New-Item -Path $TOOLS_DIR -Type directory | out-null 121 | } 122 | 123 | # Make sure that packages.config exist. 124 | if (!(Test-Path $PACKAGES_CONFIG)) { 125 | Write-Verbose -Message "Downloading packages.config..." 126 | try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { 127 | Throw "Could not download packages.config." 128 | } 129 | } 130 | 131 | # Try find NuGet.exe in path if not exists 132 | if (!(Test-Path $NUGET_EXE)) { 133 | Write-Verbose -Message "Trying to find nuget.exe in PATH..." 134 | $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) } 135 | $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 136 | if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { 137 | Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." 138 | $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName 139 | } 140 | } 141 | 142 | # Try download NuGet.exe if not exists 143 | if (!(Test-Path $NUGET_EXE)) { 144 | Write-Verbose -Message "Downloading NuGet.exe..." 145 | try { 146 | (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) 147 | } catch { 148 | Throw "Could not download NuGet.exe." 149 | } 150 | } 151 | 152 | # Save nuget.exe path to environment to be available to child processed 153 | $ENV:NUGET_EXE = $NUGET_EXE 154 | 155 | # Restore tools from NuGet? 156 | if(-Not $SkipToolPackageRestore.IsPresent) { 157 | Push-Location 158 | Set-Location $TOOLS_DIR 159 | 160 | # Check for changes in packages.config and remove installed tools if true. 161 | [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) 162 | if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or 163 | ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { 164 | Write-Verbose -Message "Missing or changed package.config hash..." 165 | Remove-Item * -Recurse -Exclude packages.config,nuget.exe 166 | } 167 | 168 | Write-Verbose -Message "Restoring tools from NuGet..." 169 | $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" 170 | 171 | if ($LASTEXITCODE -ne 0) { 172 | Throw "An error occured while restoring NuGet tools." 173 | } 174 | else 175 | { 176 | $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" 177 | } 178 | Write-Verbose -Message ($NuGetOutput | out-string) 179 | Pop-Location 180 | } 181 | 182 | # Make sure that Cake has been installed. 183 | if (!(Test-Path $CAKE_EXE)) { 184 | Throw "Could not find Cake.exe at $CAKE_EXE" 185 | } 186 | 187 | # Start Cake 188 | Write-Host "Running build script..." 189 | Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs -settings_skipverification=true" 190 | exit $LASTEXITCODE --------------------------------------------------------------------------------