Skip to content

Commit ecd0637

Browse files
committed
Added digits at the bottom
1 parent 6e89962 commit ecd0637

File tree

5 files changed

+281
-14
lines changed

5 files changed

+281
-14
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
namespace DeveMazeGeneratorCore.Coaster3MF
2+
{
3+
/// <summary>
4+
/// Generates 3x5 block patterns for digits 0-9.
5+
/// Each pattern is represented as a 2D boolean array where true = block, false = empty.
6+
/// Patterns are designed to be clear and readable when rendered as blocks.
7+
/// </summary>
8+
public static class DigitPatternGenerator
9+
{
10+
private const int PatternWidth = 3;
11+
private const int PatternHeight = 5;
12+
13+
/// <summary>
14+
/// Gets the 3x5 pattern for a given digit (0-9).
15+
/// Returns null if the digit is not in the valid range.
16+
/// Pattern coordinates: [y, x] where y is row (0=top, 4=bottom) and x is column (0=left, 2=right)
17+
/// </summary>
18+
public static bool[,]? GetDigitPattern(int digit)
19+
{
20+
return digit switch
21+
{
22+
0 => new bool[,]
23+
{
24+
{ true, true, true },
25+
{ true, false, true },
26+
{ true, false, true },
27+
{ true, false, true },
28+
{ true, true, true }
29+
},
30+
1 => new bool[,]
31+
{
32+
{ false, true, false },
33+
{ false, true, false },
34+
{ false, true, false },
35+
{ false, true, false },
36+
{ false, true, false }
37+
},
38+
2 => new bool[,]
39+
{
40+
{ true, true, true },
41+
{ false, false, true },
42+
{ true, true, true },
43+
{ true, false, false },
44+
{ true, true, true }
45+
},
46+
3 => new bool[,]
47+
{
48+
{ true, true, true },
49+
{ false, false, true },
50+
{ true, true, true },
51+
{ false, false, true },
52+
{ true, true, true }
53+
},
54+
4 => new bool[,]
55+
{
56+
{ true, false, true },
57+
{ true, false, true },
58+
{ true, true, true },
59+
{ false, false, true },
60+
{ false, false, true }
61+
},
62+
5 => new bool[,]
63+
{
64+
{ true, true, true },
65+
{ true, false, false },
66+
{ true, true, true },
67+
{ false, false, true },
68+
{ true, true, true }
69+
},
70+
6 => new bool[,]
71+
{
72+
{ true, true, true },
73+
{ true, false, false },
74+
{ true, true, true },
75+
{ true, false, true },
76+
{ true, true, true }
77+
},
78+
7 => new bool[,]
79+
{
80+
{ true, true, true },
81+
{ false, false, true },
82+
{ false, true, false },
83+
{ false, true, false },
84+
{ false, true, false }
85+
},
86+
8 => new bool[,]
87+
{
88+
{ true, true, true },
89+
{ true, false, true },
90+
{ true, true, true },
91+
{ true, false, true },
92+
{ true, true, true }
93+
},
94+
9 => new bool[,]
95+
{
96+
{ true, true, true },
97+
{ true, false, true },
98+
{ true, true, true },
99+
{ false, false, true },
100+
{ true, true, true }
101+
},
102+
_ => null
103+
};
104+
}
105+
106+
/// <summary>
107+
/// Gets the width of digit patterns (always 3).
108+
/// </summary>
109+
public static int GetPatternWidth() => PatternWidth;
110+
111+
/// <summary>
112+
/// Gets the height of digit patterns (always 5).
113+
/// </summary>
114+
public static int GetPatternHeight() => PatternHeight;
115+
}
116+
}

