using System.Globalization; using System.Text; using Cysharp.Text; using X10D.Linq; using X10D.Text; /// /// Represents a set of coordinates. /// public readonly struct Coordinates { /// /// Initializes a new instance of the struct. /// /// The X coordinate. /// The Y coordinate. /// The Z coordinate. /// The yaw. /// /// if these coordinates represent relative coordinates; otherwise. /// public Coordinates(double x, double y, double z, double yaw, bool isRelative = false) : this(null, x, y, z, yaw, isRelative) { } /// /// Initializes a new instance of the struct. /// /// The world name. /// The X coordinate. /// The Y coordinate. /// The Z coordinate. /// The yaw. /// /// if these coordinates represent relative coordinates; otherwise. /// public Coordinates(string? world, double x, double y, double z, double yaw, bool isRelative = false) { World = world; X = x; Y = y; Z = z; Yaw = yaw; IsRelative = isRelative; } /// /// Gets or initializes a value indicating whether this instance represents relative coordinates. /// /// /// if this instance represents relative coordinates; otherwise, . /// public bool IsRelative { get; init; } /// /// Gets or initializes the world. /// /// The world. public string? World { get; init; } /// /// Gets or initializes the X coordinate. /// /// The X coordinate. public double X { get; init; } /// /// Gets or initializes the Y coordinate. /// /// The Y coordinate. public double Y { get; init; } /// /// Gets or initializes the yaw. /// /// The yaw. public double Yaw { get; init; } /// /// Gets or initializes the Z coordinate. /// /// The Z coordinate. public double Z { get; init; } public static bool operator ==(Coordinates left, Coordinates right) => left.Equals(right); public static bool operator !=(Coordinates left, Coordinates right) => !(left == right); /// /// Parses a coordinate string. /// /// The coordinates to parse. /// An instance of . public static Coordinates Parse(string coordinates) { return Serializer.Deserialize(coordinates); } /// /// Returns a value indicating whether this instance of and another instance of /// are equal. /// /// The instance against which to compare. /// /// if this instance is equal to ; otherwise, . /// public bool Equals(Coordinates other) { return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && Yaw.Equals(other.Yaw) && IsRelative.Equals(other.IsRelative) && string.Equals(World, other.World); } /// public override bool Equals(object? obj) { return obj is Coordinates other && Equals(other); } /// public override int GetHashCode() { return HashCode.Combine(World, X, Y, Z, Yaw); } /// /// Returns the string representation of these coordinates. /// /// A representation of these coordinates. public override string ToString() { return ToString("{0}"); } /// /// Returns the string representation of these coordinates. /// /// The format to apply to each component. /// A representation of these coordinates. public string ToString(string format) { return Serializer.Serialize(this, format); } internal static class Serializer { public static string Serialize(in Coordinates coordinates, string format) { int count = Serialize(coordinates, format, Span.Empty); Span chars = stackalloc char[count]; Serialize(coordinates, format, chars); return chars.ToString(); } public static int Serialize(in Coordinates coordinates, string format, Span destination) { using Utf8ValueStringBuilder builder = ZString.CreateUtf8StringBuilder(); if (!string.IsNullOrWhiteSpace(coordinates.World)) { builder.Append(coordinates.World); builder.Append(' '); } bool north = coordinates.Z >= 0.0; bool west = coordinates.X >= 0.0; bool up = coordinates.Y >= 0.0; bool dir = coordinates.Yaw >= 0.0; if (coordinates.IsRelative) { if (north) { builder.Append('+'); } builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Z)); builder.Append(' '); if (west) { builder.Append('+'); } builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.X)); builder.Append(' '); if (up) { builder.Append('+'); } builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Y)); builder.Append("a "); if (up) { builder.Append('+'); } builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Yaw)); } else { char zChar = north ? 'n' : 's'; char xChar = west ? 'w' : 'e'; builder.Append(string.Format(CultureInfo.InvariantCulture, format, Math.Abs(coordinates.Z))); builder.Append(zChar); builder.Append(' '); builder.Append(string.Format(CultureInfo.InvariantCulture, format, Math.Abs(coordinates.X))); builder.Append(xChar); builder.Append(' '); builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Y)); builder.Append("a "); builder.Append(string.Format(CultureInfo.InvariantCulture, format, coordinates.Yaw)); } ReadOnlySpan bytes = builder.AsSpan(); Span chars = stackalloc char[bytes.Length]; Encoding.UTF8.GetChars(bytes, chars); for (var index = 0; index < destination.Length; index++) { destination[index] = chars[index]; } return builder.Length; } public static Coordinates Deserialize(ReadOnlySpan value) { using Utf8ValueStringBuilder builder = ZString.CreateUtf8StringBuilder(); string? world = null; var isRelative = false; double x = 0.0, y = 0.0, z = 0.0, yaw = 0.0; var word = 0; for (var index = 0; index < value.Length; index++) { char current = value[index]; bool atEnd = index == value.Length - 1; if (atEnd || char.IsWhiteSpace(current)) { if (!builder.AsSpan().All(b => char.IsWhiteSpace((char)b))) { if (atEnd) { builder.Append(current); } ProcessBuffer(); word++; } builder.Clear(); } else { builder.Append(current); } } return new Coordinates(world, x, y, z, yaw, isRelative); void ProcessBuffer() { ReadOnlySpan bytes = builder.AsSpan(); Span chars = stackalloc char[bytes.Length]; Encoding.UTF8.GetChars(bytes, chars); bool hasWorld = !string.IsNullOrWhiteSpace(world); if (word == 0 && !IsUnitString(bytes)) { world = chars.ToString().AsNullIfWhiteSpace(); } else if (IsRelativeUnit(bytes)) { isRelative = true; switch (word) { case 0 when !hasWorld: case 1 when hasWorld: double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out z); break; case 1 when !hasWorld: case 2 when hasWorld: double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out x); break; case 2 when !hasWorld: case 3 when hasWorld: double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out y); break; case 3 when !hasWorld: case 4 when hasWorld: double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out yaw); break; } } else { if (((!hasWorld && word == 1) || (hasWorld && word == 2)) && chars[^1] is 'x' or 'X' or 'w' or 'W') { _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out x); } else if (((!hasWorld && word == 0) || (hasWorld && word == 1)) && chars[^1] is 'z' or 'Z' or 'n' or 'N') { _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out z); } else if (((!hasWorld && word == 1) || (hasWorld && word == 2)) && chars[^1] is 'e' or 'E') { _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out x); x = -x; } else if (((!hasWorld && word == 0) || (hasWorld && word == 1)) && chars[^1] is 's' or 'S') { _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out z); z = -z; } else if (((!hasWorld && word == 2) || (hasWorld && word == 3)) && chars[^1] is 'a' or 'A') { _ = double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out y); } else if (((!hasWorld && word == 3) || (hasWorld && word == 4)) && double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out double temp)) { yaw = temp; } } } } /// /// Returns a value indicating whether the specified span of characters represents a relative unit string. /// /// The span of characters to validate. /// /// if represents a valid relative unit string; otherwise, /// . /// public static bool IsAbsoluteUnit(ReadOnlySpan bytes) { Span chars = stackalloc char[bytes.Length]; Encoding.UTF8.GetChars(bytes, chars); return IsRelativeUnit(chars); } /// /// Returns a value indicating whether the specified span of characters represents a relative unit string. /// /// The span of characters to validate. /// /// if represents a valid relative unit string; otherwise, /// . /// public static bool IsAbsoluteUnit(ReadOnlySpan chars) { ReadOnlySpan validChars = "nNeEwWsSaA"; return double.TryParse(chars, out _) || (validChars.Contains(chars[^1]) && double.TryParse(chars[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out _)); } /// /// Returns a value indicating whether the specified span of characters represents a relative unit string. /// /// The span of characters to validate. /// /// if represents a valid relative unit string; otherwise, /// . /// public static bool IsRelativeUnit(ReadOnlySpan bytes) { Span chars = stackalloc char[bytes.Length]; Encoding.UTF8.GetChars(bytes, chars); return IsRelativeUnit(chars); } /// /// Returns a value indicating whether the specified span of characters represents a relative unit string. /// /// The span of characters to validate. /// /// if represents a valid relative unit string; otherwise, /// . /// public static bool IsRelativeUnit(ReadOnlySpan chars) { return (chars[0] == '+' || chars[0] == '-') && double.TryParse(chars, NumberStyles.Float, CultureInfo.InvariantCulture, out _); } /// /// Returns a value indicating whether the specified span of characters represents a valid coordinate unit string. /// /// The span of characters to validate. /// /// if represents a valid coordinate unit string; otherwise, /// . /// public static bool IsUnitString(ReadOnlySpan bytes) { Span chars = stackalloc char[bytes.Length]; Encoding.UTF8.GetChars(bytes, chars); return IsUnitString(chars); } /// /// Returns a value indicating whether the specified span of characters represents a valid coordinate unit string. /// /// The span of characters to validate. /// /// if represents a valid coordinate unit string; otherwise, /// . /// public static bool IsUnitString(ReadOnlySpan chars) { chars = chars.Trim(); if (chars.Length == 0) { return false; } if (!char.IsDigit(chars[0]) && chars[0] != '+' && chars[0] != '-') { return false; } // thicc char span return IsRelativeUnit(chars) || IsAbsoluteUnit(chars); } } }