diff --git a/src/Aardvark.Base/Math/Colors/Color.cs b/src/Aardvark.Base/Math/Colors/Color.cs index de86b1bf..3b31efd1 100644 --- a/src/Aardvark.Base/Math/Colors/Color.cs +++ b/src/Aardvark.Base/Math/Colors/Color.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Text.RegularExpressions; namespace Aardvark.Base { @@ -1900,6 +1902,97 @@ public static C4f Average(this IEnumerable items) } #endregion + + #region Hex Parsing + + // See: https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color + private static readonly Regex regexHexSgl = new Regex("^(?:#|0x)?(?[0-9a-fA-F])(?[0-9a-fA-F])(?[0-9a-fA-F])(?[0-9a-fA-F])?$", RegexOptions.Compiled); + private static readonly Regex regexHexDbl = new Regex("^(?:#|0x)?(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})?$", RegexOptions.Compiled); + + private static bool TryParseHex(Regex regex, string input, out C4b result) + { + var m = regex.Match(input); + if (m.Success) + { + string[] values = new string[4]; + values[0] = m.Groups["R"].Value; + values[1] = m.Groups["G"].Value; + values[2] = m.Groups["B"].Value; + values[3] = m.Groups["A"].Success ? m.Groups["A"].Value : "FF"; + + result = + new C4b(values.Map((x) => { + var value = x.Length == 1 ? new string(x[0], 2) : x; + return byte.Parse(value, NumberStyles.AllowHexSpecifier); + })); + + return true; + } + + result = C4b.Zero; + return false; + } + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseHex(Text input, out C4b result) + { + var str = input.WhiteSpaceTrimmed.ToString(); + return TryParseHex(regexHexDbl, str, out result) || TryParseHex(regexHexSgl, str, out result); + } + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseHex(string input, out C4b result) + => TryParseHex(new Text(input), out result); + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid hexadecimal color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4b ParseHex(Text input) + => TryParseHex(input, out C4b result) ? result : throw new FormatException($"{input} is not a valid hexadecimal color."); + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid hexadecimal color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4b ParseHex(string input) + => ParseHex(new Text(input)); + + #endregion } #endregion diff --git a/src/Aardvark.Base/Math/Colors/Color_auto.cs b/src/Aardvark.Base/Math/Colors/Color_auto.cs index b548495e..5ce6fdb1 100644 --- a/src/Aardvark.Base/Math/Colors/Color_auto.cs +++ b/src/Aardvark.Base/Math/Colors/Color_auto.cs @@ -1594,30 +1594,102 @@ public static C3b DivideByInt(C3b c, int x) #region Parsing - public static C3b Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3b.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3b result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3b(); + return true; + } + else + { + bool success = true; + byte[] values = new byte[4] { 255, 255, 255, 255 }; + + byte parse(Text t) + { + if (!byte.TryParse(t.ToString(), out byte value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3b(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3b result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3b Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3b Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3b( - byte.Parse(x[0], CultureInfo.InvariantCulture), - byte.Parse(x[1], CultureInfo.InvariantCulture), - byte.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3b Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3b.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3b.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3b Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3b.Setter); - } + => TryParse(t, out C3b result) ? result : throw new FormatException($"{t} is not a valid C3b color."); #endregion @@ -1724,6 +1796,17 @@ public static bool IsTiny(this C3b c, byte epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3b c) + => $"{c.R:X2}{c.G:X2}{c.B:X2}"; + + #endregion + #region Comparisons /// @@ -3786,30 +3869,102 @@ public static C3us DivideByInt(C3us c, int x) #region Parsing - public static C3us Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3us.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3us result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3us(); + return true; + } + else + { + bool success = true; + ushort[] values = new ushort[4] { 65535, 65535, 65535, 65535 }; + + ushort parse(Text t) + { + if (!ushort.TryParse(t.ToString(), out ushort value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3us(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3us.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3us result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3us Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3us Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3us( - ushort.Parse(x[0], CultureInfo.InvariantCulture), - ushort.Parse(x[1], CultureInfo.InvariantCulture), - ushort.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3us Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3us.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3us.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3us Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3us.Setter); - } + => TryParse(t, out C3us result) ? result : throw new FormatException($"{t} is not a valid C3us color."); #endregion @@ -3916,6 +4071,17 @@ public static bool IsTiny(this C3us c, ushort epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3us c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -5901,30 +6067,102 @@ public static C3ui DivideByInt(C3ui c, int x) #region Parsing - public static C3ui Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3ui.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3ui result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3ui(); + return true; + } + else + { + bool success = true; + uint[] values = new uint[4] { UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue }; + + uint parse(Text t) + { + if (!uint.TryParse(t.ToString(), out uint value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3ui(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3ui.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3ui result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3ui Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3ui Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3ui( - uint.Parse(x[0], CultureInfo.InvariantCulture), - uint.Parse(x[1], CultureInfo.InvariantCulture), - uint.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3ui Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3ui.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3ui.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3ui Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3ui.Setter); - } + => TryParse(t, out C3ui result) ? result : throw new FormatException($"{t} is not a valid C3ui color."); #endregion @@ -6031,6 +6269,17 @@ public static bool IsTiny(this C3ui c, uint epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3ui c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -7891,30 +8140,102 @@ public static C3f DivideByInt(C3f c, int x) #region Parsing - public static C3f Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3f.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3f result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3f(); + return true; + } + else + { + bool success = true; + float[] values = new float[4] { 1.0f, 1.0f, 1.0f, 1.0f }; + + float parse(Text t) + { + if (!float.TryParse(t.ToString(), out float value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3f(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3f.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3f result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3f Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3f Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3f( - float.Parse(x[0], CultureInfo.InvariantCulture), - float.Parse(x[1], CultureInfo.InvariantCulture), - float.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3f Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3f.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3f.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3f Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3f.Setter); - } + => TryParse(t, out C3f result) ? result : throw new FormatException($"{t} is not a valid C3f color."); #endregion @@ -8064,6 +8385,17 @@ public static bool IsFinite(C3f c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3f c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -9918,30 +10250,102 @@ public static C3d DivideByInt(C3d c, int x) #region Parsing - public static C3d Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3d.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3d result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3d(); + return true; + } + else + { + bool success = true; + double[] values = new double[4] { 1.0, 1.0, 1.0, 1.0 }; + + double parse(Text t) + { + if (!double.TryParse(t.ToString(), out double value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3d(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3d.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3d result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3d Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3d Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3d( - double.Parse(x[0], CultureInfo.InvariantCulture), - double.Parse(x[1], CultureInfo.InvariantCulture), - double.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3d Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3d.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3d.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3d Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3d.Setter); - } + => TryParse(t, out C3d result) ? result : throw new FormatException($"{t} is not a valid C3d color."); #endregion @@ -10091,6 +10495,17 @@ public static bool IsFinite(C3d c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3d c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -12466,31 +12881,101 @@ public static C4b DivideByInt(C4b c, int x) #region Parsing - public static C4b Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4b result) { - return Parse(s); + if (Col.TryParseHex(t, out result)) + { + return true; + } + else + { + bool success = true; + byte[] values = new byte[4] { 255, 255, 255, 255 }; + + byte parse(Text t) + { + if (!byte.TryParse(t.ToString(), out byte value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4b(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4b result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4b Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4b Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4b( - byte.Parse(x[0], CultureInfo.InvariantCulture), - byte.Parse(x[1], CultureInfo.InvariantCulture), - byte.Parse(x[2], CultureInfo.InvariantCulture), - byte.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4b Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4b.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4b.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4b Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4b.Setter); - } + => TryParse(t, out C4b result) ? result : throw new FormatException($"{t} is not a valid C4b color."); #endregion @@ -12608,6 +13093,17 @@ public static bool IsTiny(this C4b c, byte epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4b c) + => $"{c.R:X2}{c.G:X2}{c.B:X2}{c.A:X2}"; + + #endregion + #region Comparisons /// @@ -14943,31 +15439,102 @@ public static C4us DivideByInt(C4us c, int x) #region Parsing - public static C4us Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4us.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4us result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4us(); + return true; + } + else + { + bool success = true; + ushort[] values = new ushort[4] { 65535, 65535, 65535, 65535 }; + + ushort parse(Text t) + { + if (!ushort.TryParse(t.ToString(), out ushort value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4us(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4us.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4us result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4us Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4us Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4us( - ushort.Parse(x[0], CultureInfo.InvariantCulture), - ushort.Parse(x[1], CultureInfo.InvariantCulture), - ushort.Parse(x[2], CultureInfo.InvariantCulture), - ushort.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4us Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4us.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4us.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4us Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4us.Setter); - } + => TryParse(t, out C4us result) ? result : throw new FormatException($"{t} is not a valid C4us color."); #endregion @@ -15085,6 +15652,17 @@ public static bool IsTiny(this C4us c, ushort epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4us c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// @@ -17325,31 +17903,102 @@ public static C4ui DivideByInt(C4ui c, int x) #region Parsing - public static C4ui Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4ui.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4ui result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4ui(); + return true; + } + else + { + bool success = true; + uint[] values = new uint[4] { UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue }; + + uint parse(Text t) + { + if (!uint.TryParse(t.ToString(), out uint value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4ui(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4ui.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4ui result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4ui Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4ui Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4ui( - uint.Parse(x[0], CultureInfo.InvariantCulture), - uint.Parse(x[1], CultureInfo.InvariantCulture), - uint.Parse(x[2], CultureInfo.InvariantCulture), - uint.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4ui Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4ui.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4ui.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4ui Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4ui.Setter); - } + => TryParse(t, out C4ui result) ? result : throw new FormatException($"{t} is not a valid C4ui color."); #endregion @@ -17467,6 +18116,17 @@ public static bool IsTiny(this C4ui c, uint epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4ui c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// @@ -19497,31 +20157,102 @@ public static C4f DivideByInt(C4f c, int x) #region Parsing - public static C4f Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4f.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4f result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4f(); + return true; + } + else + { + bool success = true; + float[] values = new float[4] { 1.0f, 1.0f, 1.0f, 1.0f }; + + float parse(Text t) + { + if (!float.TryParse(t.ToString(), out float value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4f(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4f.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4f result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4f Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4f Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4f( - float.Parse(x[0], CultureInfo.InvariantCulture), - float.Parse(x[1], CultureInfo.InvariantCulture), - float.Parse(x[2], CultureInfo.InvariantCulture), - float.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4f Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4f.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4f.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4f Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4f.Setter); - } + => TryParse(t, out C4f result) ? result : throw new FormatException($"{t} is not a valid C4f color."); #endregion @@ -19682,6 +20413,17 @@ public static bool IsFinite(C4f c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4f c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// @@ -21709,31 +22451,102 @@ public static C4d DivideByInt(C4d c, int x) #region Parsing - public static C4d Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4d.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4d result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4d(); + return true; + } + else + { + bool success = true; + double[] values = new double[4] { 1.0, 1.0, 1.0, 1.0 }; + + double parse(Text t) + { + if (!double.TryParse(t.ToString(), out double value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4d(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4d.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4d result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4d Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4d Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4d( - double.Parse(x[0], CultureInfo.InvariantCulture), - double.Parse(x[1], CultureInfo.InvariantCulture), - double.Parse(x[2], CultureInfo.InvariantCulture), - double.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4d Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4d.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4d.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4d Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4d.Setter); - } + => TryParse(t, out C4d result) ? result : throw new FormatException($"{t} is not a valid C4d color."); #endregion @@ -21894,6 +22707,17 @@ public static bool IsFinite(C4d c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4d c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// diff --git a/src/Aardvark.Base/Math/Colors/Color_template.cs b/src/Aardvark.Base/Math/Colors/Color_template.cs index 1bf7ca2f..8d7182b0 100644 --- a/src/Aardvark.Base/Math/Colors/Color_template.cs +++ b/src/Aardvark.Base/Math/Colors/Color_template.cs @@ -1133,28 +1133,109 @@ public static __type__ DivideByInt(__type__ c, int x) #region Parsing - public static __type__ Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, __type__.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out __type__ result) { - return Parse(s); + //# if (type == "C4b") { + if (Col.TryParseHex(t, out result)) + { + return true; + } + //# } else { + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.To__type__(); + return true; + } + //# } + else + { + bool success = true; + __ftype__[] values = new __ftype__[4] { /*# 4.ForEach(p => { */__t.MaxValue__/*# }, comma);*/ }; + + __ftype__ parse(Text t) + { + if (!__ftype__.TryParse(t.ToString(), out __ftype__ value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new __type__(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, __type__.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out __type__ result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static __type__ Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid __type__ color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static __type__ Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new __type__(/*# t.Len.ForEach(p => { */ - __ftype__.Parse(x[__p__], CultureInfo.InvariantCulture)/*# }, comma); */ - ); - } + => Parse(new Text(s)); + [Obsolete("Weird overload with level, call NestedBracketSplit() manually instead.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static __type__ Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text<__ftype__>.Parse, __type__.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text<__ftype__>.Parse, __type__.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid __type__ color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static __type__ Parse(Text t) - { - return t.NestedBracketSplit(1, Text<__ftype__>.Parse, __type__.Setter); - } + => TryParse(t, out __type__ result) ? result : throw new FormatException($"{t} is not a valid __type__ color."); #endregion @@ -1336,6 +1417,21 @@ public static bool IsFinite(__type__ c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB/*# if (t.HasAlpha) {*/AA/*#}*/. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this __type__ c) + //# if (ft == Meta.ByteType) { + => /*# if (t.HasAlpha) {*/$"{c.R:X2}{c.G:X2}{c.B:X2}{c.A:X2}"/*# } else {*/$"{c.R:X2}{c.G:X2}{c.B:X2}"/*#}*/; + //# } else { + => c.ToC__t.Len__b().ToHexString(); + //# } + + #endregion + #region Comparisons //# var bops = new[,] { { "<", "Smaller" }, { ">" , "Greater"}, diff --git a/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs b/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs index 25eb4616..bd95b80c 100644 --- a/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs +++ b/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs @@ -171,5 +171,79 @@ public void ScalarOperators() Assert.AreEqual(new C4b(scalar, scalar, scalar, scalar) / color, scalar / color); } } + + [Test] + public void Parse() + { + var rnd = new RandomSystem(0); + + for (int i = 0; i < 100; i++) + { + var c = rnd.UniformC4ui(); + var s1 = $"[{c.R}, {c.G}, {c.B}, {c.A}]"; + var s2 = $"[{c.R}, {c.G}, {c.B}]"; + + Assert.AreEqual(c.RGB, C3ui.Parse(s1)); + Assert.AreEqual(c, C4ui.Parse(s1)); + Assert.AreEqual(c.RGB, C3ui.Parse(s2)); + Assert.AreEqual(new C4ui(c.RGB, uint.MaxValue), C4ui.Parse(s2)); + } + } + + [Test] + public void ParseTooManyComponents() + { + var t1 = new Text("[1,2,3"); + var c1 = t1.NestedBracketSplitCount2(1); + var r1 = t1.NestedBracketSplit(1).ToArray(); + + var t2 = new Text("[42]"); + var c2 = t2.NestedBracketSplitCount2(1); + var r2 = t2.NestedBracketSplit(1).ToArray(); + + var t3 = new Text("[42,43]"); + var c3 = t3.NestedBracketSplitCount2(1); + var r3 = t3.NestedBracketSplit(1).ToArray(); + + Assert.AreEqual(r1.Length, c1); + Assert.AreEqual(r2.Length, c2); + Assert.AreEqual(r3.Length, c3); + + Assert.IsFalse(C4d.TryParse("[1, 2, 3, 4, 5]", out C4d _)); + } + + [Test] + public void ParseHex() + { + var rnd = new RandomSystem(1); + + for (int i = 0; i < 100; i++) + { + var color = rnd.UniformC4d().ToC4b(); + var str = color.ToHexString(); + + Assert.IsTrue(Col.TryParseHex(str, out C4b result)); + Assert.IsTrue(C4b.TryParse(str, out C4b result2)); + Assert.AreEqual(color, result); + Assert.AreEqual(result, result2); + } + } + + [Test] + public void ParseHexSingleDigit() + { + var rnd = new RandomSystem(1); + + for (int i = 0; i < 100; i++) + { + var str = rnd.UniformC4d().ToHexString(); + var dbl = $"#{str[0]}{str[0]}{str[2]}{str[2]}{str[4]}{str[4]}"; + var sgl = $"0x{str[0]}{str[2]}{str[4]}F"; + + Assert.IsTrue(Col.TryParseHex(dbl, out C4b a)); + Assert.IsTrue(C4b.TryParse(sgl, out C4b b)); + Assert.AreEqual(a, b); + } + } } }