114 lines
4.3 KiB
C#
114 lines
4.3 KiB
C#
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);
|
|
}
|