using System; using System.Runtime.InteropServices; using System.Text.RegularExpressions; namespace UnityEngine.XR.ARSubsystems { /// /// A session-unique identifier for trackables in the real-world environment, such as planes and feature points. /// /// /// /// Ids are generally unique to a particular session, but multiple sessions might produce /// identical ids for different trackables. /// /// A trackable id is a 128 bit number, stored as two ulongs. This makes it large enough to hold a Guid. /// /// [StructLayout(LayoutKind.Sequential)] public struct TrackableId : IEquatable { /// /// Regular expression that matches a valid trackable identifier. /// static readonly Regex s_TrackableIdRegex = new( @"^(?[a-fA-F\d]{16})-(?[a-fA-F\d]{16})$", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline | RegexOptions.ExplicitCapture); /// /// Get the invalid id. /// public static TrackableId invalidId => new(0,0); ulong m_SubId1; ulong m_SubId2; /// /// The first half of the id. /// public ulong subId1 { get => m_SubId1; set => m_SubId1 = value; } /// /// The second half of the id. /// public ulong subId2 { get => m_SubId2; set => m_SubId2 = value; } /// /// Constructs a TrackableId from two ulongs. /// /// The first half of the id. /// The second half of the id. public TrackableId(ulong subId1, ulong subId2) { m_SubId1 = subId1; m_SubId2 = subId2; } /// /// Construct a trackable identifier by parsing the given identifier string. /// /// An identifier string. /// Thrown if the given identifier string cannot be parsed. public TrackableId(string idString) { var regexMatch = s_TrackableIdRegex.Match(idString); if (!regexMatch.Success) { throw new FormatException($"trackable ID '{idString}' does not match expected format"); } try { m_SubId1 = ulong.Parse(regexMatch.Groups["part1"].Value, System.Globalization.NumberStyles.HexNumber); m_SubId2 = ulong.Parse(regexMatch.Groups["part2"].Value, System.Globalization.NumberStyles.HexNumber); } catch (Exception e) { throw new FormatException($"cannot parse trackable ID '{idString}'", e); } } /// /// Convert from to `TrackableId` using the constructor. /// /// The SerializableGuid to convert. /// The TrackableId. public static implicit operator TrackableId(SerializableGuid serializableGuid) { return new TrackableId(serializableGuid.guidLow, serializableGuid.guidHigh); } /// /// Generates a string representation of the id suitable for debugging. /// /// A string representation of the id. public override string ToString() { return $"{m_SubId1:X16}-{m_SubId2:X16}"; } /// /// Generates a hash code suitable for use in a Dictionary or Set. /// /// A hash code for participation in certain collections. public override int GetHashCode() => HashCodeUtil.Combine(m_SubId1.GetHashCode(), m_SubId2.GetHashCode()); /// /// Tests for equality. /// /// The `object` to compare against. /// `True` if is of type and /// also returns `true`; otherwise `false`. public override bool Equals(object obj) => obj is TrackableId && Equals((TrackableId)obj); /// /// Tests for equality. /// /// The other to compare against. /// `True` if every field in is equal to this , otherwise `false`. public bool Equals(TrackableId other) => (m_SubId1 == other.m_SubId1) && (m_SubId2 == other.m_SubId2); /// /// Tests for equality. Same as . /// /// The left-hand side of the comparison. /// The right-hand side of the comparison. /// `True` if is equal to , otherwise `false`. public static bool operator ==(TrackableId lhs, TrackableId rhs) => (lhs.m_SubId1 == rhs.m_SubId1) && (lhs.m_SubId2 == rhs.m_SubId2); /// /// Tests for inequality. Same as `!`. /// /// The left-hand side of the comparison. /// The right-hand side of the comparison. /// `True` if is not equal to , otherwise `false`. public static bool operator !=(TrackableId lhs, TrackableId rhs) => (lhs.m_SubId1 != rhs.m_SubId1) || (lhs.m_SubId2 != rhs.m_SubId2); } }