using System; using Minint.Core.Models; namespace Minint.Core.Services.Impl; public sealed class PatternGenerator : IPatternGenerator { public MinintDocument Generate(PatternType type, int width, int height, RgbaColor[] colors, int param1, int param2 = 0) { ArgumentOutOfRangeException.ThrowIfLessThan(width, 1); ArgumentOutOfRangeException.ThrowIfLessThan(height, 1); if (colors.Length < 2) throw new ArgumentException("At least two colors are required.", nameof(colors)); var doc = new MinintDocument($"Pattern ({type})"); var layer = new MinintLayer("Pattern", width * height); doc.Layers.Add(layer); int[] colorIndices = new int[colors.Length]; for (int i = 0; i < colors.Length; i++) colorIndices[i] = doc.EnsureColorCached(colors[i]); int cellSize = Math.Max(param1, 1); switch (type) { case PatternType.Checkerboard: FillCheckerboard(layer.Pixels, width, height, colorIndices, cellSize); break; case PatternType.HorizontalGradient: FillGradient(layer.Pixels, width, height, colors[0], colors[1], doc, horizontal: true); break; case PatternType.VerticalGradient: FillGradient(layer.Pixels, width, height, colors[0], colors[1], doc, horizontal: false); break; case PatternType.HorizontalStripes: FillStripes(layer.Pixels, width, height, colorIndices, cellSize, horizontal: true); break; case PatternType.VerticalStripes: FillStripes(layer.Pixels, width, height, colorIndices, cellSize, horizontal: false); break; case PatternType.ConcentricCircles: FillCircles(layer.Pixels, width, height, colorIndices, cellSize); break; case PatternType.Tile: FillTile(layer.Pixels, width, height, colorIndices, cellSize, Math.Max(param2, 1)); break; } return doc; } private static void FillCheckerboard(int[] pixels, int w, int h, int[] ci, int cell) { for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) pixels[y * w + x] = ci[((x / cell) + (y / cell)) % 2 == 0 ? 0 : 1]; } private static void FillGradient(int[] pixels, int w, int h, RgbaColor c0, RgbaColor c1, MinintDocument doc, bool horizontal) { int steps = horizontal ? w : h; for (int s = 0; s < steps; s++) { double t = steps > 1 ? (double)s / (steps - 1) : 0; var c = new RgbaColor( Lerp(c0.R, c1.R, t), Lerp(c0.G, c1.G, t), Lerp(c0.B, c1.B, t), Lerp(c0.A, c1.A, t)); int idx = doc.EnsureColorCached(c); if (horizontal) for (int y = 0; y < h; y++) pixels[y * w + s] = idx; else for (int x = 0; x < w; x++) pixels[s * w + x] = idx; } } private static void FillStripes(int[] pixels, int w, int h, int[] ci, int stripe, bool horizontal) { for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int coord = horizontal ? y : x; pixels[y * w + x] = ci[(coord / stripe) % ci.Length]; } } private static void FillCircles(int[] pixels, int w, int h, int[] ci, int ringWidth) { double cx = (w - 1) / 2.0, cy = (h - 1) / 2.0; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { double dist = Math.Sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)); int ring = (int)(dist / ringWidth); pixels[y * w + x] = ci[ring % ci.Length]; } } private static void FillTile(int[] pixels, int w, int h, int[] ci, int tileW, int tileH) { for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int tx = (x / tileW) % ci.Length; int ty = (y / tileH) % ci.Length; pixels[y * w + x] = ci[(tx + ty) % ci.Length]; } } private static byte Lerp(byte a, byte b, double t) => (byte)Math.Clamp((int)(a + (b - a) * t + 0.5), 0, 255); }