DeveMazeGeneratorCore.Coaster3MF/MazeCoaster3MF.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,12 @@ public void Generate3MFCoaster(int mazeSize, int? seed = null, int? chunkItUp =
7777

7878
int plateNumber = 0;
7979
// Bunch up the mesh into plates (4 coasters per plate)
80+
var usedSeed = seed ?? 1337;
8081
var plates = mazesToCoasterUp.Chunk(4).Select(t => {
8182
var plateModels = t.Select(mwp =>
8283
{
8384
// Generate the geometry data for each maze chunk
84-
var meshData = _geometryGenerator.GenerateMazeGeometry(mwp.InnerMap, mwp.Path);
85+
var meshData = _geometryGenerator.GenerateMazeGeometry(mwp.InnerMap, mwp.Path, seed: usedSeed);
8586
Validate(mazeSize, meshData);
8687
return GenerateModel(meshData);
8788
}).ToList();
@@ -90,7 +91,6 @@ public void Generate3MFCoaster(int mazeSize, int? seed = null, int? chunkItUp =
9091

9192

9293
// Generate filename with triangle and vertex counts
93-
var usedSeed = seed ?? 1337;
9494
var totalTriangles = plates.Sum(p => p.Models.Sum(m => m.MeshData.Triangles.Count));
9595
var totalVertices = plates.Sum(p => p.Models.Sum(m => m.MeshData.Vertices.Count));
9696
var filename = $"maze_coaster_{mazeSize}x{mazeSize}_seed{usedSeed}_{totalTriangles}tri_{totalVertices}vert.3mf";

DeveMazeGeneratorCore.Coaster3MF/MazeGeometryGenerator.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ public class MazeGeometryGenerator
3434
/// - Vertices are reused across quads to ensure manifold geometry
3535
/// - Proper counter-clockwise winding order for outward-facing normals
3636
/// </summary>
37-
public MeshData GenerateMazeGeometry(InnerMap maze, List<MazePointPos> path, bool singleCuboidPerPixel = true)
37+
public MeshData GenerateMazeGeometry(InnerMap maze, List<MazePointPos> path, bool singleCuboidPerPixel = true, int? seed = null)
3838
{
3939
// Step 1: Generate quads
40-
var quads = GenerateMazeQuads(maze, path, singleCuboidPerPixel);
40+
var quads = GenerateMazeQuads(maze, path, singleCuboidPerPixel, seed: seed);
4141

4242
// Step 2: Convert quads to mesh data
4343
return ConvertQuadsToMesh(quads);
@@ -46,15 +46,15 @@ public MeshData GenerateMazeGeometry(InnerMap maze, List<MazePointPos> path, boo
4646
/// <summary>
4747
/// Generates quads representing the maze geometry (ground, walls, path).
4848
/// </summary>
49-
public List<Quad> GenerateMazeQuads(InnerMap maze, List<MazePointPos> path, bool singleCuboidPerPixel = true, bool enableFaceCulling = true)
49+
public List<Quad> GenerateMazeQuads(InnerMap maze, List<MazePointPos> path, bool singleCuboidPerPixel = true, bool enableFaceCulling = true, int? seed = null)
5050
{
5151
var quads = new List<Quad>();
5252

5353
// Convert path to PathData for better organization
5454
var pathData = new PathData(path);
5555

5656
// Ground plane quads
57-
AddGroundPlaneQuads(quads, maze, singleCuboidPerPixel);
57+
AddGroundPlaneQuads(quads, maze, singleCuboidPerPixel, seed);
5858

5959
// Add wall quads - now split into two parts
6060
AddMazeWalls(maze, quads, singleCuboidPerPixel);
@@ -223,17 +223,34 @@ public MeshData ConvertQuadsToMesh(List<Quad> quads)
223223
return meshData;
224224
}
225225

226-
private void AddGroundPlaneQuads(List<Quad> quads, InnerMap maze, bool singleCuboidPerPixel)
226+
private void AddGroundPlaneQuads(List<Quad> quads, InnerMap maze, bool singleCuboidPerPixel, int? seed = null)
227227
{
228+
// Get the number pattern if seed is provided
229+
bool[,]? numberPattern = null;
230+
if (seed.HasValue)
231+
{
232+
numberPattern = NumberRenderer.RenderNumber(seed.Value, maze.Width, maze.Height);
233+
}
234+
228235
if (singleCuboidPerPixel)
229236
{
230237
// Generate one cube for each ground cell (maze.Width-1 x maze.Height-1)
231238
for (int y = 0; y < maze.Height; y++)
232239
{
233240
for (int x = 0; x < maze.Width; x++)
234241
{
235-
// Each ground cube has a white top and black sides/bottom
236-
AddCubeQuads(quads, x, y, 0, GroundHeight, Colors[0], Colors[1]); // White ground cubes
242+
// Check if this position should be part of the number (white on bottom)
243+
string bottomColor = Colors[0]; // Default: black bottom
244+
if (numberPattern != null && y < numberPattern.GetLength(0) && x < numberPattern.GetLength(1))
245+
{
246+
if (numberPattern[y, x])
247+
{
248+
bottomColor = Colors[1]; // White bottom for number
249+
}
250+
}
251+
252+
// Each ground cube has a white top and black (or white for number) bottom
253+
AddCubeQuads(quads, x, y, 0, GroundHeight, Colors[0], Colors[1], bottomColor);
237254
}
238255
}
239256
}
@@ -244,12 +261,12 @@ private void AddGroundPlaneQuads(List<Quad> quads, InnerMap maze, bool singleCub
244261
}
245262
}
246263

247-
private void AddCubeQuads(List<Quad> quads, int x, int y, float zBottom, float zTop, string paintColor, string? topFacePaintColor = null)
264+
private void AddCubeQuads(List<Quad> quads, int x, int y, float zBottom, float zTop, string paintColor, string? topFacePaintColor = null, string? bottomFacePaintColor = null)
248265
{
249-
AddCubeQuadsWithDimensions(quads, x, y, x + 1, y + 1, zBottom, zTop, paintColor, topFacePaintColor);
266+
AddCubeQuadsWithDimensions(quads, x, y, x + 1, y + 1, zBottom, zTop, paintColor, topFacePaintColor, bottomFacePaintColor);
250267
}
251268

252-
private void AddCubeQuadsWithDimensions(List<Quad> quads, float x, float y, float endX, float endY, float zBottom, float zTop, string paintColor, string? topFacePaintColor = null)
269+
private void AddCubeQuadsWithDimensions(List<Quad> quads, float x, float y, float endX, float endY, float zBottom, float zTop, string paintColor, string? topFacePaintColor = null, string? bottomFacePaintColor = null)
253270
{
254271
// Apply XY scaling to coordinates
255272
var scaledX = x * XYScale;
@@ -263,7 +280,7 @@ private void AddCubeQuadsWithDimensions(List<Quad> quads, float x, float y, floa
263280
new Vertex(scaledEndX, scaledY, zBottom),
264281
new Vertex(scaledEndX, scaledEndY, zBottom),
265282
new Vertex(scaledX, scaledEndY, zBottom),
266-
paintColor,
283+
bottomFacePaintColor ?? paintColor,
267284
FaceDirection.Bottom
268285
));
269286

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
namespace DeveMazeGeneratorCore.Coaster3MF
2+
{
3+
/// <summary>
4+
/// Renders numbers as a sequence of 3x5 block patterns centered in the maze.
5+
/// Each digit is separated by a 1-block gap, with an underline below to indicate orientation.
6+
/// </summary>
7+
public class NumberRenderer
8+
{
9+
private const int DigitWidth = 3;
10+
private const int DigitHeight = 5;
11+
private const int DigitSpacing = 1; // Gap between digits
12+
private const int UnderlineGap = 1; // Gap between number and underline
13+
private const int UnderlineHeight = 1; // Height of the underline
14+
15+
/// <summary>
16+
/// Calculates the total width needed to render a number (including spacing).
17+
/// </summary>
18+
public static int GetNumberWidth(int number)
19+
{
20+
var digits = GetDigits(number);
21+
if (digits.Count == 0) return 0;
22+
23+
// Width = (number of digits × digit width) + (gaps between digits)
24+
return (digits.Count * DigitWidth) + ((digits.Count - 1) * DigitSpacing);
25+
}
26+
27+
/// <summary>
28+
/// Renders a number as blocks centered in the maze.
29+
/// Returns a 2D array representing the blocks pattern, where true = white block.
30+
/// The pattern is centered both horizontally and vertically, with an underline below.
31+
/// </summary>
32+
/// <param name="number">The number to render (seed value)</param>
33+
/// <param name="mazeWidth">The width of the maze in blocks</param>
34+
/// <param name="mazeHeight">The height of the maze in blocks</param>
35+
/// <returns>A 2D array [y, x] where true indicates a white block should be placed</returns>
36+
public static bool[,] RenderNumber(int number, int mazeWidth, int mazeHeight)
37+
{
38+
var digits = GetDigits(number);
39+
if (digits.Count == 0)
40+
{
41+
return new bool[0, 0];
42+
}
43+
44+
var numberWidth = GetNumberWidth(number);
45+
var totalHeight = DigitHeight + UnderlineGap + UnderlineHeight;
46+
47+
// Result covers the entire maze
48+
var result = new bool[mazeHeight, mazeWidth];
49+
50+
// Calculate starting positions to center both horizontally and vertically
51+
// The center point should be for the entire content (digits + gap + underline)
52+
var startX = Math.Max(0, (mazeWidth - numberWidth) / 2);
53+
var centerY = mazeHeight / 2;
54+
55+
// Position underline above center, then gap, then digits (since it's mirrored)
56+
var underlineY = centerY - (totalHeight / 2);
57+
var startY = underlineY + UnderlineHeight + UnderlineGap;
58+
59+
// Render each digit
60+
var currentX = startX;
61+
foreach (var digit in digits)
62+
{
63+
var pattern = DigitPatternGenerator.GetDigitPattern(digit);
64+
if (pattern == null) continue;
65+
66+
// Copy the digit pattern into the result array (mirrored vertically since it's on the bottom)
67+
for (int y = 0; y < DigitHeight; y++)
68+
{
69+
for (int x = 0; x < DigitWidth; x++)
70+
{
71+
var targetX = currentX + x;
72+
// Mirror vertically: bottom row of pattern becomes top row
73+
var targetY = startY + (DigitHeight - 1 - y);
74+
if (targetX >= 0 && targetX < mazeWidth && targetY >= 0 && targetY < mazeHeight)
75+
{
76+
result[targetY, targetX] = pattern[y, x];
77+
}
78+
}
79+
}
80+
81+
// Move to next digit position
82+
currentX += DigitWidth + DigitSpacing;
83+
}
84+
85+
// Add underline (already calculated position above)
86+
if (underlineY >= 0 && underlineY < mazeHeight)
87+
{
88+
for (int x = startX; x < startX + numberWidth && x < mazeWidth; x++)
89+
{
90+
result[underlineY, x] = true;
91+
}
92+
}
93+
94+
return result;
95+
}
96+
97+
/// <summary>
98+
/// Extracts individual digits from a number in left-to-right order.
99+
/// </summary>
100+
private static List<int> GetDigits(int number)
101+
{
102+
// Handle negative numbers by taking absolute value
103+
number = Math.Abs(number);
104+
105+
var digits = new List<int>();
106+
if (number == 0)
107+
{
108+
digits.Add(0);
109+
return digits;
110+
}
111+
112+
while (number > 0)
113+
{
114+
digits.Insert(0, number % 10);
115+
number /= 10;
116+
}
117+
118+
return digits;
119+
}
120+
121+
/// <summary>
122+
/// Gets the total height of the number pattern including underline (5 + 1 gap + 1 underline = 7).
123+
/// </summary>
124+
public static int GetNumberHeight() => DigitHeight + UnderlineGap + UnderlineHeight;
125+
}
126+
}

DeveMazeGeneratorCore.Coaster3MF/Program.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,18 @@ public static void Main(string[] args)
4646
}
4747
}
4848

49+
//for (int i = 1; i <= 3; i++)
50+
//{
51+
// Console.WriteLine($"Creating coaster {i}...");
52+
// mazeCoaster.Generate3MFCoaster(57, 1337 + i, 57 / 3);
53+
// Console.WriteLine();
54+
// Console.WriteLine();
55+
//}
56+
4957
for (int i = 1; i <= 3; i++)
5058
{
5159
Console.WriteLine($"Creating coaster {i}...");
52-
mazeCoaster.Generate3MFCoaster(57, 1337 + i, 57 / 3);
60+
mazeCoaster.Generate3MFCoaster(19, i);
5361
Console.WriteLine();
5462
Console.WriteLine();
5563
}

0 commit comments

Comments
 (0)