-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMazeSvgExporter.cs
196 lines (181 loc) · 6.3 KB
/
MazeSvgExporter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Labyrinthian.Svg
{
/// <summary>
/// Class that can export mazes into SVG format.
/// </summary>
/// <example>
/// This code snippet demonstrates the creation, generation, and export of an orthogonal maze as an SVG file:
/// <code>
/// using Labyrinthian;
/// using Labyrinthian.Svg;
///
/// // Create an orthogonal maze 30x20
/// Maze maze = new OrthogonalMaze(30, 20);
/// // Generate it using Prim's algorithm
/// MazeGenerator generator = new PrimGeneration(maze);
/// generator.Generate();
///
/// // Create a maze exporter(it doesn't need to be closed or disposed)
/// MazeSvgExporter exporter = new MazeSvgExporter(maze)
/// {
/// // Here we're adding export modules.
/// // You can also use MazeSvgExporter.Add(IExportModule) method for this
/// Walls.AsOnePath()
///
/// /*
/// Besides Walls there are others modules, including:
///
/// * Background
/// * Cells
/// * Edges
/// * MazeDescription
/// * Nodes
/// * Solutions
/// */
/// };
///
/// // Use a FileStream for exporting.
/// // You can also use any Stream or TextWriter(e.g. StreamWriter) or XmlWriter
/// using var fs = File.Create(@"d:\orthogonal-maze.svg");
/// using var svgWriter = new SvgWriter(fs);
/// // Export a maze
/// exporter.Export(svgWriter);
///
/// // Note: SvgWriter should be disposed after exporting.
/// // Here we do it with 'using'
/// </code>
/// </example>
public sealed class MazeSvgExporter : IEnumerable<IExportModule>
{
private float _cellSize, _padding;
private Walls? _wallsModule;
private readonly List<IExportModule> _exportModules = new List<IExportModule>();
/// <summary>
/// Maze that's being exported.
/// </summary>
public readonly Maze Maze;
/// <summary>
/// Size of a maze cell.
/// </summary>
public float CellSize
{
get => _cellSize;
set
{
_cellSize = value;
RecalculateSizes();
}
}
/// <summary>
/// Padding from all sides.
/// </summary>
public float Padding
{
get => _padding;
set
{
_padding = value;
RecalculateSizes();
}
}
/// <summary>
/// Offset from top and left. Can't be set.
/// </summary>
public float Offset { get; private set; }
/// <summary>
/// Width of SVG document(in pixels). Can't be set.
/// </summary>
public float Width { get; private set; }
/// <summary>
/// Height of SVG document(in pixels). Can't be set.
/// </summary>
public float Height { get; private set; }
private void RecalculateSizes()
{
Offset = Padding;
if (_wallsModule != null) Offset += _wallsModule.WallsWidth / 2f;
Width = Maze.Width2D * CellSize + Offset * 2f;
Height = Maze.Height2D * CellSize + Offset * 2f;
}
/// <summary>
/// Create an SVG-exporter.
/// </summary>
/// <param name="maze">Maze that will be exported.</param>
/// <param name="cellSize">Size of one maze cell.</param>
/// <param name="padding">Padding from all sides.</param>
public MazeSvgExporter(Maze maze, float cellSize = 32f, float padding = 0f)
{
Maze = maze;
_cellSize = cellSize;
_padding = padding;
RecalculateSizes();
}
/// <summary>
/// Add a module for future exporting.
/// </summary>
/// <remarks>
/// Order, in which modules are added, matters.
/// For instance, if you add background first and walls later,
/// then walls will be drawn in the top of the background.
/// </remarks>
/// <param name="exportModule">Export module to be added.</param>
public void Add(IExportModule exportModule)
{
_exportModules.Add(exportModule);
if (exportModule is Walls wallsModule)
{
_wallsModule = wallsModule;
RecalculateSizes();
}
}
/// <inheritdoc cref="ExportAsync" />
public void Export(SvgWriter svgWriter, SvgRoot? root = null, string? style = null)
{
AsyncHelper.RunSync(() => ExportAsync(svgWriter, root, style));
}
/// <summary>
/// Export a maze using provided SVG-writer.
/// </summary>
/// <param name="svgWriter">
/// SVG-writer to be used.
/// </param>
/// <param name="root">
/// Optional <svg> element parameters.
/// </param>
/// <param name="style">
/// Optional CSS style.
/// </param>
/// <exception cref="InvalidOperationException" />
public async Task ExportAsync(SvgWriter svgWriter, SvgRoot? root = null, string? style = null)
{
if (_exportModules.Count == 0)
{
throw new InvalidOperationException("Export modules are not added. Please make sure, that before exporting a maze, you have added at least one export module using method Add.");
}
SvgRoot svgRoot = root ?? new SvgRoot();
svgRoot.ViewBox ??= new SvgViewBox(0f, 0f, Width, Height);
await svgWriter.StartRootAsync(svgRoot);
if (style != null)
{
await svgWriter.WriteStringElementAsync("style", style);
}
foreach (IExportModule exportModule in _exportModules)
{
await exportModule.ExportAsync(this, svgWriter);
}
await svgWriter.EndRootAsync();
}
public IEnumerator<IExportModule> GetEnumerator()
{
return ((IEnumerable<IExportModule>)_exportModules).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_exportModules).GetEnumerator();
}
}
}