From 12e7e6b9c1854d8fa5238fdb0113c08e9e1903b7 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 9 Mar 2023 15:14:16 -0800 Subject: [PATCH] add Color.FromHSV --- src/Graphics/Color.cs | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/Graphics/Color.cs b/src/Graphics/Color.cs index b01ff26c..bf535cf4 100644 --- a/src/Graphics/Color.cs +++ b/src/Graphics/Color.cs @@ -1758,6 +1758,67 @@ namespace MoonWorks.Graphics ); } + // Modified from one of the responses here: + // https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both/6930407#6930407 + public static Color FromHSV(float r, float g, float b) + { + r = (100 + r) % 1f; + + float hueSlice = 6 * r; // [0, 6) + float hueSliceInteger = MathF.Floor(hueSlice); + + // In [0,1) for each hue slice + float hueSliceInterpolant = hueSlice - hueSliceInteger; + + Vector3 tempRGB = new Vector3( + b * (1f - g), + b * (1f - g * hueSliceInterpolant), + b * (1f - g * (1f - hueSliceInterpolant)) + ); + + // The idea here to avoid conditions is to notice that the conversion code can be rewritten: + // if ( var_i == 0 ) { R = V ; G = TempRGB.z ; B = TempRGB.x } + // else if ( var_i == 2 ) { R = TempRGB.x ; G = V ; B = TempRGB.z } + // else if ( var_i == 4 ) { R = TempRGB.z ; G = TempRGB.x ; B = V } + // + // else if ( var_i == 1 ) { R = TempRGB.y ; G = V ; B = TempRGB.x } + // else if ( var_i == 3 ) { R = TempRGB.x ; G = TempRGB.y ; B = V } + // else if ( var_i == 5 ) { R = V ; G = TempRGB.x ; B = TempRGB.y } + // + // This shows several things: + // . A separation between even and odd slices + // . If slices (0,2,4) and (1,3,5) can be rewritten as basically being slices (0,1,2) then + // the operation simply amounts to performing a "rotate right" on the RGB components + // . The base value to rotate is either (V, B, R) for even slices or (G, V, R) for odd slices + // + float isOddSlice = hueSliceInteger % 2f; // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5) + float threeSliceSelector = 0.5f * (hueSliceInteger - isOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5) + + Vector3 scrollingRGBForEvenSlices = new Vector3(b, tempRGB.Z, tempRGB.X); // (V, Temp Blue, Temp Red) for even slices (0, 2, 4) + Vector3 scrollingRGBForOddSlices = new Vector3(tempRGB.Y, b, tempRGB.X); // (Temp Green, V, Temp Red) for odd slices (1, 3, 5) + Vector3 scrollingRGB = Vector3.Lerp(scrollingRGBForEvenSlices, scrollingRGBForOddSlices, isOddSlice); + + float IsNotFirstSlice = MathHelper.Clamp(threeSliceSelector, 0f, 1f); // 1 if NOT the first slice (true for slices 1 and 2) + float IsNotSecondSlice = MathHelper.Clamp(threeSliceSelector - 1f, 0f, 1f); // 1 if NOT the first or second slice (true only for slice 2) + + Vector3 color = Vector3.Lerp( + scrollingRGB, + Vector3.Lerp( + new Vector3(scrollingRGB.Z, scrollingRGB.X, scrollingRGB.Y), + new Vector3(scrollingRGB.Y, scrollingRGB.Z, scrollingRGB.X), + IsNotSecondSlice + ), + IsNotFirstSlice + ); + + return new Color(color); + } + + public static Color FromHSV(int r, int g, int b) + { + return Color.FromHSV(r / 255f, g / 255f, b / 255f); + } + #endregion #region Public Static Operators and Override Methods