Skip to content

Commit eb3936d

Browse files
authored
Merge pull request #1459 from Peter-B-/doc-generator
Doc generator
2 parents 21ff20c + 1fe04bc commit eb3936d

29 files changed

Lines changed: 275 additions & 4 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,5 @@ _Pvt_Extensions
240240
.fake/
241241

242242
# OxyPlot specific
243-
Output/
243+
Output/
244+
launchSettings.json

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
1414
- Added properties LegendKey and SeriesGroupName to Series, allowing grouping series between multiple legends and/or within same legend
1515
- Remove PlotModel.Legends
1616
- Default behaviour is now plot without Legend.
17+
- Generate documentation images from ExampleLibrary
1718

1819
### Added
1920
- OxyPlot.ImageSharp (#1188)

CONTRIBUTORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ mrtncls
9090
Oleg Tarasov <oleg.v.tarasov@gmail.com>
9191
Oystein Bjorke <oystein.bjorke@gmail.com>
9292
Patrice Marin <patrice.marin@thomsonreuters.com>
93+
Peter-B-
9394
Philippe AURIOU <p.auriou@live.fr>
9495
Piotr Warzocha <pw@piootr.pl>
9596
Poul Erik Venø <poulerikvenoehansen@gmail.com>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.0</TargetFramework>
5+
<!--<UseWindowsForms>true</UseWindowsForms>-->
6+
<OutputType>Exe</OutputType>
7+
<ApplicationIcon />
8+
<StartupObject />
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\OxyPlot.WindowsForms\OxyPlot.WindowsForms.csproj" />
13+
<ProjectReference Include="..\ExampleLibrary\ExampleLibrary.csproj" />
14+
</ItemGroup>
15+
<!--<ItemGroup>
16+
<Content Include="..\TruePNG\TruePNG.exe">
17+
<Link>TruePNG.exe</Link>
18+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
19+
</Content>
20+
</ItemGroup>-->
21+
22+
</Project>
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Diagnostics;
5+
using System.IO;
6+
using System.Reflection;
7+
using System.Threading.Tasks;
8+
using ExampleLibrary;
9+
using OxyPlot;
10+
using OxyPlot.WindowsForms;
11+
12+
namespace ExampleGenerator
13+
{
14+
public static class Program
15+
{
16+
private static readonly OxyColor defaultBackground = OxyColor.Parse("#00000000");
17+
18+
public static string OutputDirectory { get; set; }
19+
20+
public static bool DoOptimizePng { get; set; }
21+
22+
public static bool ExportPng { get; set; }
23+
24+
public static bool ExportPdf { get; set; }
25+
26+
public static bool ExportSvg { get; set; }
27+
28+
public static void Main(string[] args)
29+
{
30+
ExportPng = true;
31+
ExportPdf = true;
32+
ExportSvg = true;
33+
//DoOptimizePng = true;
34+
OutputDirectory = @".";
35+
if (args.Length > 0)
36+
{
37+
OutputDirectory = args[0];
38+
}
39+
40+
var exportTasks = new List<Task>();
41+
42+
var exampleAssembly = typeof(DocumentationExampleAttribute).Assembly;
43+
foreach (var type in exampleAssembly.GetTypes())
44+
{
45+
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
46+
{
47+
var exportAttribute = method.GetCustomAttribute<DocumentationExampleAttribute>();
48+
if (exportAttribute == null)
49+
{
50+
continue;
51+
}
52+
53+
var model = (PlotModel)method.Invoke(null, null);
54+
var exportTask = Export(model, exportAttribute.Filename.Replace('/', Path.DirectorySeparatorChar));
55+
exportTasks.Add(exportTask);
56+
}
57+
}
58+
59+
//Wait for exports to finish
60+
Task.WaitAll(exportTasks.ToArray());
61+
}
62+
63+
private static async Task Export(PlotModel model, string name)
64+
{
65+
if (model.Background == defaultBackground)
66+
model.Background = OxyColors.White;
67+
68+
var fileName = Path.Combine(OutputDirectory, name + ".png");
69+
var directory = Path.GetDirectoryName(fileName) ?? ".";
70+
71+
if (!Directory.Exists(directory))
72+
{
73+
Directory.CreateDirectory(directory);
74+
}
75+
76+
if (ExportPng)
77+
{
78+
Console.WriteLine(fileName);
79+
using (var stream = File.Create(fileName))
80+
{
81+
var exporter = new PngExporter { Width = 600, Height = 400 };
82+
exporter.Export(model, stream);
83+
}
84+
85+
if (DoOptimizePng)
86+
await OptimizePng(fileName);
87+
}
88+
89+
if (ExportPdf)
90+
{
91+
fileName = Path.ChangeExtension(fileName, ".pdf");
92+
Console.WriteLine(fileName);
93+
using (var stream = File.Create(fileName))
94+
{
95+
var exporter = new PdfExporter { Width = 600d * 72 / 96, Height = 400d * 72 / 96 };
96+
exporter.Export(model, stream);
97+
}
98+
}
99+
100+
if (ExportSvg)
101+
{
102+
fileName = Path.ChangeExtension(fileName, ".svg");
103+
Console.WriteLine(fileName);
104+
105+
using (var stream = File.Create(fileName))
106+
{
107+
using (var exporter = new OxyPlot.WindowsForms.SvgExporter { Width = 600, Height = 400, IsDocument = true })
108+
{
109+
exporter.Export(model, stream);
110+
}
111+
}
112+
}
113+
}
114+
115+
116+
/* PNG Optimization */
117+
118+
private static async Task OptimizePng(string pngFile)
119+
{
120+
if (Environment.OSVersion.Platform == PlatformID.Unix)
121+
{
122+
await OptimizePngWithOptiPNG(pngFile);
123+
}
124+
else
125+
{
126+
await OptimizePngWithTruePNG(pngFile);
127+
}
128+
}
129+
130+
private static async Task OptimizePngWithTruePNG(string pngFile)
131+
{
132+
// /o max : optimization level
133+
// /nc : don't change ColorType and BitDepth
134+
// /md keep pHYs : keep pHYs metadata
135+
var psi = new ProcessStartInfo("TruePNG.exe", pngFile + " /o max /nc /md keep pHYs")
136+
{
137+
CreateNoWindow = true,
138+
WindowStyle = ProcessWindowStyle.Hidden
139+
};
140+
try
141+
{
142+
var p = Process.Start(psi);
143+
await Task.Run(() => p.WaitForExit());
144+
}
145+
catch (Win32Exception e)
146+
{
147+
throw new Win32Exception(
148+
"Failed to run TruePNG optimization. Please ensure that TruePNG is installed and registered in the PATH variable.",
149+
e);
150+
}
151+
}
152+
153+
private static async Task OptimizePngWithOptiPNG(string pngFile)
154+
{
155+
var psi = new ProcessStartInfo("optipng", "-o7 " + pngFile)
156+
{
157+
CreateNoWindow = true,
158+
WindowStyle = ProcessWindowStyle.Hidden
159+
};
160+
var p = Process.Start(psi);
161+
await Task.Run(() => p.WaitForExit());
162+
}
163+
}
164+
165+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
3+
namespace ExampleLibrary
4+
{
5+
/// <summary>
6+
/// Marks the model as documentation example to be exported by the ExampleGenerator program.
7+
/// </summary>
8+
[AttributeUsage(AttributeTargets.Method)]
9+
public class DocumentationExampleAttribute : Attribute
10+
{
11+
/// <summary>
12+
///
13+
/// </summary>
14+
/// <param name="filename">The filename of the documentation file without extension. For sub folders, use '/' as path delimiter.</param>
15+
public DocumentationExampleAttribute(string filename)
16+
{
17+
this.Filename = filename;
18+
}
19+
20+
/// <summary>
21+
/// Gets the filename.
22+
/// </summary>
23+
/// <value>The filename.</value>
24+
/// <remarks>
25+
/// For sub folders, use '/' as path delimiter.
26+
/// This is then replaced with the current platforms path separator later in the process.
27+
/// </remarks>
28+
public string Filename { get; private set; }
29+
}
30+
}

Source/Examples/ExampleLibrary/Series/AreaSeriesExamples.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace ExampleLibrary
1616
public static class AreaSeriesExamples
1717
{
1818
[Example("Default style")]
19+
[DocumentationExample("Series/AreaSeries")]
1920
public static PlotModel DefaultStyle()
2021
{
2122
var plotModel1 = new PlotModel { Title = "AreaSeries with default style" };

Source/Examples/ExampleLibrary/Series/BarSeriesExamples.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public static PlotModel WithLabelsXAxisReversed()
7070
}
7171

7272
[Example("Stacked")]
73+
[DocumentationExample("Series/BarSeries")]
7374
public static PlotModel StackedSeries()
7475
{
7576
return CreateSimpleModel(true, "Simple stacked model");

Source/Examples/ExampleLibrary/Series/BoxPlotSeriesExamples.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ public static PlotModel MichelsonMorleyExperiment()
173173
}
174174

175175
[Example("BoxPlot (DateTime axis)")]
176+
[DocumentationExample("Series/BoxPlotSeries")]
176177
public static PlotModel BoxPlotSeries_DateTimeAxis()
177178
{
178179
var m = new PlotModel();

Source/Examples/ExampleLibrary/Series/ContourSeriesExamples.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public static PlotModel Peaks()
3636
}
3737

3838
[Example("Peaks (different contour colors)")]
39+
[DocumentationExample("Series/ContourSeries")]
3940
public static PlotModel PeaksWithColors()
4041
{
4142
var model = new PlotModel { Title = "Peaks" };

0 commit comments

Comments
 (0)