// Copyright (c) 2021 homuler // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. using UnityEngine; namespace Mediapipe.Unity { /// /// This class draws annotations on the screen which is the parent of the attached .
/// That is, it's used like this.
/// 1. Select a GameObject where you'd like to draw annotations.
/// 2. Create an empty child GameObject (let's say AnnotationLayer) directly under it.
/// 3. Attach to AnnotationLayer.
/// 4. Create an empty child GameObject (let's say RootAnnotation) directly under AnnotationLayer.
/// 5. Attach to RootAnnotation.
///
/// /// Note that this class can be accessed from a thread other than main thread. /// Extended classes must be implemented to work in such a situation, since Unity APIs won't work in other threads. /// public abstract class AnnotationController : MonoBehaviour where T : HierarchicalAnnotation { [SerializeField] protected T annotation; protected bool isStale = false; public bool isMirrored { get => annotation.isMirrored; set { if (annotation.isMirrored != value) { annotation.isMirrored = value; } } } public RotationAngle rotationAngle { get => annotation.rotationAngle; set { if (annotation.rotationAngle != value) { annotation.rotationAngle = value; } } } protected virtual void Start() { if (!TryGetComponent(out var _)) { Logger.LogVerbose(GetType().Name, $"Adding RectTransform to {gameObject.name}"); var rectTransform = gameObject.AddComponent(); // stretch width and height by default rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.anchoredPosition3D = Vector3.zero; rectTransform.sizeDelta = Vector2.zero; } } protected virtual void LateUpdate() { if (isStale) { SyncNow(); } } protected virtual void OnDestroy() { if (annotation != null) { Destroy(annotation); annotation = null; } isStale = false; } /// /// Draw annotations in current thread. /// This method must set to false. /// /// /// This method can only be called from main thread. /// protected abstract void SyncNow(); protected void UpdateCurrentTarget(TValue newTarget, ref TValue currentTarget) { if (IsTargetChanged(newTarget, currentTarget)) { currentTarget = newTarget; isStale = true; } } protected bool IsTargetChanged(TValue newTarget, TValue currentTarget) { // It's assumed that target has not changed iff previous target and new target are both null. return currentTarget != null || newTarget != null; } } }