diff --git a/Assets/Scripts/Anchor.meta b/Assets/Scripts/Anchor.meta new file mode 100644 index 0000000..13cb4bc --- /dev/null +++ b/Assets/Scripts/Anchor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60cc4243135e5cd4b9d21f837db97b31 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Anchor/Anchor.cs b/Assets/Scripts/Anchor/Anchor.cs new file mode 100644 index 0000000..14b8687 --- /dev/null +++ b/Assets/Scripts/Anchor/Anchor.cs @@ -0,0 +1,63 @@ +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + +public class Anchor : MonoBehaviour +{ + // Comparison tolerances + const float positionEPS = 0.1f; + const float rotationEPS = 0.1f; + + // See anchor within editor + #if UNITY_EDITOR + public const float tipsSize = 0.2f; + + private void OnDrawGizmosSelected() + { + Handles.color = Color.magenta; + Vector3 position = transform.position; + float size = transform.lossyScale.x; + + if (size >= 0) + { + Handles.ArrowHandleCap( + 0, + position, + Quaternion.LookRotation(transform.forward), + size, + EventType.Repaint + ); + + Vector3 leftLimit = position - size*transform.right; + Vector3 rightLimit = position + size*transform.right; + Handles.DrawLine(leftLimit, rightLimit); + + Handles.DrawLine( + leftLimit - tipsSize*size*transform.forward, + leftLimit + tipsSize*size*transform.forward + ); + Handles.DrawLine( + rightLimit - tipsSize*size*transform.forward, + rightLimit + tipsSize*size*transform.forward + ); + } + } + #endif + + /// + /// Test whether this anchor is connected to another anchor + /// + /// The other anchor to compare to + /// Whether to test for scale match + /// The anchoring state of this anchor and the given anchor + public bool IsAnchoredTo(Anchor other, bool matchScales=true) + { + if (matchScales && (Mathf.Abs(transform.lossyScale.x - other.transform.lossyScale.x) > positionEPS)) + { + return false; + } + return Quaternion.Angle(transform.rotation*Quaternion.AngleAxis(180f, Vector3.up), other.transform.rotation) < rotationEPS + && Vector3.Distance(transform.position, other.transform.position) < positionEPS; + } +} diff --git a/Assets/Scripts/Anchor/Anchor.cs.meta b/Assets/Scripts/Anchor/Anchor.cs.meta new file mode 100644 index 0000000..670587e --- /dev/null +++ b/Assets/Scripts/Anchor/Anchor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c8e92a0688e18aa4891eb7937ca9cd0e \ No newline at end of file diff --git a/Assets/Scripts/Anchor/AnchoredObject.cs b/Assets/Scripts/Anchor/AnchoredObject.cs new file mode 100644 index 0000000..decbef7 --- /dev/null +++ b/Assets/Scripts/Anchor/AnchoredObject.cs @@ -0,0 +1,120 @@ +using System.Collections.Generic; +using UnityEngine; + +public class AnchoredObject : MonoBehaviour +{ + [SerializeField] List anchors = new List(); + + /// + /// Get an anchor from its ID (index) + /// + /// ID of the anchor to search for + /// The anchor that has the given ID + public Anchor GetAnchorFromID(int anchorID) + { + if (anchorID > anchors.Count) + { + Debug.LogError("Error: Tried to anchor to an inexisting anchor. Check anchors list."); + } + return anchors[anchorID]; + } + + /// + /// Anchor this object to another + /// + /// The anchored object to connect to + /// ID of the partner anchor to connect to + /// ID of anchor this object wants to connect + /// Whether to anchor scales too + public void AnchorTo(AnchoredObject partner, int partnerAnchorID, int myAnchorID, bool matchScales=true) + { + AnchorTo( + GetAnchorFromID(myAnchorID), + partner.GetAnchorFromID(partnerAnchorID) + ); + } + + /// + /// Anchor this object to another + /// + /// The anchor to connect + /// The anchor of the other object to connect to + /// Whether to anchor scales too + public void AnchorTo(Anchor myAnchor, Anchor partnerAnchor, bool matchScales=true) + { + if (matchScales) + { + // Match scales + float sizeDelta = partnerAnchor.transform.lossyScale.x / myAnchor.transform.lossyScale.x; + transform.localScale *= sizeDelta; + } + + // Match rotations + transform.rotation = + partnerAnchor.transform.rotation + * Quaternion.AngleAxis(180f, Vector3.up) + * Quaternion.Inverse(myAnchor.transform.rotation) + * transform.rotation; + + // Match positions + transform.position += partnerAnchor.transform.position - myAnchor.transform.position; + } + + /// + /// Connect multiple anchors of this object with multiple anchors. + /// If one of the connections fails, the anchoring operation fails. + /// + /// The anchored object to connect to + /// IDs of the partner anchors to connect to + /// IDs of the anchors this object wants to connect + /// Whether to anchor scales too + /// Whether the anchoring succeeded or not + public bool AnchorToMultiple(AnchoredObject partner, IList partnerAnchorIDs, IList myAnchorIDs, bool matchScales=true) + { + if (myAnchorIDs.Count != partnerAnchorIDs.Count) + { + Debug.LogError("Error: Number of anchors must match."); + } + List myAnchors = new List(); + List partnerAnchors = new List(); + for (int i = 0; i < myAnchorIDs.Count; i++) + { + myAnchors.Add(GetAnchorFromID(myAnchorIDs[i])); + partnerAnchors.Add(partner.GetAnchorFromID(partnerAnchorIDs[i])); + } + return AnchorToMultiple(myAnchors, partnerAnchors, matchScales); + } + + /// + /// Connect multiple anchors of this object with multiple anchors. + /// If one of the connections fails, the anchoring operation fails. + /// + /// The anchors this object wants to connect + /// The anchors this object wants to connect to + /// Whether to anchor scales too + /// Whether the anchoring succeeded or not + public bool AnchorToMultiple(IList myAnchors, IList targetAnchors, bool matchScales=true) + { + if (myAnchors.Count != targetAnchors.Count) + { + Debug.LogError("Error: Number of anchors must match."); + return false; + } + if (myAnchors.Count == 0) + { + Debug.LogError("Amount of anchors to connect must be at least 1."); + return false; + } + + // Connect the first anchors. Then, test if other anchors match. + AnchorTo(myAnchors[0], targetAnchors[0], matchScales); + for (int i=1; i