Added colors projects
This commit is contained in:
Родитель
f266ff12df
Коммит
6523bef8e3
|
@ -41,8 +41,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{A88815F2
|
|||
build\Newtonsoft.Json.props = build\Newtonsoft.Json.props
|
||||
build\ReactiveUI.props = build\ReactiveUI.props
|
||||
build\Rx.props = build\Rx.props
|
||||
build\XUnit.props = build\XUnit.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F2D7C825-1AC1-45FF-A221-CEE80E2F2374}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThemeEditor.Colors", "src\ThemeEditor.Colors\ThemeEditor.Colors.csproj", "{BFDD803E-2B9B-46A2-AC25-C77FD2376893}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThemeEditor.Colors.UnitTests", "tests\ThemeEditor.Colors.UnitTests\ThemeEditor.Colors.UnitTests.csproj", "{E2079787-7F2F-46F8-BD56-2C226D883904}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -57,6 +64,14 @@ Global
|
|||
{2B5611EB-1E0D-4EAB-96C9-821A965E496B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2B5611EB-1E0D-4EAB-96C9-821A965E496B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B5611EB-1E0D-4EAB-96C9-821A965E496B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BFDD803E-2B9B-46A2-AC25-C77FD2376893}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BFDD803E-2B9B-46A2-AC25-C77FD2376893}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BFDD803E-2B9B-46A2-AC25-C77FD2376893}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BFDD803E-2B9B-46A2-AC25-C77FD2376893}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E2079787-7F2F-46F8-BD56-2C226D883904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E2079787-7F2F-46F8-BD56-2C226D883904}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E2079787-7F2F-46F8-BD56-2C226D883904}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E2079787-7F2F-46F8-BD56-2C226D883904}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -68,6 +83,8 @@ Global
|
|||
{497FBEFD-7228-4E2F-A947-9F416498E263} = {3353FD4A-FE9B-4670-86C9-09EF0F7CEDB2}
|
||||
{9A80222C-EDE8-42AF-9D6D-76325FC4673F} = {3353FD4A-FE9B-4670-86C9-09EF0F7CEDB2}
|
||||
{A88815F2-21C3-4EBA-8DBC-0A461EA7F82C} = {3353FD4A-FE9B-4670-86C9-09EF0F7CEDB2}
|
||||
{BFDD803E-2B9B-46A2-AC25-C77FD2376893} = {219954C4-6A40-4D94-914E-148B7EBB718E}
|
||||
{E2079787-7F2F-46F8-BD56-2C226D883904} = {F2D7C825-1AC1-45FF-A221-CEE80E2F2374}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {CF676549-9409-4598-8229-80E04B51ADC5}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
|
||||
namespace ThemeEditor.Colors
|
||||
{
|
||||
public readonly struct HSV
|
||||
{
|
||||
public double H { get; }
|
||||
public double S { get; }
|
||||
public double V { get; }
|
||||
|
||||
public HSV(double h, double s, double v)
|
||||
{
|
||||
H = h;
|
||||
S = s;
|
||||
V = v;
|
||||
}
|
||||
|
||||
public HSV(HSV hsv)
|
||||
{
|
||||
H = hsv.H;
|
||||
S = hsv.S;
|
||||
V = hsv.V;
|
||||
}
|
||||
|
||||
public HSV(RGB rgb)
|
||||
{
|
||||
HSV hsv = rgb.ToHSV();
|
||||
H = hsv.H;
|
||||
S = hsv.S;
|
||||
V = hsv.V;
|
||||
}
|
||||
|
||||
public RGB ToRGB() => ToRGB(H, S, V);
|
||||
|
||||
public static RGB ToRGB(double h, double s, double v)
|
||||
{
|
||||
double R = default;
|
||||
double G = default;
|
||||
double B = default;
|
||||
|
||||
if (s == 0)
|
||||
{
|
||||
R = G = B = Math.Round(v * 2.55);
|
||||
return new RGB(R, G, B);
|
||||
}
|
||||
|
||||
s = s / 100;
|
||||
v = v / 100;
|
||||
h /= 60;
|
||||
|
||||
var i = Math.Floor(h);
|
||||
var f = h - i;
|
||||
var p = v * (1 - s);
|
||||
var q = v * (1 - s * f);
|
||||
var t = v * (1 - s * (1 - f));
|
||||
|
||||
switch ((int)i)
|
||||
{
|
||||
case 0:
|
||||
R = v;
|
||||
G = t;
|
||||
B = p;
|
||||
break;
|
||||
case 1:
|
||||
R = q;
|
||||
G = v;
|
||||
B = p;
|
||||
break;
|
||||
case 2:
|
||||
R = p;
|
||||
G = v;
|
||||
B = t;
|
||||
break;
|
||||
case 3:
|
||||
R = p;
|
||||
G = q;
|
||||
B = v;
|
||||
break;
|
||||
case 4:
|
||||
R = t;
|
||||
G = p;
|
||||
B = v;
|
||||
break;
|
||||
default:
|
||||
R = v;
|
||||
G = p;
|
||||
B = q;
|
||||
break;
|
||||
}
|
||||
|
||||
R = Math.Round(R * 255);
|
||||
G = Math.Round(G * 255);
|
||||
B = Math.Round(B * 255);
|
||||
|
||||
return new RGB(R, G, B);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
|
||||
namespace ThemeEditor.Colors
|
||||
{
|
||||
public readonly struct RGB
|
||||
{
|
||||
public double R { get; }
|
||||
public double G { get; }
|
||||
public double B { get; }
|
||||
|
||||
public RGB(double r, double g, double b)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
|
||||
public RGB(RGB rgb)
|
||||
{
|
||||
R = rgb.R;
|
||||
G = rgb.G;
|
||||
B = rgb.B;
|
||||
}
|
||||
|
||||
public RGB(HSV hsv)
|
||||
{
|
||||
RGB rgb = hsv.ToRGB();
|
||||
R = rgb.R;
|
||||
G = rgb.G;
|
||||
B = rgb.B;
|
||||
}
|
||||
|
||||
public HSV ToHSV() => ToHSV(R, G, B);
|
||||
|
||||
public static HSV ToHSV(double r, double g, double b)
|
||||
{
|
||||
double H = default;
|
||||
double S = default;
|
||||
double V = default;
|
||||
|
||||
var m = r;
|
||||
|
||||
if (g < m)
|
||||
{
|
||||
m = g;
|
||||
}
|
||||
|
||||
if (b < m)
|
||||
{
|
||||
m = b;
|
||||
}
|
||||
|
||||
var v = r;
|
||||
|
||||
if (g > v)
|
||||
{
|
||||
v = g;
|
||||
}
|
||||
|
||||
if (b > v)
|
||||
{
|
||||
v = b;
|
||||
}
|
||||
|
||||
var value = 100 * v / 255;
|
||||
var delta = v - m;
|
||||
|
||||
if (v == 0.0)
|
||||
{
|
||||
S = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
S = 100 * delta / v;
|
||||
}
|
||||
|
||||
if (S == 0)
|
||||
{
|
||||
H = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r == v)
|
||||
{
|
||||
H = 60.0 * (g - b) / delta;
|
||||
}
|
||||
else if (g == v)
|
||||
{
|
||||
H = 120.0 + 60.0 * (b - r) / delta;
|
||||
}
|
||||
else if (b == v)
|
||||
{
|
||||
H = 240.0 + 60.0 * (r - g) / delta;
|
||||
}
|
||||
|
||||
if (H < 0.0)
|
||||
{
|
||||
H = H + 360.0;
|
||||
}
|
||||
}
|
||||
|
||||
H = Math.Round(H);
|
||||
S = Math.Round(S);
|
||||
V = Math.Round(value);
|
||||
|
||||
return new HSV(H, S, V);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\build\Base.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="**\*.cs" Exclude="obj\**" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,12 +1,12 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:ThemeEditor.Controls;assembly=ThemeEditor"
|
||||
xmlns:converters="clr-namespace:ThemeEditor.Converters;assembly=ThemeEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="256" d:DesignWidth="256">
|
||||
<UserControl.Resources>
|
||||
<local:HueToColorConverter x:Key="HueToColorConverter"/>
|
||||
<converters:HueToColorConverter x:Key="HueToColorConverter"/>
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Slider.hue:vertical">
|
||||
|
|
|
@ -1,165 +1,8 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace ThemeEditor.Controls
|
||||
{
|
||||
public class HSV
|
||||
{
|
||||
public double H { get; set; }
|
||||
public double S { get; set; }
|
||||
public double V { get; set; }
|
||||
|
||||
public HSV()
|
||||
{
|
||||
}
|
||||
|
||||
public HSV(double h, double s, double v)
|
||||
{
|
||||
this.H = h;
|
||||
this.S = s;
|
||||
this.V = v;
|
||||
}
|
||||
|
||||
public HSV(HSV hs)
|
||||
{
|
||||
this.H = hs.H;
|
||||
this.S = hs.S;
|
||||
this.V = hs.V;
|
||||
}
|
||||
|
||||
public HSV(RGB rg)
|
||||
{
|
||||
HSV hs = rg.ToHSV();
|
||||
this.H = hs.H;
|
||||
this.S = hs.S;
|
||||
this.V = hs.V;
|
||||
}
|
||||
|
||||
public RGB ToRGB()
|
||||
{
|
||||
RGB rg = new RGB();
|
||||
HSV hsx = new HSV(this.H, this.S, this.V);
|
||||
|
||||
if (hsx.S == 0)
|
||||
{
|
||||
rg.R = rg.G = rg.B = Math.Round(hsx.V * 2.55); return (rg);
|
||||
}
|
||||
|
||||
hsx.S = hsx.S / 100;
|
||||
hsx.V = hsx.V / 100;
|
||||
hsx.H /= 60;
|
||||
|
||||
var i = Math.Floor(hsx.H);
|
||||
var f = hsx.H - i;
|
||||
var p = hsx.V * (1 - hsx.S);
|
||||
var q = hsx.V * (1 - hsx.S * f);
|
||||
var t = hsx.V * (1 - hsx.S * (1 - f));
|
||||
|
||||
switch ((int)i)
|
||||
{
|
||||
case 0: rg.R = hsx.V; rg.G = t; rg.B = p; break;
|
||||
case 1: rg.R = q; rg.G = hsx.V; rg.B = p; break;
|
||||
case 2: rg.R = p; rg.G = hsx.V; rg.B = t; break;
|
||||
case 3: rg.R = p; rg.G = q; rg.B = hsx.V; break;
|
||||
case 4: rg.R = t; rg.G = p; rg.B = hsx.V; break;
|
||||
default: rg.R = hsx.V; rg.G = p; rg.B = q; break;
|
||||
}
|
||||
|
||||
rg.R = Math.Round(rg.R * 255);
|
||||
rg.G = Math.Round(rg.G * 255);
|
||||
rg.B = Math.Round(rg.B * 255);
|
||||
|
||||
return rg;
|
||||
}
|
||||
}
|
||||
|
||||
public class RGB
|
||||
{
|
||||
public double R { get; set; }
|
||||
public double G { get; set; }
|
||||
public double B { get; set; }
|
||||
|
||||
public RGB()
|
||||
{
|
||||
}
|
||||
|
||||
public RGB(double r, double g, double b)
|
||||
{
|
||||
this.R = r;
|
||||
this.G = g;
|
||||
this.B = b;
|
||||
}
|
||||
|
||||
public RGB(RGB rg)
|
||||
{
|
||||
this.R = rg.R;
|
||||
this.G = rg.G;
|
||||
this.B = rg.B;
|
||||
}
|
||||
|
||||
public RGB(HSV hs)
|
||||
{
|
||||
RGB rg = hs.ToRGB();
|
||||
this.R = rg.R;
|
||||
this.G = rg.G;
|
||||
this.B = rg.B;
|
||||
}
|
||||
|
||||
public HSV ToHSV()
|
||||
{
|
||||
HSV hs = new HSV();
|
||||
RGB rg = new RGB(this.R, this.G, this.B);
|
||||
|
||||
var m = rg.R;
|
||||
if (rg.G < m) { m = rg.G; }
|
||||
if (rg.B < m) { m = rg.B; }
|
||||
var v = rg.R;
|
||||
if (rg.G > v) { v = rg.G; }
|
||||
if (rg.B > v) { v = rg.B; }
|
||||
var value = 100 * v / 255;
|
||||
var delta = v - m;
|
||||
if (v == 0.0) { hs.S = 0; } else { hs.S = 100 * delta / v; }
|
||||
|
||||
if (hs.S == 0) { hs.H = 0; }
|
||||
else
|
||||
{
|
||||
if (rg.R == v) { hs.H = 60.0 * (rg.G - rg.B) / delta; }
|
||||
else if (rg.G == v) { hs.H = 120.0 + 60.0 * (rg.B - rg.R) / delta; }
|
||||
else if (rg.B == v) { hs.H = 240.0 + 60.0 * (rg.R - rg.G) / delta; }
|
||||
if (hs.H < 0.0) { hs.H = hs.H + 360.0; }
|
||||
}
|
||||
|
||||
hs.H = Math.Round(hs.H);
|
||||
hs.S = Math.Round(hs.S);
|
||||
hs.V = Math.Round(value);
|
||||
|
||||
return hs;
|
||||
}
|
||||
}
|
||||
|
||||
public class HueToColorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is double hue && targetType == typeof(Color))
|
||||
{
|
||||
var rgb = new HSV(hue, 100, 100).ToRGB();
|
||||
var color = new Color(255, (byte)rgb.R, (byte)rgb.G, (byte)rgb.B);
|
||||
return color;
|
||||
}
|
||||
return AvaloniaProperty.UnsetValue;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class ColorPicker : UserControl
|
||||
{
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using ThemeEditor.Colors;
|
||||
|
||||
namespace ThemeEditor.Converters
|
||||
{
|
||||
public class HueToColorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is double hue && targetType == typeof(Color))
|
||||
{
|
||||
var rgb = new HSV(hue, 100, 100).ToRGB();
|
||||
var color = new Color(255, (byte)rgb.R, (byte)rgb.G, (byte)rgb.B);
|
||||
return color;
|
||||
}
|
||||
return AvaloniaProperty.UnsetValue;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
<Import Project="..\..\build\EmbedXaml.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThemeEditor.Colors\ThemeEditor.Colors.csproj" />
|
||||
<ProjectReference Include="..\ThemeEditor.ViewModels\ThemeEditor.ViewModels.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
using Xunit;
|
||||
|
||||
namespace ThemeEditor.Colors.UnitTests
|
||||
{
|
||||
public class HSVTests
|
||||
{
|
||||
[Fact]
|
||||
public void HSV_ToRGB()
|
||||
{
|
||||
var hsv = new HSV(213.0, 46.0, 49.0);
|
||||
var actual = hsv.ToRGB();
|
||||
Assert.Equal(67.0, actual.R);
|
||||
Assert.Equal(93.0, actual.G);
|
||||
Assert.Equal(125.0, actual.B);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using Xunit;
|
||||
|
||||
namespace ThemeEditor.Colors.UnitTests
|
||||
{
|
||||
public class RGBTests
|
||||
{
|
||||
[Fact]
|
||||
public void RGB_ToHSV()
|
||||
{
|
||||
var rgb = new RGB(67.0, 93.0, 125.0);
|
||||
var actual = rgb.ToHSV();
|
||||
Assert.Equal(213.0, actual.H);
|
||||
Assert.Equal(46.0, actual.S);
|
||||
Assert.Equal(49.0, actual.V);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\build\Rx.props" />
|
||||
<Import Project="..\..\build\XUnit.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\ThemeEditor.Colors\ThemeEditor.Colors.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Загрузка…
Ссылка в новой задаче