Skip to content

Commit 7255a5e

Browse files
committed
fix: remove PixiEditor.ColorPicker
- Microsoft.Xaml.Behaviors.Wpf cause conflict within other plugin - add ColorPicker from RevitLookup.UI.Framework
1 parent dda68b8 commit 7255a5e

13 files changed

+1314
-215
lines changed
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Copyright (c) Lookup Foundation and Contributors
2+
//
3+
// Permission to use, copy, modify, and distribute this software in
4+
// object code form for any purpose and without fee is hereby granted,
5+
// provided that the above copyright notice appears in all copies and
6+
// that both that copyright notice and the limited warranty and
7+
// restricted rights notice below appear in all supporting
8+
// documentation.
9+
//
10+
// THIS PROGRAM IS PROVIDED "AS IS" AND WITH ALL FAULTS.
11+
// NO IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE IS PROVIDED.
12+
// THERE IS NO GUARANTEE THAT THE OPERATION OF THE PROGRAM WILL BE
13+
// UNINTERRUPTED OR ERROR FREE.
14+
15+
using Color = System.Drawing.Color ;
16+
17+
namespace RevitDevTool.Controls;
18+
19+
/// <summary>
20+
/// Helper class to easier work with color formats
21+
/// </summary>
22+
/// <remarks>
23+
/// Implementation: https://github.com/microsoft/PowerToys/blob/main/src/common/ManagedCommon/ColorFormatHelper.cs
24+
/// </remarks>
25+
public static class ColorFormatUtils
26+
{
27+
/// <summary>
28+
/// Return a drawing color of a given <see cref="System.Windows.Media.Color"/>
29+
/// </summary>
30+
public static Color GetDrawingColor(this System.Windows.Media.Color color)
31+
{
32+
return Color.FromArgb(1, color.R, color.G, color.B);
33+
}
34+
35+
/// <summary>
36+
/// Convert a given <see cref="Color"/> to a CMYK color (cyan, magenta, yellow, black key)
37+
/// </summary>
38+
/// <param name="color">The <see cref="Color"/> to convert</param>
39+
/// <returns>The cyan[0..1], magenta[0..1], yellow[0..1] and black key[0..1] of the converted color</returns>
40+
public static (double Cyan, double Magenta, double Yellow, double BlackKey) ConvertToCmykColor(Color color)
41+
{
42+
// special case for black (avoid division by zero)
43+
if (color is {R: 0, G: 0, B: 0})
44+
{
45+
return (0d, 0d, 0d, 1d);
46+
}
47+
48+
var red = color.R / 255d;
49+
var green = color.G / 255d;
50+
var blue = color.B / 255d;
51+
52+
var blackKey = 1d - Math.Max(Math.Max(red, green), blue);
53+
54+
// special case for black (avoid division by zero)
55+
if (1d - blackKey == 0d)
56+
{
57+
return (0d, 0d, 0d, 1d);
58+
}
59+
60+
var cyan = (1d - red - blackKey) / (1d - blackKey);
61+
var magenta = (1d - green - blackKey) / (1d - blackKey);
62+
var yellow = (1d - blue - blackKey) / (1d - blackKey);
63+
64+
return (cyan, magenta, yellow, blackKey);
65+
}
66+
67+
/// <summary>
68+
/// Convert a given <see cref="Color"/> to a HSB color (hue, saturation, brightness)
69+
/// </summary>
70+
/// <param name="color">The <see cref="Color"/> to convert</param>
71+
/// <returns>The hue [0°..360°], saturation [0..1] and brightness [0..1] of the converted color</returns>
72+
public static (double Hue, double Saturation, double Brightness) ConvertToHsbColor(Color color)
73+
{
74+
// HSB and HSV represents the same color space
75+
return ConvertToHsvColor(color);
76+
}
77+
78+
/// <summary>
79+
/// Convert a given <see cref="Color"/> to a HSV color (hue, saturation, value)
80+
/// </summary>
81+
/// <param name="color">The <see cref="Color"/> to convert</param>
82+
/// <returns>The hue [0°..360°], saturation [0..1] and value [0..1] of the converted color</returns>
83+
public static (double Hue, double Saturation, double Value) ConvertToHsvColor(Color color)
84+
{
85+
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
86+
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
87+
88+
return (color.GetHue(), max == 0d ? 0d : (max - min) / max, max);
89+
}
90+
91+
/// <summary>
92+
/// Convert a given <see cref="Color"/> to a HSI color (hue, saturation, intensity)
93+
/// </summary>
94+
/// <param name="color">The <see cref="Color"/> to convert</param>
95+
/// <returns>The hue [0°..360°], saturation [0..1] and intensity [0..1] of the converted color</returns>
96+
public static (double Hue, double Saturation, double Intensity) ConvertToHsiColor(Color color)
97+
{
98+
// special case for black
99+
if (color.R == 0 && color.G == 0 && color.B == 0)
100+
{
101+
return (0d, 0d, 0d);
102+
}
103+
104+
var red = color.R / 255d;
105+
var green = color.G / 255d;
106+
var blue = color.B / 255d;
107+
108+
var intensity = (red + green + blue) / 3d;
109+
110+
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
111+
112+
return (color.GetHue(), 1d - (min / intensity), intensity);
113+
}
114+
115+
/// <summary>
116+
/// Convert a given <see cref="Color"/> to a HSL color (hue, saturation, lightness)
117+
/// </summary>
118+
/// <param name="color">The <see cref="Color"/> to convert</param>
119+
/// <returns>The hue [0°..360°], saturation [0..1] and lightness [0..1] values of the converted color</returns>
120+
public static (double Hue, double Saturation, double Lightness) ConvertToHslColor(Color color)
121+
{
122+
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
123+
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
124+
125+
var lightness = (max + min) / 2d;
126+
127+
if (lightness == 0d || Math.Abs(min - max) < 1e-9)
128+
{
129+
return (color.GetHue(), 0d, lightness);
130+
}
131+
132+
if (lightness is > 0d and <= 0.5d)
133+
{
134+
return (color.GetHue(), (max - min) / (max + min), lightness);
135+
}
136+
137+
return (color.GetHue(), (max - min) / (2d - (max + min)), lightness);
138+
}
139+
140+
/// <summary>
141+
/// Convert a given <see cref="Color"/> to a HWB color (hue, whiteness, blackness)
142+
/// </summary>
143+
/// <param name="color">The <see cref="Color"/> to convert</param>
144+
/// <returns>The hue [0°..360°], whiteness [0..1] and blackness [0..1] of the converted color</returns>
145+
public static (double Hue, double Whiteness, double Blackness) ConvertToHwbColor(Color color)
146+
{
147+
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
148+
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
149+
150+
return (color.GetHue(), min, 1 - max);
151+
}
152+
153+
/// <summary>
154+
/// Convert a given <see cref="Color"/> to a CIE LAB color (LAB)
155+
/// </summary>
156+
/// <param name="color">The <see cref="Color"/> to convert</param>
157+
/// <returns>The lightness [0..100] and two chromaticities [-128..127]</returns>
158+
public static (double Lightness, double ChromaticityA, double ChromaticityB) ConvertToCielabColor(Color color)
159+
{
160+
var xyz = ConvertToCiexyzColor(color);
161+
var lab = GetCielabColorFromCieXyz(xyz.X, xyz.Y, xyz.Z);
162+
163+
return lab;
164+
}
165+
166+
/// <summary>
167+
/// Convert a given <see cref="Color"/> to a CIE XYZ color (XYZ)
168+
/// The constants of the formula matches this Wikipedia page, but at a higher precision:
169+
/// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ)
170+
/// This page provides a method to calculate the constants:
171+
/// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
172+
/// </summary>
173+
/// <param name="color">The <see cref="Color"/> to convert</param>
174+
/// <returns>The X [0..1], Y [0..1] and Z [0..1]</returns>
175+
public static (double X, double Y, double Z) ConvertToCiexyzColor(Color color)
176+
{
177+
var r = color.R / 255d;
178+
var g = color.G / 255d;
179+
var b = color.B / 255d;
180+
181+
// inverse companding, gamma correction must be undone
182+
var rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
183+
var gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
184+
var bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
185+
186+
return (
187+
(rLinear * 0.41239079926595948) + (gLinear * 0.35758433938387796) + (bLinear * 0.18048078840183429),
188+
(rLinear * 0.21263900587151036) + (gLinear * 0.71516867876775593) + (bLinear * 0.07219231536073372),
189+
(rLinear * 0.01933081871559185) + (gLinear * 0.11919477979462599) + (bLinear * 0.95053215224966058)
190+
);
191+
}
192+
193+
/// <summary>
194+
/// Convert a CIE XYZ color <see cref="double"/> to a CIE LAB color (LAB) adapted to sRGB D65 white point
195+
/// The constants of the formula used come from this wikipedia page:
196+
/// https://en.wikipedia.org/wiki/CIELAB_color_space#Converting_between_CIELAB_and_CIEXYZ_coordinates
197+
/// </summary>
198+
/// <param name="x">The <see cref="x"/> represents a mix of the three CIE RGB curves</param>
199+
/// <param name="y">The <see cref="y"/> represents the luminance</param>
200+
/// <param name="z">The <see cref="z"/> is quasi-equal to blue (of CIE RGB)</param>
201+
/// <returns>The lightness [0..100] and two chromaticities [-128..127]</returns>
202+
private static (double Lightness, double ChromaticityA, double ChromaticityB) GetCielabColorFromCieXyz(double x, double y, double z)
203+
{
204+
// sRGB reference white (x=0.3127, y=0.3290, Y=1.0), actually CIE Standard Illuminant D65 truncated to 4 decimal places,
205+
// then converted to XYZ using the formula:
206+
// X = x * (Y / y)
207+
// Y = Y
208+
// Z = (1 - x - y) * (Y / y)
209+
const double xN = 0.9504559270516717;
210+
const double yN = 1.0;
211+
const double zN = 1.0890577507598784;
212+
213+
// Scale XYZ values relative to reference white
214+
x /= xN;
215+
y /= yN;
216+
z /= zN;
217+
218+
// XYZ to CIELab transformation
219+
const double delta = 6d / 29;
220+
var m = (1d / 3) * Math.Pow(delta, -2);
221+
var t = Math.Pow(delta, 3);
222+
223+
var fx = (x > t) ? Math.Pow(x, 1.0 / 3.0) : (x * m) + (16.0 / 116.0);
224+
var fy = (y > t) ? Math.Pow(y, 1.0 / 3.0) : (y * m) + (16.0 / 116.0);
225+
var fz = (z > t) ? Math.Pow(z, 1.0 / 3.0) : (z * m) + (16.0 / 116.0);
226+
227+
var l = (116 * fy) - 16;
228+
var a = 500 * (fx - fy);
229+
var b = 200 * (fy - fz);
230+
231+
return (l, a, b);
232+
}
233+
234+
/// <summary>
235+
/// Convert a given <see cref="Color"/> to a natural color (hue, whiteness, blackness)
236+
/// </summary>
237+
/// <param name="color">The <see cref="Color"/> to convert</param>
238+
/// <returns>The hue, whiteness [0..1] and blackness [0..1] of the converted color</returns>
239+
public static (string Hue, double Whiteness, double Blackness) ConvertToNaturalColor(Color color)
240+
{
241+
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
242+
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
243+
244+
return (GetNaturalColorFromHue(color.GetHue()), min, 1 - max);
245+
}
246+
247+
/// <summary>
248+
/// Return the natural color for the given hue value
249+
/// </summary>
250+
/// <param name="hue">The hue value to convert</param>
251+
/// <returns>A natural color</returns>
252+
private static string GetNaturalColorFromHue(double hue)
253+
{
254+
return hue switch
255+
{
256+
< 60d => $"R{Math.Round(hue / 0.6d, 0)}",
257+
< 120d => $"Y{Math.Round((hue - 60d) / 0.6d, 0)}",
258+
< 180d => $"G{Math.Round((hue - 120d) / 0.6d, 0)}",
259+
< 240d => $"C{Math.Round((hue - 180d) / 0.6d, 0)}",
260+
< 300d => $"B{Math.Round((hue - 240d) / 0.6d, 0)}",
261+
_ => $"M{Math.Round((hue - 300d) / 0.6d, 0)}"
262+
};
263+
}
264+
}

0 commit comments

Comments
 (0)