diff --git a/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/Anchor3dAnnotation.cs b/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/Anchor3dAnnotation.cs index af9230e6c..435254887 100644 --- a/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/Anchor3dAnnotation.cs +++ b/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/Anchor3dAnnotation.cs @@ -53,7 +53,7 @@ public void Draw(Anchor3d? target, Quaternion rotation, Vector3 cameraPosition, if (ActivateFor(target)) { var anchor3d = (Anchor3d)target; - var anchor2dPosition = GetAnnotationLayer().GetLocalPosition(anchor3d, rotationAngle, isMirrored); + var anchor2dPosition = GetScreenRect().GetPoint(anchor3d, rotationAngle, isMirrored); var anchor3dPosition = GetAnchorPositionInRay(anchor2dPosition, anchor3d.z * defaultDepth, cameraPosition); _pointAnnotation.Draw(anchor2dPosition); diff --git a/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/InstantMotionTrackingSolution.cs b/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/InstantMotionTrackingSolution.cs index 65f9f8d4d..80ca74841 100644 --- a/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/InstantMotionTrackingSolution.cs +++ b/Assets/MediaPipeUnity/Samples/Scenes/Instant Motion Tracking/InstantMotionTrackingSolution.cs @@ -26,7 +26,7 @@ private void Update() if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, Input.mousePosition, Camera.main, out var localPoint)) { var isMirrored = ImageSourceProvider.ImageSource.isFrontFacing ^ ImageSourceProvider.ImageSource.isHorizontallyFlipped; - var normalizedPoint = rectTransform.GetNormalizedPosition(localPoint, graphRunner.rotation, isMirrored); + var normalizedPoint = rectTransform.rect.PointToImageNormalized(localPoint, graphRunner.rotation, isMirrored); graphRunner.ResetAnchor(normalizedPoint.x, normalizedPoint.y); _trackedAnchorDataAnnotationController.ResetAnchor(); } diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/DetectionAnnotation.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/DetectionAnnotation.cs index 88e94a466..2e24a6671 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/DetectionAnnotation.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/DetectionAnnotation.cs @@ -65,7 +65,7 @@ public void Draw(Detection target, float threshold = 0.0f) // Assume that location data's format is always RelativeBoundingBox // TODO: fix if there are cases where this assumption is not correct. - var rectVertices = GetAnnotationLayer().GetRectVertices(target.LocationData.RelativeBoundingBox, rotationAngle, isMirrored); + var rectVertices = GetScreenRect().GetRectVertices(target.LocationData.RelativeBoundingBox, rotationAngle, isMirrored); _locationDataAnnotation.SetColor(GetColor(score, Mathf.Clamp(threshold, 0.0f, 1.0f))); _locationDataAnnotation.Draw(rectVertices); diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/HierarchicalAnnotation.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/HierarchicalAnnotation.cs index 1e4c7eab6..c3a8d6286 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/HierarchicalAnnotation.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/HierarchicalAnnotation.cs @@ -13,6 +13,7 @@ public interface IHierachicalAnnotation IHierachicalAnnotation root { get; } Transform transform { get; } RectTransform GetAnnotationLayer(); + UnityEngine.Rect GetScreenRect(); } public abstract class HierarchicalAnnotation : MonoBehaviour, IHierachicalAnnotation @@ -37,6 +38,11 @@ public RectTransform GetAnnotationLayer() return root.transform.parent.gameObject.GetComponent(); } + public UnityEngine.Rect GetScreenRect() + { + return GetAnnotationLayer().rect; + } + public bool isActive => gameObject.activeSelf; public bool isActiveInHierarchy => gameObject.activeInHierarchy; diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/IrisLandmarkListAnnotation.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/IrisLandmarkListAnnotation.cs index 9e516c1a0..d0ef2a077 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/IrisLandmarkListAnnotation.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/IrisLandmarkListAnnotation.cs @@ -65,13 +65,13 @@ public void Draw(IList target, bool visualizeZ = false, int { _landmarkListAnnotation.Draw(target, visualizeZ); - var rectTransform = GetAnnotationLayer(); - var center = rectTransform.GetLocalPosition(target[0], rotationAngle, isMirrored); + var rect = GetScreenRect(); + var center = rect.GetPoint(target[0], rotationAngle, isMirrored); if (!visualizeZ) { center.z = 0.0f; } - var radius = CalculateRadius(rectTransform, target); + var radius = CalculateRadius(rect, target); _circleAnnotation.Draw(center, radius, vertices); } } @@ -81,17 +81,17 @@ public void Draw(NormalizedLandmarkList target, bool visualizeZ = false, int ver Draw(target?.Landmark, visualizeZ, vertices); } - private float CalculateRadius(RectTransform rectTransform, IList target) + private float CalculateRadius(UnityEngine.Rect rect, IList target) { - var r1 = CalculateDistance(rectTransform, target[1], target[3]); - var r2 = CalculateDistance(rectTransform, target[2], target[4]); + var r1 = CalculateDistance(rect, target[1], target[3]); + var r2 = CalculateDistance(rect, target[2], target[4]); return (r1 + r2) / 4; } - private float CalculateDistance(RectTransform rectTransform, NormalizedLandmark a, NormalizedLandmark b) + private float CalculateDistance(UnityEngine.Rect rect, NormalizedLandmark a, NormalizedLandmark b) { - var aPos = rectTransform.GetLocalPosition(a, rotationAngle, isMirrored); - var bPos = rectTransform.GetLocalPosition(b, rotationAngle, isMirrored); + var aPos = rect.GetPoint(a, rotationAngle, isMirrored); + var bPos = rect.GetPoint(b, rotationAngle, isMirrored); aPos.z = 0.0f; bPos.z = 0.0f; return Vector3.Distance(aPos, bPos); diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/PointAnnotation.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/PointAnnotation.cs index e416d887d..d6eb284c8 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/PointAnnotation.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/PointAnnotation.cs @@ -53,7 +53,7 @@ public void Draw(Landmark target, Vector3 scale, bool visualizeZ = true) { if (ActivateFor(target)) { - var position = GetAnnotationLayer().GetLocalPosition(target, scale, rotationAngle, isMirrored); + var position = GetScreenRect().GetPoint(target, scale, rotationAngle, isMirrored); if (!visualizeZ) { position.z = 0.0f; @@ -66,7 +66,7 @@ public void Draw(NormalizedLandmark target, bool visualizeZ = true) { if (ActivateFor(target)) { - var position = GetAnnotationLayer().GetLocalPosition(target, rotationAngle, isMirrored); + var position = GetScreenRect().GetPoint(target, rotationAngle, isMirrored); if (!visualizeZ) { position.z = 0.0f; @@ -79,7 +79,7 @@ public void Draw(NormalizedPoint2D target) { if (ActivateFor(target)) { - var position = GetAnnotationLayer().GetLocalPosition(target, rotationAngle, isMirrored); + var position = GetScreenRect().GetPoint(target, rotationAngle, isMirrored); transform.localPosition = position; } } @@ -88,7 +88,7 @@ public void Draw(Point3D target, Vector2 focalLength, Vector2 principalPoint, fl { if (ActivateFor(target)) { - var position = GetAnnotationLayer().GetLocalPosition(target, focalLength, principalPoint, zScale, rotationAngle, isMirrored); + var position = GetScreenRect().GetPoint(target, focalLength, principalPoint, zScale, rotationAngle, isMirrored); if (!visualizeZ) { position.z = 0.0f; @@ -113,7 +113,7 @@ public void Draw(mplt.RelativeKeypoint target, float threshold = 0.0f) { if (ActivateFor(target)) { - Draw(GetAnnotationLayer().GetLocalPosition(target, rotationAngle, isMirrored)); + Draw(GetScreenRect().GetPoint(target, rotationAngle, isMirrored)); SetColor(GetColor(target.Score, threshold)); } } diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleAnnotation.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleAnnotation.cs index c2f7fab0e..ce46841f1 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleAnnotation.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleAnnotation.cs @@ -58,11 +58,11 @@ public void Draw(Vector3[] positions) _lineRenderer.SetPositions(positions ?? _EmptyPositions); } - public void Draw(Rect target, Vector2 imageSize) + public void Draw(Rect target, Vector2Int imageSize) { if (ActivateFor(target)) { - Draw(GetAnnotationLayer().GetRectVertices(target, imageSize, rotationAngle, isMirrored)); + Draw(GetScreenRect().GetRectVertices(target, imageSize, rotationAngle, isMirrored)); } } @@ -70,11 +70,11 @@ public void Draw(NormalizedRect target) { if (ActivateFor(target)) { - Draw(GetAnnotationLayer().GetRectVertices(target, rotationAngle, isMirrored)); + Draw(GetScreenRect().GetRectVertices(target, rotationAngle, isMirrored)); } } - public void Draw(LocationData target, Vector2 imageSize) + public void Draw(LocationData target, Vector2Int imageSize) { if (ActivateFor(target)) { @@ -82,12 +82,12 @@ public void Draw(LocationData target, Vector2 imageSize) { case mplt.Format.BoundingBox: { - Draw(GetAnnotationLayer().GetRectVertices(target.BoundingBox, imageSize, rotationAngle, isMirrored)); + Draw(GetScreenRect().GetRectVertices(target.BoundingBox, imageSize, rotationAngle, isMirrored)); break; } case mplt.Format.RelativeBoundingBox: { - Draw(GetAnnotationLayer().GetRectVertices(target.RelativeBoundingBox, rotationAngle, isMirrored)); + Draw(GetScreenRect().GetRectVertices(target.RelativeBoundingBox, rotationAngle, isMirrored)); break; } case mplt.Format.Global: @@ -108,7 +108,7 @@ public void Draw(LocationData target) { case mplt.Format.RelativeBoundingBox: { - Draw(GetAnnotationLayer().GetRectVertices(target.RelativeBoundingBox, rotationAngle, isMirrored)); + Draw(GetScreenRect().GetRectVertices(target.RelativeBoundingBox, rotationAngle, isMirrored)); break; } case mplt.Format.BoundingBox: diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleListAnnotation.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleListAnnotation.cs index 33a23a174..580cb52dd 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleListAnnotation.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/RectangleListAnnotation.cs @@ -36,7 +36,7 @@ public void SetLineWidth(float lineWidth) ApplyLineWidth(_lineWidth); } - public void Draw(IList targets, Vector2 imageSize) + public void Draw(IList targets, Vector2Int imageSize) { if (ActivateFor(targets)) { diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/TransformAnnotation.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/TransformAnnotation.cs index 8e7c8df40..b867391fd 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/TransformAnnotation.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/Annotation/TransformAnnotation.cs @@ -38,34 +38,25 @@ public void SetArrowWidth(float arrowWidth) public void Draw(Quaternion rotation, Vector3 scale, bool visualizeZ = true) { var q = Quaternion.Euler(0, 0, -(int)rotationAngle); - DrawArrow(_xArrow, q * rotation * Vector3.right, scale.x, visualizeZ); - DrawArrow(_yArrow, q * rotation * Vector3.up, scale.y, visualizeZ); - DrawArrow(_zArrow, q * rotation * Vector3.forward, scale.z, visualizeZ); + DrawArrow(_xArrow, scale.x * (q * rotation * Vector3.right), visualizeZ); + DrawArrow(_yArrow, scale.y * (q * rotation * Vector3.up), visualizeZ); + DrawArrow(_zArrow, scale.z * (q * rotation * Vector3.forward), visualizeZ); } public void Draw(ObjectAnnotation target, Vector3 position, float arrowLengthScale = 1.0f, bool visualizeZ = true) { origin = position; - var isInverted = CameraCoordinate.IsInverted(rotationAngle); - var (xScale, yScale) = isInverted ? (target.Scale[1], target.Scale[0]) : (target.Scale[0], target.Scale[1]); - var zScale = target.Scale[2]; - // convert from right-handed to left-handed - var isXReversed = CameraCoordinate.IsXReversed(rotationAngle, isMirrored); - var isYReversed = CameraCoordinate.IsYReversed(rotationAngle, isMirrored); - var rotation = target.Rotation; - var xDir = GetDirection(rotation[0], rotation[3], rotation[6], isXReversed, isYReversed, isInverted); - var yDir = GetDirection(rotation[1], rotation[4], rotation[7], isXReversed, isYReversed, isInverted); - var zDir = GetDirection(rotation[2], rotation[5], rotation[8], isXReversed, isYReversed, isInverted); - DrawArrow(_xArrow, xDir, (isMirrored ? -1 : 1) * arrowLengthScale * xScale, visualizeZ); - DrawArrow(_yArrow, yDir, arrowLengthScale * yScale, visualizeZ); - DrawArrow(_zArrow, zDir, -arrowLengthScale * zScale, visualizeZ); + var (xDir, yDir, zDir) = CameraCoordinate.GetDirections(target, rotationAngle, isMirrored); + DrawArrow(_xArrow, arrowLengthScale * xDir, visualizeZ); + DrawArrow(_yArrow, arrowLengthScale * yDir, visualizeZ); + DrawArrow(_zArrow, arrowLengthScale * zDir, visualizeZ); } - private void DrawArrow(Arrow arrow, Vector3 normalizedDirection, float scale, bool visualizeZ) + private void DrawArrow(Arrow arrow, Vector3 vec, bool visualizeZ) { - var direction = Mathf.Sign(scale) * normalizedDirection; - var magnitude = Mathf.Abs(scale); + var magnitude = vec.magnitude; + var direction = vec.normalized; if (!visualizeZ) { @@ -76,11 +67,5 @@ private void DrawArrow(Arrow arrow, Vector3 normalizedDirection, float scale, bo arrow.direction = direction; arrow.magnitude = magnitude; } - - private Vector3 GetDirection(float x, float y, float z, bool isXReversed, bool isYReversed, bool isInverted) - { - var dir = isInverted ? new Vector3(y, x, z) : new Vector3(x, y, z); - return Vector3.Scale(dir, new Vector3(isXReversed ? -1 : 1, isYReversed ? -1 : 1, -1)); - } } } diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/CameraCoordinate.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/CameraCoordinate.cs index ff50c69e6..cce6ce0d7 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/CameraCoordinate.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/CameraCoordinate.cs @@ -17,45 +17,262 @@ namespace Mediapipe.Unity.CoordinateSystem /// public static class CameraCoordinate { + public static Vector3 CameraToRealWorld(float x, float y, float z, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + var isInverted = IsInverted(imageRotation); + var (rx, ry) = isInverted ? (y, x) : (x, y); + return new Vector3(IsXReversed(imageRotation, isMirrored) ? -rx : rx, IsYReversed(imageRotation, isMirrored) ? -ry : ry, -z); + } + /// /// Convert from camera coordinates to local coordinates in Unity. /// - /// - /// to be used for calculating local coordinates - /// /// X in camera coordinates /// Y in camera coordinates /// Z in camera coordinates - /// Normalized focal lengths in image coordinates - /// Normalized principal point in image coordinates + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// Normalized focal length X in NDC space + /// Normalized focal length Y in NDC space + /// Normalized principal point X in NDC space + /// Normalized principal point Y in NDC space /// Ratio of Z values in camera coordinates to local coordinates in Unity - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(RectTransform rectTransform, float x, float y, float z, Vector2 focalLength, Vector2 principalPoint, float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 CameraToLocalPoint(float x, float y, float z, float xMin, float xMax, float yMin, float yMax, + float focalLengthX, float focalLengthY, float principalX, float principalY, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - var pixelX = ((-focalLength.x * x / z) + principalPoint.x) / 2; - var pixelY = ((focalLength.y * y / z) + principalPoint.y) / 2; + var (ndcX, ndcY) = ((-focalLengthX * x / z) + principalX, (focalLengthY * y / z) + principalY); + var (width, height) = (xMax - xMin, yMax - yMin); + var (nx, ny) = ((1 + ndcX) / 2.0f, (1 - ndcY) / 2.0f); + var (rectX, rectY) = IsInverted(imageRotation) ? (ny * width, nx * height) : (nx * width, ny * height); + var localX = (IsXReversed(imageRotation, isMirrored) ? width - rectX : rectX) + xMin; + var localY = (IsYReversed(imageRotation, isMirrored) ? height - rectY : rectY) + yMin; // Reverse the sign of Z because camera coordinate system is right-handed - var rect = rectTransform.rect; - return RealWorldCoordinate.GetLocalPosition(pixelX, pixelY, -z, new Vector3(rect.width, rect.height, zScale), imageRotation, isMirrored); + var localZ = -z * zScale; + + return new Vector3(localX, localY, localZ); + } + + /// + /// Convert from camera coordinates to local coordinates in Unity. + /// + /// X in camera coordinates + /// Y in camera coordinates + /// Z in camera coordinates + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// Normalized focal lengths in NDC space + /// Normalized principal point in NDC space + /// Ratio of Z values in camera coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 CameraToLocalPoint(float x, float y, float z, float xMin, float xMax, float yMin, float yMax, Vector2 focalLength, Vector2 principalPoint, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return CameraToLocalPoint(x, y, z, xMin, xMax, yMin, yMax, focalLength.x, focalLength.y, principalPoint.x, principalPoint.y, zScale, imageRotation, isMirrored); + } + + /// + /// Convert from camera coordinates to local coordinates in Unity. + /// + /// Rectangle to get a point inside + /// X in camera coordinates + /// Y in camera coordinates + /// Z in camera coordinates + /// Normalized focal length X in NDC space + /// Normalized focal length Y in NDC space + /// Normalized principal point X in NDC space + /// Normalized principal point Y in NDC space + /// Ratio of Z values in camera coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 CameraToPoint(UnityEngine.Rect rectangle, float x, float y, float z, + float focalLengthX, float focalLengthY, float principalX, float principalY, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return CameraToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, focalLengthX, focalLengthY, principalX, principalY, + zScale, imageRotation, isMirrored); + } + + /// + /// Convert from camera coordinates to local coordinates in Unity. + /// + /// Rectangle to get a point inside + /// X in camera coordinates + /// Y in camera coordinates + /// Z in camera coordinates + /// Normalized focal lengths in NDC space + /// Normalized principal point in NDC space + /// Ratio of Z values in camera coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 CameraToPoint(UnityEngine.Rect rectangle, float x, float y, float z, + Vector2 focalLength, Vector2 principalPoint, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return CameraToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, focalLength.x, focalLength.y, principalPoint.x, principalPoint.y, + zScale, imageRotation, isMirrored); + } + + /// + /// Convert from camera coordinates to local coordinates in Unity. + /// It is assumed that the principal point is (0, 0) in the camera coordinate system and the focal length is (1, 1). + /// + /// Rectangle to get a point inside + /// X in camera coordinates + /// Y in camera coordinates + /// Z in camera coordinates + /// Ratio of Z values in camera coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 CameraToPoint(UnityEngine.Rect rectangle, float x, float y, float z, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return CameraToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, Vector2.one, Vector2.zero, zScale, imageRotation, isMirrored); + } + + /// + /// Get the coordinates represented by in local coordinate system. + /// + /// Rectangle to get a point inside + /// Normalized focal length X in NDC space + /// Normalized focal length Y in NDC space + /// Normalized principal point X in NDC space + /// Normalized principal point Y in NDC space + /// Ratio of values in camera coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 GetPoint(this UnityEngine.Rect rectangle, Point3D point3d, float focalLengthX, float focalLengthY, float principalX, float principalY, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return CameraToPoint(rectangle, point3d.X, point3d.Y, point3d.Z, focalLengthX, focalLengthY, principalX, principalY, zScale, imageRotation, isMirrored); } /// /// Get the coordinates represented by in local coordinate system. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// Normalized focal lengths in NDC space + /// Normalized principal point in NDC space + /// Ratio of values in camera coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// Focal lengths in image coordinates - /// Principal point in image coordinates + /// Set to true if the original coordinates is mirrored + public static Vector3 GetPoint(this UnityEngine.Rect rectangle, Point3D point3d, Vector2 focalLength, Vector2 principalPoint, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return CameraToPoint(rectangle, point3d.X, point3d.Y, point3d.Z, focalLength, principalPoint, zScale, imageRotation, isMirrored); + } + + /// + /// Get the coordinates represented by in local coordinate system. + /// + /// Rectangle to get a point inside /// Ratio of values in camera coordinates to local coordinates in Unity - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(this RectTransform rectTransform, Point3D point3d, Vector2 focalLength, Vector2 principalPoint, float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 GetPoint(this UnityEngine.Rect rectangle, Point3D point3d, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return CameraToPoint(rectangle, point3d.X, point3d.Y, point3d.Z, zScale, imageRotation, isMirrored); + } + + public static Quaternion GetApproximateQuaternion(ObjectAnnotation objectAnnotation, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + var isInverted = IsInverted(imageRotation); + var isXReversed = IsXReversed(imageRotation, isMirrored); + var isYReversed = IsYReversed(imageRotation, isMirrored); + var forward = GetZDir(objectAnnotation, isXReversed, isYReversed, isInverted); + var upward = GetYDir(objectAnnotation, isXReversed, isYReversed, isInverted); + + return Quaternion.LookRotation(forward, upward); + } + + public static (Vector3, Vector3, Vector3) GetDirections(ObjectAnnotation objectAnnotation, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + var isInverted = IsInverted(imageRotation); + var isXReversed = IsXReversed(imageRotation, isMirrored); + var isYReversed = IsYReversed(imageRotation, isMirrored); + var scale = objectAnnotation.Scale; + var xDir = scale[0] * GetXDir(objectAnnotation, isXReversed, isYReversed, isInverted); + var yDir = scale[1] * GetYDir(objectAnnotation, isXReversed, isYReversed, isInverted); + var zDir = scale[2] * GetZDir(objectAnnotation, isXReversed, isYReversed, isInverted); + return (xDir, yDir, zDir); + } + + private static Vector3 GetXDir(ObjectAnnotation objectAnnotation, bool isXReversed, bool isYReversed, bool isInverted) + { + var points = objectAnnotation.Keypoints; + var v1 = GetDirection(points[1].Point3D, points[5].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v2 = GetDirection(points[2].Point3D, points[6].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v3 = GetDirection(points[3].Point3D, points[7].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v4 = GetDirection(points[4].Point3D, points[8].Point3D, isXReversed, isYReversed, isInverted).normalized; + return (v1 + v2 + v3 + v4) / 4; + } + + private static Vector3 GetYDir(ObjectAnnotation objectAnnotation, bool isXReversed, bool isYReversed, bool isInverted) { - return GetLocalPosition(rectTransform, point3d.X, point3d.Y, point3d.Z, focalLength, principalPoint, zScale, imageRotation, isMirrored); + var points = objectAnnotation.Keypoints; + var v1 = GetDirection(points[1].Point3D, points[3].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v2 = GetDirection(points[2].Point3D, points[4].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v3 = GetDirection(points[5].Point3D, points[7].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v4 = GetDirection(points[6].Point3D, points[8].Point3D, isXReversed, isYReversed, isInverted).normalized; + return (v1 + v2 + v3 + v4) / 4; } + private static Vector3 GetZDir(ObjectAnnotation objectAnnotation, bool isXReversed, bool isYReversed, bool isInverted) + { + var points = objectAnnotation.Keypoints; + var v1 = GetDirection(points[2].Point3D, points[1].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v2 = GetDirection(points[4].Point3D, points[3].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v3 = GetDirection(points[6].Point3D, points[5].Point3D, isXReversed, isYReversed, isInverted).normalized; + var v4 = GetDirection(points[8].Point3D, points[7].Point3D, isXReversed, isYReversed, isInverted).normalized; + return (v1 + v2 + v3 + v4) / 4; + } + + private static Vector3 GetDirection(Point3D from, Point3D to, bool isXReversed, bool isYReversed, bool isInverted) + { + var xDiff = to.X - from.X; + var yDiff = to.Y - from.Y; + var (xDir, yDir) = isInverted ? (yDiff, xDiff) : (xDiff, yDiff); + // convert from right-handed to left-handed + return new Vector3(isXReversed ? -xDir : xDir, isYReversed ? -yDir : yDir, from.Z - to.Z); + } + + /// + /// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates. + /// For example, if is and is false, this returns true + /// because the original Y axis will be exactly opposite the X axis in Unity coordinates if the image is rotated back. + /// public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = false) { return isMirrored ? @@ -63,6 +280,11 @@ public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = fa rotationAngle == RotationAngle.Rotation180 || rotationAngle == RotationAngle.Rotation270; } + /// + /// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates. + /// For example, if is and is false, this returns true + /// because the original X axis will be exactly opposite the Y axis in Unity coordinates if the image is rotated back. + /// public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = false) { return isMirrored ? @@ -70,6 +292,9 @@ public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = fa rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation180; } + /// + /// Returns true if is or . + /// public static bool IsInverted(RotationAngle rotationAngle) { return rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation270; diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/ImageCoordinate.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/ImageCoordinate.cs index b1499d93b..b70bcbae7 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/ImageCoordinate.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/ImageCoordinate.cs @@ -18,178 +18,363 @@ public static class ImageCoordinate /// /// Convert from image coordinates to local coordinates in Unity. /// - /// - /// to be used for calculating local coordinates - /// /// Column value in the image coordinate system /// Row value in the image coordinate system - /// Depth value in local coordinate system - /// Image size in pixels - /// Counterclockwise rotation angle of the input image - /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(RectTransform rectTransform, int x, int y, int z, Vector2 imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + /// Depth value in the local coordinate system + /// + /// The target screen width. The returned value will be local to this screen. + /// + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// Image width in pixels + /// Image width in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the image coordinates is mirrored + public static Vector3 ImageToLocalPoint(int x, int y, int z, float xMin, float xMax, float yMin, float yMax, + int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - var rect = rectTransform.rect; var isInverted = IsInverted(imageRotation); var (rectX, rectY) = isInverted ? (y, x) : (x, y); - var localX = ((IsXReversed(imageRotation, isMirrored) ? imageSize.x - rectX : rectX) * rect.width / imageSize.x) - (rect.width / 2); - var localY = ((IsYReversed(imageRotation, isMirrored) ? imageSize.y - rectY : rectY) * rect.height / imageSize.y) - (rect.height / 2); + var (width, height) = (xMax - xMin, yMax - yMin); + var localX = ((IsXReversed(imageRotation, isMirrored) ? imageWidth - rectX : rectX) * width / imageWidth) + xMin; + var localY = ((IsYReversed(imageRotation, isMirrored) ? imageHeight - rectY : rectY) * height / imageHeight) + yMin; return new Vector3(localX, localY, z); } /// /// Convert from image coordinates to local coordinates in Unity. /// - /// - /// to be used for calculating local coordinates - /// + /// Rectangle to get a point inside /// Column value in the image coordinate system /// Row value in the image coordinate system - /// Depth value in local coordinate system - /// Image size in pixels - /// Counterclockwise rotation angle of the input image - /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(RectTransform rectTransform, int x, int y, int z, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + /// Depth value in the local coordinate system + /// Image width in pixels + /// Image width in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the image coordinates is mirrored + public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, int x, int y, int z, + int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPosition(rectTransform, x, y, z, new Vector2(rectTransform.rect.width, rectTransform.rect.height), imageRotation, isMirrored); + return ImageToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, imageWidth, imageHeight, imageRotation, isMirrored); } /// /// Convert from image coordinates to local coordinates in Unity. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// + /// The position in the image coordinate system. + /// If position.z is not zero, it's assumed to be the depth value in the local coordinate system. /// + /// Image size in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the image coordinates is mirrored + public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, Vector3Int position, + Vector2Int imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageToPoint(rectangle, position.x, position.y, position.z, imageSize.x, imageSize.y, imageRotation, isMirrored); + } + + /// + /// Convert from image coordinates to local coordinates in Unity. + /// + /// Rectangle to get a point inside /// Column value in the image coordinate system /// Row value in the image coordinate system + /// Image width in pixels + /// Image width in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the image coordinates is mirrored + public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, int x, int y, + int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageToLocalPoint(x, y, 0, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, imageWidth, imageHeight, imageRotation, isMirrored); + } + + /// + /// Convert from image coordinates to local coordinates in Unity. + /// + /// Rectangle to get a point inside + /// The position in the image coordinate system /// Image size in pixels - /// Counterclockwise rotation angle of the input image - /// Set to true if the original coordinates is mirrored - public static Vector2 GetLocalPosition(RectTransform rectTransform, int x, int y, Vector2 imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the image coordinates is mirrored + public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, Vector2Int position, Vector2Int imageSize, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPosition(rectTransform, x, y, 0, imageSize, imageRotation, isMirrored); + return ImageToPoint(rectangle, position.x, position.y, imageSize.x, imageSize.y, imageRotation, isMirrored); } /// /// Convert normalized values in the image coordinate system to local coordinate values in Unity. - /// If the normalized values are out of [0, 1], the return value will be outside 's rect. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. /// - /// - /// to be used for calculating local coordinates - /// /// Normalized x value in the image coordinate system /// Normalized y value in the image coordinate system /// Normalized z value in the image coordinate system + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle /// Ratio of Z value in image coordinates to local coordinates in Unity - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPositionNormalized(RectTransform rectTransform, float normalizedX, float normalizedY, float normalizedZ, float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 ImageNormalizedToLocalPoint(float normalizedX, float normalizedY, float normalizedZ, float xMin, float xMax, float yMin, float yMax, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - var rect = rectTransform.rect; var isInverted = IsInverted(imageRotation); var (nx, ny) = isInverted ? (normalizedY, normalizedX) : (normalizedX, normalizedY); - var x = IsXReversed(imageRotation, isMirrored) ? Mathf.LerpUnclamped(rect.xMax, rect.xMin, nx) : Mathf.LerpUnclamped(rect.xMin, rect.xMax, nx); - var y = IsYReversed(imageRotation, isMirrored) ? Mathf.LerpUnclamped(rect.yMax, rect.yMin, ny) : Mathf.LerpUnclamped(rect.yMin, rect.yMax, ny); + var x = IsXReversed(imageRotation, isMirrored) ? Mathf.LerpUnclamped(xMax, xMin, nx) : Mathf.LerpUnclamped(xMin, xMax, nx); + var y = IsYReversed(imageRotation, isMirrored) ? Mathf.LerpUnclamped(yMax, yMin, ny) : Mathf.LerpUnclamped(yMin, yMax, ny); var z = zScale * normalizedZ; return new Vector3(x, y, z); } /// /// Convert normalized values in the image coordinate system to local coordinate values in Unity. - /// If the normalized values are out of [0, 1], the return value will be outside 's rect. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// Normalized x value in the image coordinate system + /// Normalized y value in the image coordinate system + /// Normalized z value in the image coordinate system + /// Ratio of Z value in image coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// + /// Set to true if the original coordinates is mirrored + public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, float normalizedX, float normalizedY, float normalizedZ, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageNormalizedToLocalPoint(normalizedX, normalizedY, normalizedZ, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, zScale, imageRotation, isMirrored); + } + + /// + /// Convert normalized values in the image coordinate system to local coordinate values in Unity. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. + /// + /// Rectangle to get a point inside + /// The position in the image coordinate system + /// Ratio of Z value in image coordinates to local coordinates in Unity + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, Vector3 position, + float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageNormalizedToPoint(rectangle, position.x, position.y, position.z, zScale, imageRotation, isMirrored); + } + + /// + /// Convert normalized values in the image coordinate system to local coordinate values in Unity. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. + /// /// Normalized x value in the image coordinate system /// Normalized y value in the image coordinate system /// Normalized z value in the image coordinate system - /// Counterclockwise rotation angle of the input image + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// The minimum X coordinate of the target rectangle + /// The maximum X coordinate of the target rectangle + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPositionNormalized(RectTransform rectTransform, float normalizedX, float normalizedY, float normalizedZ, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 ImageNormalizedToLocalPoint(float normalizedX, float normalizedY, float normalizedZ, float xMin, float xMax, float yMin, float yMax, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { // Z usually uses roughly the same scale as X - var zScale = IsInverted(imageRotation) ? rectTransform.rect.height : rectTransform.rect.width; - return GetLocalPositionNormalized(rectTransform, normalizedX, normalizedY, normalizedZ, zScale, imageRotation, isMirrored); + var zScale = IsInverted(imageRotation) ? (yMax - yMin) : (xMax - xMin); + return ImageNormalizedToLocalPoint(normalizedX, normalizedY, normalizedZ, xMin, xMax, yMin, yMax, zScale, imageRotation, isMirrored); } /// /// Convert normalized values in the image coordinate system to local coordinate values in Unity. - /// If the normalized values are out of [0, 1], the return value will be outside 's rect. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// Normalized x value in the image coordinate system + /// Normalized y value in the image coordinate system + /// Normalized z value in the image coordinate system + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, float normalizedX, float normalizedY, float normalizedZ, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageNormalizedToLocalPoint(normalizedX, normalizedY, normalizedZ, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, imageRotation, isMirrored); + } + + /// + /// Convert normalized values in the image coordinate system to local coordinate values in Unity. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. + /// + /// Rectangle to get a point inside + /// The position in the image coordinate system + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// + /// Set to true if the original coordinates is mirrored + public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, Vector3 position, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageNormalizedToPoint(rectangle, position.x, position.y, position.z, imageRotation, isMirrored); + } + + /// + /// Convert normalized values in the image coordinate system to local coordinate values in Unity. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. + /// + /// Rectangle to get a point inside /// Normalized x value in the image coordinate system /// Normalized y value in the image coordinate system - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, float normalizedX, float normalizedY, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageNormalizedToPoint(rectangle, normalizedX, normalizedY, 0, imageRotation, isMirrored); + } + + /// + /// Convert normalized values in the image coordinate system to local coordinate values in Unity. + /// If the normalized values are out of [0, 1], the return value will be off the target screen. + /// + /// Rectangle to get a point inside + /// The position in the image coordinate system + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector2 GetLocalPositionNormalized(RectTransform rectTransform, float normalizedX, float normalizedY, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, Vector2 position, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPositionNormalized(rectTransform, normalizedX, normalizedY, 0.0f, imageRotation, isMirrored); + return ImageNormalizedToPoint(rectangle, position.x, position.y, imageRotation, isMirrored); } /// - /// Returns a Vector3 array which represents a rectangle's vertices. + /// Returns a array which represents a rectangle's vertices. /// They are in clockwise order, starting from the coordinate that was in the bottom-left. /// /// /// Z values are always zero. /// - /// - /// to be used for calculating local coordinates - /// + /// Rectangle to get a point inside /// Leftmost X value /// Topmost Y value - /// Image size in pixels - /// Counterclockwise rotation angle of the input image + /// Image width in pixels + /// Image width in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRectVertices(RectTransform rectTransform, int xMin, int yMin, int width, int height, Vector2 imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xMin, int yMin, int width, int height, + int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - var p = GetLocalPosition(rectTransform, xMin, yMin + height, imageSize, imageRotation, isMirrored); - var q = GetLocalPosition(rectTransform, xMin + width, yMin, imageSize, imageRotation, isMirrored); + var p = ImageToPoint(rectangle, xMin, yMin + height, imageWidth, imageHeight, imageRotation, isMirrored); + var q = ImageToPoint(rectangle, xMin + width, yMin, imageWidth, imageHeight, imageRotation, isMirrored); return GetRectVertices(p, q); } /// - /// Returns a Vector3 array which represents a rectangle's vertices. + /// Returns a array which represents a rectangle's vertices. /// They are in clockwise order, starting from the coordinate that was in the bottom-left. /// - /// - /// to be used for calculating local coordinates + /// + /// Z values are always zero. + /// + /// Rectangle to get a point inside + /// Leftmost X value + /// Topmost Y value + /// Image size in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// + /// Set to true if the original coordinates is mirrored + public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xMin, int yMin, int width, int height, + Vector2Int imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageToRectVertices(rectangle, xMin, yMin, width, height, imageSize.x, imageSize.y, imageRotation, isMirrored); + } + + /// + /// Returns a array which represents a rectangle's vertices. + /// They are in clockwise order, starting from the coordinate that was in the bottom-left. + /// + /// + /// Z values are always zero. + /// + /// Rectangle to get a point inside /// Normalized leftmost X value /// Normalized topmost Y value /// Normalized width /// Normalized height - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRectVerticesNormalized(RectTransform rectTransform, float normalizedXMin, float normalizedYMin, float normalizedWidth, float normalizedHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] ImageNormalizedToRectVertices(UnityEngine.Rect rectangle, float normalizedXMin, float normalizedYMin, float normalizedWidth, float normalizedHeight, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - var p = GetLocalPositionNormalized(rectTransform, normalizedXMin, normalizedYMin + normalizedHeight, imageRotation, isMirrored); - var q = GetLocalPositionNormalized(rectTransform, normalizedXMin + normalizedWidth, normalizedYMin, imageRotation, isMirrored); + var p = ImageNormalizedToPoint(rectangle, normalizedXMin, normalizedYMin + normalizedHeight, imageRotation, isMirrored); + var q = ImageNormalizedToPoint(rectangle, normalizedXMin + normalizedWidth, normalizedYMin, imageRotation, isMirrored); return GetRectVertices(p, q); } /// - /// Returns a Vector3 array which represents a rectangle's vertices. + /// Returns a array which represents a rectangle's vertices. /// They are in clockwise order, starting from the coordinate that was in the bottom-left before the rotation. /// - /// - /// to be used for calculating local coordinates - /// + /// Rectangle to get a point inside /// X value of the rectangle's center coordinate /// Y value of the rectangle's center coordinate /// Clockwise rotation angle in radians - /// Image size in pixels - /// Counterclockwise rotation angle of the input image + /// Image width in pixels + /// Image width in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRotatedRectVertices(RectTransform rectTransform, int xCenter, int yCenter, int width, int height, float rotation, Vector2 imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xCenter, int yCenter, int width, int height, float rotation, + int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { var isInverted = IsInverted(imageRotation); var (rectWidth, rectHeight) = IsInverted(imageRotation) ? (height, width) : (width, height); - Vector3 center = GetLocalPosition(rectTransform, xCenter, yCenter, imageSize, imageRotation, isMirrored); + var center = ImageToPoint(rectangle, xCenter, yCenter, imageWidth, imageHeight, imageRotation, isMirrored); var isRotationReversed = isInverted ^ IsXReversed(imageRotation, isMirrored) ^ IsYReversed(imageRotation, isMirrored); var quaternion = Quaternion.Euler(0, 0, (isRotationReversed ? -1 : 1) * Mathf.Rad2Deg * rotation); @@ -205,27 +390,48 @@ public static Vector3[] GetRotatedRectVertices(RectTransform rectTransform, int } /// - /// Returns a Vector3 array which represents a rectangle's vertices. + /// Returns a array which represents a rectangle's vertices. /// They are in clockwise order, starting from the coordinate that was in the bottom-left before the rotation. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// X value of the rectangle's center coordinate + /// Y value of the rectangle's center coordinate + /// Clockwise rotation angle in radians + /// Image size in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// + /// Set to true if the original coordinates is mirrored + public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xCenter, int yCenter, int width, int height, float rotation, + Vector2Int imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageToRectVertices(rectangle, xCenter, yCenter, width, height, rotation, imageSize.x, imageSize.y, imageRotation, isMirrored); + } + + /// + /// Returns a array which represents a rectangle's vertices. + /// They are in clockwise order, starting from the coordinate that was in the bottom-left before the rotation. + /// + /// Rectangle to get a point inside /// X value of the rectangle's center coordinate /// Y value of the rectangle's center coordinate /// Normalized width /// Normalized height /// Clockwise rotation angle in radians - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRotatedRectVerticesNormalized(RectTransform rectTransform, float normalizedXCenter, float normalizedYCenter, float normalizedWidth, float normalizedHeight, float rotation, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] ImageNormalizedToRectVertices(UnityEngine.Rect rectangle, float normalizedXCenter, float normalizedYCenter, float normalizedWidth, float normalizedHeight, + float rotation, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - var rect = rectTransform.rect; var isInverted = IsInverted(imageRotation); - var width = rect.width * (isInverted ? normalizedHeight : normalizedWidth); - var height = rect.height * (isInverted ? normalizedWidth : normalizedHeight); + var width = rectangle.width * (isInverted ? normalizedHeight : normalizedWidth); + var height = rectangle.height * (isInverted ? normalizedWidth : normalizedHeight); - Vector3 center = GetLocalPositionNormalized(rectTransform, normalizedXCenter, normalizedYCenter, imageRotation, isMirrored); + var center = ImageNormalizedToPoint(rectangle, normalizedXCenter, normalizedYCenter, imageRotation, isMirrored); var isRotationReversed = isInverted ^ IsXReversed(imageRotation, isMirrored) ^ IsYReversed(imageRotation, isMirrored); var quaternion = Quaternion.Euler(0, 0, (isRotationReversed ? -1 : 1) * Mathf.Rad2Deg * rotation); @@ -256,161 +462,188 @@ private static Vector3[] GetRectVertices(Vector2 p, Vector2 q) } /// - /// Get the coordinates represented by in local coordinate system. + /// Get the coordinates represented by in the local coordinate system. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// Counterclockwise rotation angle of the input image /// Set to true if the original coordinates is mirrored - public static Vector2 GetLocalPosition(this RectTransform rectTransform, mplt.RelativeKeypoint relativeKeypoint, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector2 GetPoint(this UnityEngine.Rect rectangle, mplt.RelativeKeypoint relativeKeypoint, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPositionNormalized(rectTransform, relativeKeypoint.X, relativeKeypoint.Y, imageRotation, isMirrored); + return ImageNormalizedToPoint(rectangle, relativeKeypoint.X, relativeKeypoint.Y, imageRotation, isMirrored); } /// - /// Get the coordinates represented by in local coordinate system. + /// Get the coordinates represented by in the local coordinate system. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// Counterclockwise rotation angle of the input image /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(this RectTransform rectTransform, NormalizedLandmark normalizedLandmark, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 GetPoint(this UnityEngine.Rect rectangle, NormalizedLandmark normalizedLandmark, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPositionNormalized(rectTransform, normalizedLandmark.X, normalizedLandmark.Y, normalizedLandmark.Z, imageRotation, isMirrored); + return ImageNormalizedToPoint(rectangle, normalizedLandmark.X, normalizedLandmark.Y, normalizedLandmark.Z, imageRotation, isMirrored); } /// - /// Get the coordinates represented by in local coordinate system. + /// Get the coordinates represented by in the local coordinate system. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// Counterclockwise rotation angle of the input image /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(this RectTransform rectTransform, NormalizedPoint2D point2d, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 GetPoint(this UnityEngine.Rect rectangle, NormalizedPoint2D point2d, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPositionNormalized(rectTransform, point2d.X, point2d.Y, imageRotation, isMirrored); + return ImageNormalizedToPoint(rectangle, point2d.X, point2d.Y, imageRotation, isMirrored); } /// - /// Get the coordinates represented by in local coordinate system. + /// Get the coordinates represented by in the local coordinate system. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// Counterclockwise rotation angle of the input image /// Set to true if the original coordinates is mirrored - public static Vector2 GetLocalPosition(this RectTransform rectTransform, Anchor3d anchor3d, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector2 GetPoint(this UnityEngine.Rect rectangle, Anchor3d anchor3d, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPositionNormalized(rectTransform, anchor3d.x, anchor3d.y, imageRotation, isMirrored); + return ImageNormalizedToPoint(rectangle, anchor3d.x, anchor3d.y, imageRotation, isMirrored); } /// - /// Get the coordinates represented by in local coordinate system. - /// This method calculates the coordinates of the anchor so that it is projected to the correct position on the plane when viewed from the . + /// Get a Vector3 array which represents 's vertex coordinates in the local coordinate system. + /// They are ordered clockwise from bottom-left point. /// - /// - /// Assume that the camera is oriented perpendicular to the plane. - /// - /// - /// that is attached to the target plane + /// Rectangle to get a point inside + /// Image width in pixels + /// Image width in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// The position of the camera represented in local coordinates of the plane - /// - /// Depth value when the anchor's Z is 1.0. - /// Depth here refers to the distance from the camera on the Z axis. - /// - /// Counterclockwise rotation angle of the input image /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(this RectTransform rectTransform, Anchor3d anchor3d, Vector3 cameraPosition, float defaultDepth, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, mplt.BoundingBox boundingBox, int imageWidth, int imageHeight, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - if (Mathf.Approximately(cameraPosition.z, 0.0f)) - { - throw new System.ArgumentException("Z value of the camera position must not be zero"); - } - - var cameraDepth = Mathf.Abs(cameraPosition.z); - var anchorPoint2d = rectTransform.GetLocalPosition(anchor3d, imageRotation, isMirrored); - var anchorDepth = anchor3d.z * defaultDepth; - - // Maybe it should be defined as a CameraCoordinate method - var x = ((anchorPoint2d.x - cameraPosition.x) * anchorDepth / cameraDepth) + cameraPosition.x; - var y = ((anchorPoint2d.y - cameraPosition.y) * anchorDepth / cameraDepth) + cameraPosition.y; - var z = cameraPosition.z > 0 ? cameraPosition.z - anchorDepth : cameraPosition.z + anchorDepth; - return new Vector3(x, y, z); + return ImageToRectVertices(rectangle, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageWidth, imageHeight, imageRotation, isMirrored); } /// - /// Get a Vector3 array which represents 's vertex coordinates in local coordinate system. + /// Get a Vector3 array which represents 's vertex coordinates in the local coordinate system. /// They are ordered clockwise from bottom-left point. /// - /// - /// to be used for calculating local coordinates - /// + /// Rectangle to get a point inside /// Image size in pixels - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRectVertices(this RectTransform rectTransform, mplt.BoundingBox boundingBox, Vector2 imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, mplt.BoundingBox boundingBox, Vector2Int imageSize, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetRectVertices(rectTransform, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageSize, imageRotation, isMirrored); + return ImageToRectVertices(rectangle, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageSize, imageRotation, isMirrored); } /// - /// Get a Vector3 array which represents 's vertex coordinates in local coordinate system. + /// Get a Vector3 array which represents 's vertex coordinates in the local coordinate system. /// They are ordered clockwise from bottom-left point. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// Counterclockwise rotation angle of the input image /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRectVertices(this RectTransform rectTransform, mplt.RelativeBoundingBox boundingBox, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, mplt.RelativeBoundingBox boundingBox, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetRectVerticesNormalized(rectTransform, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageRotation, isMirrored); + return ImageNormalizedToRectVertices(rectangle, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageRotation, isMirrored); } /// - /// Get a Vector3 array which represents 's vertex coordinates in local coordinate system. + /// Get a Vector3 array which represents 's vertex coordinates in the local coordinate system. /// They are ordered clockwise from bottom-left point. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// Image width in pixels + /// Image width in pixels + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// + /// Set to true if the original coordinates is mirrored + public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, Rect rect, int imageWidth, int imageHeight, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return ImageToRectVertices(rectangle, rect.XCenter, rect.YCenter, rect.Width, rect.Height, rect.Rotation, imageWidth, imageHeight, imageRotation, isMirrored); + } + + /// + /// Get a Vector3 array which represents 's vertex coordinates in the local coordinate system. + /// They are ordered clockwise from bottom-left point. + /// + /// Rectangle to get a point inside /// Image size in pixels - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRectVertices(this RectTransform rectTransform, Rect rect, Vector2 imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, Rect rect, Vector2Int imageSize, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetRotatedRectVertices(rectTransform, rect.XCenter, rect.YCenter, rect.Width, rect.Height, rect.Rotation, imageSize, imageRotation, isMirrored); + return ImageToRectVertices(rectangle, rect.XCenter, rect.YCenter, rect.Width, rect.Height, rect.Rotation, imageSize, imageRotation, isMirrored); } /// - /// Get a Vector3 array which represents 's vertex coordinates in local coordinate system. + /// Get a Vector3 array which represents 's vertex coordinates in the local coordinate system. /// They are ordered clockwise from bottom-left point. /// - /// - /// to be used for calculating local coordinates + /// Rectangle to get a point inside + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// - /// Counterclockwise rotation angle of the input image /// Set to true if the original coordinates is mirrored - public static Vector3[] GetRectVertices(this RectTransform rectTransform, NormalizedRect normalizedRect, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, NormalizedRect normalizedRect, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetRotatedRectVerticesNormalized(rectTransform, normalizedRect.XCenter, normalizedRect.YCenter, normalizedRect.Width, normalizedRect.Height, normalizedRect.Rotation, imageRotation, isMirrored); + return ImageNormalizedToRectVertices(rectangle, normalizedRect.XCenter, normalizedRect.YCenter, normalizedRect.Width, normalizedRect.Height, normalizedRect.Rotation, imageRotation, isMirrored); } - public static Vector2 GetNormalizedPosition(this RectTransform rectTransform, Vector2 localPosition, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + /// + /// Get the image normalized point corresponding to . + /// + /// Rectangle to get a point inside + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// + /// Set to true if the original coordinates is mirrored + public static Vector2 PointToImageNormalized(this UnityEngine.Rect rectangle, Vector2 localPosition, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - var rect = rectTransform.rect; - var normalizedX = IsXReversed(imageRotation, isMirrored) ? Mathf.InverseLerp(rect.width / 2, -rect.width / 2, localPosition.x) : Mathf.InverseLerp(-rect.width / 2, rect.width / 2, localPosition.x); - var normalizedY = IsYReversed(imageRotation, isMirrored) ? Mathf.InverseLerp(rect.height / 2, -rect.height / 2, localPosition.y) : Mathf.InverseLerp(-rect.height / 2, rect.height / 2, localPosition.y); + var normalizedX = IsXReversed(imageRotation, isMirrored) ? + Mathf.InverseLerp(rectangle.width / 2, -rectangle.width / 2, localPosition.x) : + Mathf.InverseLerp(-rectangle.width / 2, rectangle.width / 2, localPosition.x); + var normalizedY = IsYReversed(imageRotation, isMirrored) ? + Mathf.InverseLerp(rectangle.height / 2, -rectangle.height / 2, localPosition.y) : + Mathf.InverseLerp(-rectangle.height / 2, rectangle.height / 2, localPosition.y); return IsInverted(imageRotation) ? new Vector2(normalizedY, normalizedX) : new Vector2(normalizedX, normalizedY); } /// /// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates. - /// For example, if is and is False, this returns True + /// For example, if is and is false, this returns true /// because the original Y axis will be exactly opposite the X axis in Unity coordinates if the image is rotated back. /// public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = false) @@ -422,7 +655,7 @@ public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = fa /// /// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates. - /// For example, if is and is False, this returns True + /// For example, if is and is false, this returns true /// because the original X axis will be exactly opposite the Y axis in Unity coordinates if the image is rotated back. /// public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = false) @@ -432,6 +665,9 @@ public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = fa rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation90; } + /// + /// Returns true if is or . + /// public static bool IsInverted(RotationAngle rotationAngle) { return rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation270; diff --git a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/RealWorldCoordinate.cs b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/RealWorldCoordinate.cs index 6fb8e58a4..c0ed39356 100644 --- a/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/RealWorldCoordinate.cs +++ b/Packages/com.github.homuler.mediapipe/Runtime/Scripts/Unity/CoordinateSystem/RealWorldCoordinate.cs @@ -18,16 +18,17 @@ public static class RealWorldCoordinate /// Convert from real world coordinates to Unity local coordinates. /// Assume that the origin is common to the two coordinate systems. /// - /// - /// to be used for calculating local coordinates - /// /// X in real world coordinates /// Y in real world coordinates /// Z in real world coordinates /// Ratio of real world coordinate values to local coordinate values - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(float x, float y, float z, Vector3 scale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 RealWorldToLocalPoint(float x, float y, float z, Vector3 scale, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { var (rx, ry) = IsInverted(imageRotation) ? (y, x) : (x, y); var realX = IsXReversed(imageRotation, isMirrored) ? -rx : rx; @@ -36,19 +37,45 @@ public static Vector3 GetLocalPosition(float x, float y, float z, Vector3 scale, } /// - /// Get the coordinates represented by in local coordinate system. + /// Convert from real world coordinates to Unity local coordinates. + /// Assume that the origin is common to the two coordinate systems. /// - /// - /// to be used for calculating local coordinates + /// X in real world coordinates + /// Y in real world coordinates + /// Z in real world coordinates + /// Ratio of real world coordinate X to local coordinate X + /// Ratio of real world coordinate Y to local coordinate Y + /// Ratio of real world coordinate Z to local coordinate Z + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. /// + /// Set to true if the original coordinates is mirrored + public static Vector3 RealWorldToLocalPoint(float x, float y, float z, float scaleX = 1, float scaleY = 1, float scaleZ = 1, + RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + { + return RealWorldToLocalPoint(x, y, z, new Vector3(scaleX, scaleY, scaleZ), imageRotation, isMirrored); + } + + /// + /// Get the coordinates represented by in the local coordinate system. + /// /// Ratio of real world coordinate values to local coordinate values - /// Counterclockwise rotation angle of the input image + /// + /// Counterclockwise rotation angle of the input image in the image coordinate system. + /// In the local coordinate system, this value will often represent a clockwise rotation angle. + /// /// Set to true if the original coordinates is mirrored - public static Vector3 GetLocalPosition(this RectTransform _, Landmark landmark, Vector3 scale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) + public static Vector3 GetPoint(this UnityEngine.Rect _, Landmark landmark, Vector3 scale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false) { - return GetLocalPosition(landmark.X, landmark.Y, landmark.Z, scale, imageRotation, isMirrored); + return RealWorldToLocalPoint(landmark.X, landmark.Y, landmark.Z, scale, imageRotation, isMirrored); } + /// + /// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates. + /// For example, if is and is false, this returns true + /// because the original Y axis will be exactly opposite the X axis in Unity coordinates if the image is rotated back. + /// public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = false) { return isMirrored ? @@ -56,6 +83,11 @@ public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = fa rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation180; } + /// + /// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates. + /// For example, if is and is false, this returns true + /// because the original X axis will be exactly opposite the Y axis in Unity coordinates if the image is rotated back. + /// public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = false) { return isMirrored ? @@ -63,6 +95,9 @@ public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = fa rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation90; } + /// + /// Returns true if is or . + /// public static bool IsInverted(RotationAngle rotationAngle) { return rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation270; diff --git a/Packages/com.github.homuler.mediapipe/Tests/EditMode/Unity/CoordinateSystem/ImageCoordinateTest.cs b/Packages/com.github.homuler.mediapipe/Tests/EditMode/Unity/CoordinateSystem/ImageCoordinateTest.cs index 2e971e119..8be702b9a 100644 --- a/Packages/com.github.homuler.mediapipe/Tests/EditMode/Unity/CoordinateSystem/ImageCoordinateTest.cs +++ b/Packages/com.github.homuler.mediapipe/Tests/EditMode/Unity/CoordinateSystem/ImageCoordinateTest.cs @@ -13,7 +13,7 @@ namespace Tests { public class ImageCoordinateTest { - #region GetLocalPosition + #region ImageToPoint [TestCase(640, 480, -160, 0, 10, RotationAngle.Rotation0, false, -480, 240, 10)] [TestCase(640, 480, 0, -160, 10, RotationAngle.Rotation0, false, -320, 400, 10)] [TestCase(640, 480, 800, 0, 10, RotationAngle.Rotation0, false, 480, 240, 10)] @@ -78,14 +78,12 @@ public class ImageCoordinateTest [TestCase(640, 480, 0, 800, 10, RotationAngle.Rotation270, true, 480, 240, 10)] [TestCase(640, 480, 640, 640, 10, RotationAngle.Rotation270, true, 320, -400, 10)] [TestCase(640, 480, 480, 800, 10, RotationAngle.Rotation270, true, 480, -240, 10)] - public void GetLocalPosition_ShouldReturnLocalPosition_When_ImageSizeIsNotSpecified(int width, int height, int x, int y, int z, RotationAngle imageRotation, bool isMirrored, float expectedX, float expectedY, float expectedZ) + public void ImageToPoint_ShouldReturnLocalPoint_When_ImageSizeIsSameAsScreenSize(int width, int height, int x, int y, int z, RotationAngle imageRotation, bool isMirrored, + float expectedX, float expectedY, float expectedZ) { - WithRectTransform((rectTransform) => - { - rectTransform.sizeDelta = new Vector2(width, height); - var result = ImageCoordinate.GetLocalPosition(rectTransform, x, y, z, imageRotation, isMirrored); - Assert.AreEqual(new Vector3(expectedX, expectedY, expectedZ), result); - }); + var rect = BuildRect(-width / 2, width / 2, -height / 2, height / 2); + var result = ImageCoordinate.ImageToPoint(rect, x, y, z, width, height, imageRotation, isMirrored); + Assert.AreEqual(new Vector3(expectedX, expectedY, expectedZ), result); } [TestCase(640, 480, 720, 540, -160, 0, 0, RotationAngle.Rotation0, false, -540, 270, 0)] @@ -120,18 +118,96 @@ public void GetLocalPosition_ShouldReturnLocalPosition_When_ImageSizeIsNotSpecif [TestCase(640, 480, 320, 240, 0, 800, 0, RotationAngle.Rotation270, false, 240, -120, 0)] [TestCase(640, 480, 320, 240, 640, 640, 0, RotationAngle.Rotation270, false, 160, 200, 0)] [TestCase(640, 480, 320, 240, 480, 800, 0, RotationAngle.Rotation270, false, 240, 120, 0)] - public void GetLocalPosition_ShouldReturnLocalPosition_When_ImageSizeIsSpecified(int imageWidth, int imageHeight, int width, int height, int x, int y, int z, RotationAngle imageRotation, bool isMirrored, float expectedX, float expectedY, float expectedZ) + public void ImageToPoint_ShouldReturnLocalPoint_When_ImageSizeIsNotSameAsScreenSize(int imageWidth, int imageHeight, int width, int height, int x, int y, int z, + RotationAngle imageRotation, bool isMirrored, float expectedX, float expectedY, float expectedZ) { - WithRectTransform((rectTransform) => - { - rectTransform.sizeDelta = new Vector2(width, height); - var result = ImageCoordinate.GetLocalPosition(rectTransform, x, y, z, new Vector2(imageWidth, imageHeight), imageRotation, isMirrored); - Assert.AreEqual(new Vector3(expectedX, expectedY, expectedZ), result); - }); + var rect = BuildRect(-width / 2, width / 2, -height / 2, height / 2); + var result = ImageCoordinate.ImageToPoint(rect, x, y, z, imageWidth, imageHeight, imageRotation, isMirrored); + Assert.AreEqual(new Vector3(expectedX, expectedY, expectedZ), result); + } + + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation0, false, 0, 480)] + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation0, true, 640, 480)] + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation90, false, 640, 480)] + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation90, true, 640, 0)] + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation180, false, 640, 0)] + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation180, true, 0, 0)] + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation270, false, 0, 0)] + [TestCase(640, 480, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation270, true, 0, 480)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation0, false, -320, 480)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation0, true, 320, 480)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation90, false, 320, 480)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation90, true, 320, 0)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation180, false, 320, 0)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation180, true, -320, 0)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation270, false, -320, 0)] + [TestCase(640, 480, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation270, true, -320, 480)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation0, false, -640, 480)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation0, true, 0, 480)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation90, false, 0, 480)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation90, true, 0, 0)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation180, false, 0, 0)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation180, true, -640, 0)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation270, false, -640, 0)] + [TestCase(640, 480, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation270, true, -640, 480)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation0, false, 0, 240)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation0, true, 640, 240)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation90, false, 640, 240)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation90, true, 640, -240)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation180, false, 640, -240)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation180, true, 0, -240)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation270, false, 0, -240)] + [TestCase(640, 480, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation270, true, 0, 240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation0, false, -320, 240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation0, true, 320, 240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation90, false, 320, 240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation90, true, 320, -240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation180, false, 320, -240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation180, true, -320, -240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation270, false, -320, -240)] + [TestCase(640, 480, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation270, true, -320, 240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation0, false, -640, 240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation0, true, 0, 240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation90, false, 0, 240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation90, true, 0, -240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation180, false, 0, -240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation180, true, -640, -240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation270, false, -640, -240)] + [TestCase(640, 480, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation270, true, -640, 240)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation0, false, 0, 0)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation0, true, 640, 0)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation90, false, 640, 0)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation90, true, 640, -480)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation180, false, 640, -480)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation180, true, 0, -480)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation270, false, 0, -480)] + [TestCase(640, 480, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation270, true, 0, 0)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation0, false, -320, 0)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation0, true, 320, 0)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation90, false, 320, 0)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation90, true, 320, -480)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation180, false, 320, -480)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation180, true, -320, -480)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation270, false, -320, -480)] + [TestCase(640, 480, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation270, true, -320, 0)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation0, false, -640, 0)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation0, true, 0, 0)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation90, false, 0, 0)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation90, true, 0, -480)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation180, false, 0, -480)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation180, true, -640, -480)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation270, false, -640, -480)] + [TestCase(640, 480, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation270, true, -640, 0)] + public void ImageToPoint_ShouldReturnLocalPoint_When_TheAnchorOfRectIsNotAtTheCenter(int width, int height, int x, int y, float xMin, float xMax, float yMin, float yMax, + RotationAngle imageRotation, bool isMirrored, float expectedX, float expectedY) + { + var rect = BuildRect(xMin, xMax, yMin, yMax); + var result = ImageCoordinate.ImageToPoint(rect, x, y, width, height, imageRotation, isMirrored); + Assert.AreEqual(new Vector3(expectedX, expectedY, 0), result); } #endregion - #region GetLocalPositionNormalized + #region ImageNormalizedToPoint [TestCase(640, 480, -0.25f, 0, 0, RotationAngle.Rotation0, false, -480, 240, 0)] [TestCase(640, 480, 0, -0.25f, 0, RotationAngle.Rotation0, false, -320, 360, 0)] [TestCase(640, 480, 1.25f, 0, 1, RotationAngle.Rotation0, false, 480, 240, 640)] @@ -196,14 +272,92 @@ public void GetLocalPosition_ShouldReturnLocalPosition_When_ImageSizeIsSpecified [TestCase(640, 480, 0, 1.25f, -1, RotationAngle.Rotation270, true, 480, 240, -480)] [TestCase(640, 480, 1.25f, 1, 0, RotationAngle.Rotation270, true, 320, -360, 0)] [TestCase(640, 480, 1, 1.25f, 0, RotationAngle.Rotation270, true, 480, -240, 0)] - public void GetLocalPositionNormalized_ShouldReturnLocalPosition_When_ImageRotationAndIsMirroredAreSpecified(int width, int height, float normalizedX, float normalizedY, float normalizedZ, RotationAngle imageRotation, bool isMirrored, float expectedX, float expectedY, float expectedZ) + public void ImageNormalizedToPoint_ShouldReturnLocalPoint_When_TheAnchorOfRectIsAtTheCenter(int width, int height, float normalizedX, float normalizedY, float normalizedZ, + RotationAngle imageRotation, bool isMirrored, float expectedX, float expectedY, float expectedZ) { - WithRectTransform((rectTransform) => - { - rectTransform.sizeDelta = new Vector2(width, height); - var result = ImageCoordinate.GetLocalPositionNormalized(rectTransform, normalizedX, normalizedY, normalizedZ, imageRotation, isMirrored); - Assert.AreEqual(new Vector3(expectedX, expectedY, expectedZ), result); - }); + var rect = BuildRect(-width / 2, width / 2, -height / 2, height / 2); + var result = ImageCoordinate.ImageNormalizedToPoint(rect, normalizedX, normalizedY, normalizedZ, imageRotation, isMirrored); + Assert.AreEqual(new Vector3(expectedX, expectedY, expectedZ), result); + } + + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation0, false, 0, 480, 0)] + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation0, true, 640, 480, 0)] + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation90, false, 640, 480, 0)] + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation90, true, 640, 0, 0)] + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation180, false, 640, 0, 0)] + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation180, true, 0, 0, 0)] + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation270, false, 0, 0, 0)] + [TestCase(0, 0, 0, 0, 640, 0, 480, RotationAngle.Rotation270, true, 0, 480, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation0, false, -320, 480, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation0, true, 320, 480, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation90, false, 320, 480, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation90, true, 320, 0, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation180, false, 320, 0, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation180, true, -320, 0, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation270, false, -320, 0, 0)] + [TestCase(0, 0, 0, -320, 320, 0, 480, RotationAngle.Rotation270, true, -320, 480, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation0, false, -640, 480, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation0, true, 0, 480, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation90, false, 0, 480, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation90, true, 0, 0, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation180, false, 0, 0, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation180, true, -640, 0, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation270, false, -640, 0, 0)] + [TestCase(0, 0, 0, -640, 0, 0, 480, RotationAngle.Rotation270, true, -640, 480, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation0, false, 0, 240, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation0, true, 640, 240, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation90, false, 640, 240, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation90, true, 640, -240, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation180, false, 640, -240, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation180, true, 0, -240, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation270, false, 0, -240, 0)] + [TestCase(0, 0, 0, 0, 640, -240, 240, RotationAngle.Rotation270, true, 0, 240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation0, false, -320, 240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation0, true, 320, 240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation90, false, 320, 240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation90, true, 320, -240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation180, false, 320, -240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation180, true, -320, -240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation270, false, -320, -240, 0)] + [TestCase(0, 0, 0, -320, 320, -240, 240, RotationAngle.Rotation270, true, -320, 240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation0, false, -640, 240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation0, true, 0, 240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation90, false, 0, 240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation90, true, 0, -240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation180, false, 0, -240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation180, true, -640, -240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation270, false, -640, -240, 0)] + [TestCase(0, 0, 0, -640, 0, -240, 240, RotationAngle.Rotation270, true, -640, 240, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation0, false, 0, 0, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation0, true, 640, 0, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation90, false, 640, 0, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation90, true, 640, -480, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation180, false, 640, -480, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation180, true, 0, -480, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation270, false, 0, -480, 0)] + [TestCase(0, 0, 0, 0, 640, -480, 0, RotationAngle.Rotation270, true, 0, 0, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation0, false, -320, 0, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation0, true, 320, 0, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation90, false, 320, 0, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation90, true, 320, -480, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation180, false, 320, -480, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation180, true, -320, -480, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation270, false, -320, -480, 0)] + [TestCase(0, 0, 0, -320, 320, -480, 0, RotationAngle.Rotation270, true, -320, 0, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation0, false, -640, 0, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation0, true, 0, 0, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation90, false, 0, 0, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation90, true, 0, -480, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation180, false, 0, -480, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation180, true, -640, -480, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation270, false, -640, -480, 0)] + [TestCase(0, 0, 0, -640, 0, -480, 0, RotationAngle.Rotation270, true, -640, 0, 0)] + public void ImageNormalizedToPoint_ShouldReturnLocalPoint_When_TheAnchorOfRectIsNotAtTheCenter(float normalizedX, float normalizedY, float normalizedZ, float xMin, float xMax, float yMin, float yMax, + RotationAngle imageRotation, bool isMirrored, float expectedX, float expectedY, float expectedZ) + { + var rect = BuildRect(xMin, xMax, yMin, yMax); + var result = ImageCoordinate.ImageNormalizedToPoint(rect, normalizedX, normalizedY, normalizedZ, imageRotation, isMirrored); + Assert.AreEqual(new Vector3(expectedX, expectedY, expectedZ), result); } [TestCase(640, 480, 0, 0, 1, 100, RotationAngle.Rotation0, false, 100)] @@ -238,29 +392,40 @@ public void GetLocalPositionNormalized_ShouldReturnLocalPosition_When_ImageRotat [TestCase(640, 480, 0, 0, 0.5f, 100, RotationAngle.Rotation270, true, 50)] [TestCase(640, 480, 0, 0, -1, 100, RotationAngle.Rotation270, true, -100)] [TestCase(640, 480, 0, 0, -0.5f, 100, RotationAngle.Rotation270, true, -50)] - public void GetLocalPositionNormalized_ShouldScaleZ_When_ZScaleIsSpecified(int width, int height, float normalizedX, float normalizedY, float normalizedZ, float zScale, RotationAngle imageRotation, bool isMirrored, float expectedZ) + public void ImageNormalizedToPoint_ShouldReturnLocalPoint_When_ZScaleIsSpecified(int width, int height, float normalizedX, float normalizedY, float normalizedZ, float zScale, + RotationAngle imageRotation, bool isMirrored, float expectedZ) { - WithRectTransform((rectTransform) => - { - rectTransform.sizeDelta = new Vector2(width, height); - var result = ImageCoordinate.GetLocalPositionNormalized(rectTransform, normalizedX, normalizedY, normalizedZ, zScale, imageRotation, isMirrored); - Assert.AreEqual(expectedZ, result.z); - }); + var rect = BuildRect(-width / 2, width / 2, -height / 2, height / 2); + var result = ImageCoordinate.ImageNormalizedToPoint(rect, normalizedX, normalizedY, normalizedZ, zScale, imageRotation, isMirrored); + Assert.AreEqual(expectedZ, result.z); } #endregion - private void WithRectTransform(System.Action action) + private Rect BuildRect(float xMin, float xMax, float yMin, float yMax) { - var gameObject = new GameObject(); - var rectTransform = gameObject.AddComponent(); - rectTransform.pivot = 0.5f * Vector2.one; - rectTransform.anchorMin = 0.5f * Vector2.one; - rectTransform.anchorMax = 0.5f * Vector2.one; + var x = xMax < 0 ? xMin : -xMax; + var y = yMax < 0 ? yMin : -yMax; + var rect = new Rect(x, y, -2 * x, -2 * y); + + if (xMax < 0) + { + rect.xMax = xMax; + } + else + { + rect.xMin = xMin; + } + + if (yMax < 0) + { + rect.yMax = yMax; + } + else + { + rect.yMin = yMin; + } - action(rectTransform); -#pragma warning disable IDE0002 - GameObject.DestroyImmediate(gameObject); -#pragma warning restore IDE0002 + return rect; } } }