public static Hsv FindNextNamedColor()

in src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs [409:611]


        public static Hsv FindNextNamedColor(
            Hsv originalHsv,
            HsvComponent component,
            IncrementDirection direction,
            bool shouldWrap,
            double minBound,
            double maxBound)
        {
            // There's no easy way to directly get the next named color, so what we'll do
            // is just iterate in the direction that we want to find it until we find a color
            // in that direction that has a color name different than our current color name.
            // Once we find a new color name, then we'll iterate across that color name until
            // we find its bounds on the other side, and then select the color that is exactly
            // in the middle of that color's bounds.
            Hsv newHsv = originalHsv;
            
            string originalColorName = ColorHelper.ToDisplayName(originalHsv.ToRgb().ToColor());
            string newColorName = originalColorName;

            // Note: *newValue replaced with ref local variable for C#, must be initialized
            double originalValue = 0.0;
            ref double newValue = ref newHsv.H;
            double incrementAmount = 0.0;

            switch (component)
            {
                case HsvComponent.Hue:
                    originalValue = originalHsv.H;
                    newValue = ref newHsv.H;
                    incrementAmount = 1;
                    break;

                case HsvComponent.Saturation:
                    originalValue = originalHsv.S;
                    newValue = ref newHsv.S;
                    incrementAmount = 0.01;
                    break;

                case HsvComponent.Value:
                    originalValue = originalHsv.V;
                    newValue = ref newHsv.V;
                    incrementAmount = 0.01;
                    break;

                default:
                    throw new InvalidOperationException("Invalid HsvComponent.");
            }

            bool shouldFindMidPoint = true;

            while (newColorName == originalColorName)
            {
                double previousValue = newValue;
                newValue += (direction == IncrementDirection.Lower ? -1 : 1) * incrementAmount;

                bool justWrapped = false;

                // If we've hit a boundary, then either we should wrap or we shouldn't.
                // If we should, then we'll perform that wrapping if we were previously up against
                // the boundary that we've now hit.  Otherwise, we'll stop at that boundary.
                if (newValue > maxBound)
                {
                    if (shouldWrap)
                    {
                        newValue = minBound;
                        justWrapped = true;
                    }
                    else
                    {
                        newValue = maxBound;
                        shouldFindMidPoint = false;
                        newColorName = ColorHelper.ToDisplayName(newHsv.ToRgb().ToColor());
                        break;
                    }
                }
                else if (newValue < minBound)
                {
                    if (shouldWrap)
                    {
                        newValue = maxBound;
                        justWrapped = true;
                    }
                    else
                    {
                        newValue = minBound;
                        shouldFindMidPoint = false;
                        newColorName = ColorHelper.ToDisplayName(newHsv.ToRgb().ToColor());
                        break;
                    }
                }

                if (!justWrapped &&
                    previousValue != originalValue &&
                    Math.Sign(newValue - originalValue) != Math.Sign(previousValue - originalValue))
                {
                    // If we've wrapped all the way back to the start and have failed to find a new color name,
                    // then we'll just quit - there isn't a new color name that we're going to find.
                    shouldFindMidPoint = false;
                    break;
                }

                newColorName = ColorHelper.ToDisplayName(newHsv.ToRgb().ToColor());
            }

            if (shouldFindMidPoint)
            {
                Hsv startHsv = newHsv;
                Hsv currentHsv = startHsv;
                double startEndOffset = 0;
                string currentColorName = newColorName;

                // Note: *startValue/*currentValue replaced with ref local variables for C#, must be initialized
                ref double startValue = ref startHsv.H;
                ref double currentValue = ref currentHsv.H;
                double wrapIncrement = 0;

                switch (component)
                {
                    case HsvComponent.Hue:
                        startValue = ref startHsv.H;
                        currentValue = ref currentHsv.H;
                        wrapIncrement = 360.0;
                        break;

                    case HsvComponent.Saturation:
                        startValue = ref startHsv.S;
                        currentValue = ref currentHsv.S;
                        wrapIncrement = 1.0;
                        break;

                    case HsvComponent.Value:
                        startValue = ref startHsv.V;
                        currentValue = ref currentHsv.V;
                        wrapIncrement = 1.0;
                        break;

                    default:
                        throw new InvalidOperationException("Invalid HsvComponent.");
                }

                while (newColorName == currentColorName)
                {
                    currentValue += (direction == IncrementDirection.Lower ? -1 : 1) * incrementAmount;

                    // If we've hit a boundary, then either we should wrap or we shouldn't.
                    // If we should, then we'll perform that wrapping if we were previously up against
                    // the boundary that we've now hit.  Otherwise, we'll stop at that boundary.
                    if (currentValue > maxBound)
                    {
                        if (shouldWrap)
                        {
                            currentValue = minBound;
                            startEndOffset = maxBound - minBound;
                        }
                        else
                        {
                            currentValue = maxBound;
                            break;
                        }
                    }
                    else if (currentValue < minBound)
                    {
                        if (shouldWrap)
                        {
                            currentValue = maxBound;
                            startEndOffset = minBound - maxBound;
                        }
                        else
                        {
                            currentValue = minBound;
                            break;
                        }
                    }

                    currentColorName = ColorHelper.ToDisplayName(currentHsv.ToRgb().ToColor());
                }

                newValue = (startValue + currentValue + startEndOffset) / 2;

                // Dividing by 2 may have gotten us halfway through a single step, so we'll
                // remove that half-step if it exists.
                double leftoverValue = Math.Abs(newValue);

                while (leftoverValue > incrementAmount)
                {
                    leftoverValue -= incrementAmount;
                }

                newValue -= leftoverValue;

                while (newValue < minBound)
                {
                    newValue += wrapIncrement;
                }

                while (newValue > maxBound)
                {
                    newValue -= wrapIncrement;
                }
            }

            return newHsv;
        }