mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 00:21:06 -04:00
feat(ui): add Halloween, Christmas and 4th of July color schemes
Built-in festive schemes theme the background, sheet and accents while keeping 12-color part palettes distinct, light enough for black part IDs and clear of the green etch stroke. They are only applied when chosen; there is no automatic seasonal switching. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9f2e3d5291
commit
b115b9dcb0
3 files changed
+156
No files matched your search
@@ -0,0 +1,56 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenNest.WinForms.Tests;
|
||||
|
||||
public class FestiveColorSchemeTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("Halloween")]
|
||||
[InlineData("Christmas")]
|
||||
[InlineData("4th of July")]
|
||||
public void FestiveSchemeKeepsPartsDistinctAndReadable(string name)
|
||||
{
|
||||
var scheme = ColorSchemeRegistry.Get(name);
|
||||
|
||||
Assert.Equal(name, scheme.Name);
|
||||
Assert.False(scheme.UseGoldenAngleColors);
|
||||
Assert.True(scheme.PartColors.Length >= 12);
|
||||
Assert.Equal(Color.Lime.ToArgb(), scheme.EtchColor.ToArgb());
|
||||
Assert.True(scheme.SelectedPartColor.IsEmpty);
|
||||
|
||||
var colors = scheme.PartColors;
|
||||
for (var i = 0; i < colors.Length; i++)
|
||||
{
|
||||
// Part IDs are drawn in black over the fill.
|
||||
Assert.True(Luminance(colors[i]) >= 90, $"{name} part color {i} is too dark for black part IDs.");
|
||||
Assert.True(Distance(colors[i], scheme.EtchColor) >= 120, $"{name} part color {i} is too close to the etch color.");
|
||||
for (var j = i + 1; j < colors.Length; j++)
|
||||
Assert.True(Distance(colors[i], colors[j]) >= 40, $"{name} part colors {i} and {j} are too similar.");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FestiveSchemesRoundTripAndLeaveClassicAsDefault()
|
||||
{
|
||||
foreach (var name in new[] { "Halloween", "Christmas", "4th of July" })
|
||||
{
|
||||
var scheme = ColorSchemeRegistry.Get(name);
|
||||
var restored = ColorSchemeSerializer.Deserialize(ColorSchemeSerializer.Serialize(scheme));
|
||||
Assert.Equal(name, restored.Name);
|
||||
Assert.Equal(scheme.BackgroundColor.ToArgb(), restored.BackgroundColor.ToArgb());
|
||||
Assert.Equal(scheme.PartColors.Select(c => c.ToArgb()), restored.PartColors.Select(c => c.ToArgb()));
|
||||
}
|
||||
|
||||
Assert.Equal("Classic", ColorSchemeRegistry.Get(null).Name);
|
||||
}
|
||||
|
||||
private static double Distance(Color a, Color b)
|
||||
{
|
||||
var dr = a.R - b.R;
|
||||
var dg = a.G - b.G;
|
||||
var db = a.B - b.B;
|
||||
return System.Math.Sqrt(dr * dr + dg * dg + db * db);
|
||||
}
|
||||
|
||||
private static double Luminance(Color c) => 0.299 * c.R + 0.587 * c.G + 0.114 * c.B;
|
||||
}
|
||||
@@ -19,6 +19,9 @@ namespace OpenNest
|
||||
["Pastel"] = BuildPastel(),
|
||||
["Dark"] = BuildDark(),
|
||||
["Workshop"] = BuildWorkshop(),
|
||||
["Halloween"] = BuildHalloween(),
|
||||
["Christmas"] = BuildChristmas(),
|
||||
["4th of July"] = BuildFourthOfJuly(),
|
||||
};
|
||||
|
||||
private static List<ColorScheme> diskCache;
|
||||
@@ -135,6 +138,95 @@ namespace OpenNest
|
||||
PartColors = Enumerable.Range(0, 12).Select(PartColorPalette.GoldenAngle).ToArray(),
|
||||
};
|
||||
|
||||
// Festive schemes theme the background, sheet and accents; their part palettes
|
||||
// stay varied and light enough for black part IDs, and avoid the etch green.
|
||||
private static ColorScheme BuildHalloween() =>
|
||||
new ColorScheme
|
||||
{
|
||||
Name = "Halloween",
|
||||
BackgroundColor = Color.FromArgb(28, 18, 36),
|
||||
LayoutOutlineColor = Color.FromArgb(110, 80, 130),
|
||||
LayoutFillColor = Color.FromArgb(52, 48, 58),
|
||||
BoundingBoxColor = Color.FromArgb(170, 120, 230),
|
||||
RapidColor = Color.FromArgb(255, 140, 0),
|
||||
OriginColor = Color.FromArgb(130, 110, 140),
|
||||
EdgeSpacingColor = Color.FromArgb(80, 70, 92),
|
||||
PreviewPartColor = Color.FromArgb(160, 255, 80),
|
||||
PartColors = new[]
|
||||
{
|
||||
Color.FromArgb(255, 130, 30), // Pumpkin
|
||||
Color.FromArgb(170, 110, 230), // Witch Purple
|
||||
Color.FromArgb(255, 205, 60), // Candy Corn
|
||||
Color.FromArgb(235, 235, 245), // Ghost
|
||||
Color.FromArgb(220, 60, 60), // Blood Red
|
||||
Color.FromArgb(130, 170, 230), // Moonlight
|
||||
Color.FromArgb(225, 210, 170), // Bone
|
||||
Color.FromArgb(230, 90, 200), // Potion
|
||||
Color.FromArgb(60, 200, 180), // Cauldron Teal
|
||||
Color.FromArgb(200, 120, 60), // Caramel Apple
|
||||
Color.FromArgb(200, 170, 240), // Phantom Lavender
|
||||
Color.FromArgb(255, 90, 40), // Ember
|
||||
},
|
||||
};
|
||||
|
||||
private static ColorScheme BuildChristmas() =>
|
||||
new ColorScheme
|
||||
{
|
||||
Name = "Christmas",
|
||||
BackgroundColor = Color.FromArgb(18, 58, 38),
|
||||
LayoutOutlineColor = Color.FromArgb(190, 160, 80),
|
||||
LayoutFillColor = Color.FromArgb(248, 248, 244),
|
||||
BoundingBoxColor = Color.FromArgb(200, 40, 50),
|
||||
RapidColor = Color.FromArgb(200, 150, 30),
|
||||
OriginColor = Color.FromArgb(150, 130, 90),
|
||||
EdgeSpacingColor = Color.FromArgb(215, 220, 215),
|
||||
PreviewPartColor = Color.FromArgb(230, 60, 60),
|
||||
PartColors = new[]
|
||||
{
|
||||
Color.FromArgb(215, 60, 60), // Holly Red
|
||||
Color.FromArgb(80, 160, 90), // Pine
|
||||
Color.FromArgb(230, 190, 70), // Gold
|
||||
Color.FromArgb(150, 200, 235), // Ice
|
||||
Color.FromArgb(190, 195, 205), // Silver
|
||||
Color.FromArgb(240, 150, 160), // Candy Cane
|
||||
Color.FromArgb(175, 120, 200), // Sugarplum
|
||||
Color.FromArgb(150, 215, 170), // Mint
|
||||
Color.FromArgb(190, 110, 70), // Cinnamon
|
||||
Color.FromArgb(180, 50, 80), // Cranberry
|
||||
Color.FromArgb(245, 225, 160), // Eggnog
|
||||
Color.FromArgb(100, 150, 200), // Winter Sky
|
||||
},
|
||||
};
|
||||
|
||||
private static ColorScheme BuildFourthOfJuly() =>
|
||||
new ColorScheme
|
||||
{
|
||||
Name = "4th of July",
|
||||
BackgroundColor = Color.FromArgb(18, 28, 60),
|
||||
LayoutOutlineColor = Color.FromArgb(110, 120, 150),
|
||||
LayoutFillColor = Color.FromArgb(250, 250, 252),
|
||||
BoundingBoxColor = Color.FromArgb(120, 150, 230),
|
||||
RapidColor = Color.FromArgb(210, 40, 50),
|
||||
OriginColor = Color.FromArgb(120, 130, 160),
|
||||
EdgeSpacingColor = Color.FromArgb(205, 210, 225),
|
||||
PreviewPartColor = Color.FromArgb(255, 190, 40),
|
||||
PartColors = new[]
|
||||
{
|
||||
Color.FromArgb(220, 60, 60), // Old Glory Red
|
||||
Color.FromArgb(110, 160, 230), // Liberty Blue
|
||||
Color.FromArgb(250, 195, 60), // Firework Gold
|
||||
Color.FromArgb(200, 205, 215), // Star Silver
|
||||
Color.FromArgb(220, 90, 180), // Magenta Burst
|
||||
Color.FromArgb(70, 190, 180), // Teal Burst
|
||||
Color.FromArgb(240, 120, 110), // Coral
|
||||
Color.FromArgb(150, 200, 240), // Sky
|
||||
Color.FromArgb(250, 150, 60), // Sparkler
|
||||
Color.FromArgb(170, 150, 230), // Twilight
|
||||
Color.FromArgb(235, 150, 170), // Rose
|
||||
Color.FromArgb(90, 120, 210), // Royal Blue
|
||||
},
|
||||
};
|
||||
|
||||
private static ColorScheme BuildClassic() =>
|
||||
new ColorScheme
|
||||
{
|
||||
|
||||
@@ -36,6 +36,14 @@ classified as `Cut` remains a cut line. Drawing geometry, layer classifications,
|
||||
cutting plans and post-processor output are not changed by the renderer. The CAD
|
||||
conversion/program-editing views retain their own source/classification colors.
|
||||
|
||||
## Festive schemes
|
||||
|
||||
**Halloween**, **Christmas** and **4th of July** are built-in schemes chosen like
|
||||
any other; OpenNest never switches to them automatically. Their background,
|
||||
sheet and accent colors carry the theme, while each 12-color part palette stays
|
||||
varied, light enough for black part IDs and clear of the green etch stroke.
|
||||
Etch, cut-outline and selection behavior match Classic.
|
||||
|
||||
## Custom scheme files
|
||||
|
||||
Scheme JSON files in `Schemes/` beside the application may specify `etchColor`
|
||||
|
||||
Reference in new issue
Block a user