/****************************************************************************** * Copyright (C) Ultraleap, Inc. 2011-2021. * * * * Use subject to the terms of the Apache License 2.0 available at * * http://www.apache.org/licenses/LICENSE-2.0, or another agreement * * between Ultraleap and you, your company or other organization. * ******************************************************************************/ using Leap.Unity.Infix; using Leap.Unity.RuntimeGizmos; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Leap.Unity.Geometry { using UnityRect = UnityEngine.Rect; public struct Rect { public static readonly Vector3 PLANE_NORMAL = new Vector3(0, 0, -1); public static readonly Color DEFAULT_GIZMO_COLOR = LeapColor.cerulean; public Vector3 center; public Vector2 radii; public Transform transform; public Matrix4x4? overrideMatrix; public Rect(LocalRect localRect, Transform transform) : this(localRect.center, localRect.radii, transform) { } public Rect(Vector3 center, Vector2 radii, Transform transform = null) { this.center = center; this.radii = radii; this.transform = transform; overrideMatrix = null; } public Rect(Vector3 center, float radius, Transform transform = null) { this.center = center; this.radii = Vector2.one * radius; this.transform = transform; overrideMatrix = null; } public Rect(Vector2 radii, Transform transform = null) : this(center: default(Vector3), radii: radii, transform: transform) { } /// /// Local-to-world matrix for this Rect. (Read only.) /// public Matrix4x4 matrix { get { if (overrideMatrix.HasValue) { return overrideMatrix.Value * Matrix4x4.Translate(center); } if (transform == null) { return Matrix4x4.Translate(center); } return transform.localToWorldMatrix * Matrix4x4.Translate(center); } } /// /// Local-to-world pose for the center of this Rect. (Read only.) /// /// This extracts a Pose from the attached matrix, eliminating scale information. /// public Pose pose { get { return matrix.GetPose(); } } public Vector3 localCorner00 { get { return new Vector3(-radii.x, -radii.y); } } public Vector3 localCorner01 { get { return new Vector3(-radii.x, radii.y); } } public Vector3 localCorner11 { get { return new Vector3(radii.x, radii.y); } } public Vector3 localCorner10 { get { return new Vector3(radii.x, -radii.y); } } /// /// Returns whether the given world-space point projects inside this Rect. Optionally /// also outputs the calculated rect-space point to point_rect. /// public bool ContainsProjectedPoint(Vector3 point) { Vector3 unusedPoint_rect; return ContainsProjectedPoint(point, out unusedPoint_rect); } /// /// Returns whether the given world-space point projects inside this Rect. Optionally /// also outputs the calculated rect-space point to point_rect. /// public bool ContainsProjectedPoint(Vector3 point, out Vector3 point_rect) { point_rect = this.matrix.inverse.MultiplyPoint3x4(point); var absPoint_rect = point_rect.Abs(); return absPoint_rect.x <= radii.x && absPoint_rect.y <= radii.y; } #region Corner Enumerator public RectWorldCornerEnumerator corners { get { return new RectWorldCornerEnumerator(this); } } public struct RectWorldCornerEnumerator { private int _sideIdx; private Vector3 corner00; private Vector3 corner01; private Vector3 corner11; private Vector3 corner10; public RectWorldCornerEnumerator(Rect rect) { //this.rect = rect; _sideIdx = -1; corner00 = rect.matrix.MultiplyPoint3x4(rect.localCorner00); corner01 = rect.matrix.MultiplyPoint3x4(rect.localCorner01); corner11 = rect.matrix.MultiplyPoint3x4(rect.localCorner11); corner10 = rect.matrix.MultiplyPoint3x4(rect.localCorner10); } public RectWorldCornerEnumerator GetEnumerator() { return this; } public Vector3 Current { get { switch (_sideIdx) { case 0: return corner00; case 1: return corner01; case 2: return corner11; case 3: return corner10; default: return default(Vector3); } } } public bool MoveNext() { _sideIdx += 1; return _sideIdx < 4; } } public RectLocalCornerEnumerator localCorners { get { return new RectLocalCornerEnumerator(this); } } public struct RectLocalCornerEnumerator { private int _sideIdx; private Vector3 corner00; private Vector3 corner01; private Vector3 corner11; private Vector3 corner10; public RectLocalCornerEnumerator(Rect rect) { //this.rect = rect; _sideIdx = -1; corner00 = rect.localCorner00; corner01 = rect.localCorner01; corner11 = rect.localCorner11; corner10 = rect.localCorner10; } public RectLocalCornerEnumerator GetEnumerator() { return this; } public Vector3 Current { get { switch (_sideIdx) { case 0: return corner00; case 1: return corner01; case 2: return corner11; case 3: return corner10; default: return default(Vector3); } } } public bool MoveNext() { _sideIdx += 1; return _sideIdx < 4; } } #endregion #region Side Enumerator public RectWorldSegmentEnumerator segments { get { return new RectWorldSegmentEnumerator(this); } } public struct RectWorldSegmentEnumerator { //Rect rect; private int _sideIdx; private Vector3 corner00; private Vector3 corner01; private Vector3 corner11; private Vector3 corner10; public RectWorldSegmentEnumerator(Rect rect) { //this.rect = rect; _sideIdx = -1; corner00 = rect.matrix.MultiplyPoint3x4(rect.localCorner00); corner01 = rect.matrix.MultiplyPoint3x4(rect.localCorner01); corner11 = rect.matrix.MultiplyPoint3x4(rect.localCorner11); corner10 = rect.matrix.MultiplyPoint3x4(rect.localCorner10); } public RectWorldSegmentEnumerator GetEnumerator() { return this; } public LocalSegment3 Current { get { switch (_sideIdx) { case 0: return new LocalSegment3(corner00, corner01); case 1: return new LocalSegment3(corner01, corner11); case 2: return new LocalSegment3(corner11, corner10); case 3: return new LocalSegment3(corner10, corner00); default: return new LocalSegment3(); } } } public bool MoveNext() { _sideIdx += 1; return _sideIdx < 4; } } public RectLocalSegmentEnumerator localSegments { get { return new RectLocalSegmentEnumerator(this); } } public struct RectLocalSegmentEnumerator { //Rect rect; private int _sideIdx; private Vector3 corner00; private Vector3 corner01; private Vector3 corner11; private Vector3 corner10; public RectLocalSegmentEnumerator(Rect rect) { //this.rect = rect; _sideIdx = -1; corner00 = rect.localCorner00; corner01 = rect.localCorner01; corner11 = rect.localCorner11; corner10 = rect.localCorner10; } public RectLocalSegmentEnumerator GetEnumerator() { return this; } public LocalSegment3 Current { get { switch (_sideIdx) { case 0: return new LocalSegment3(corner00, corner01); case 1: return new LocalSegment3(corner01, corner11); case 2: return new LocalSegment3(corner11, corner10); case 3: return new LocalSegment3(corner10, corner00); default: return new LocalSegment3(); } } } public bool MoveNext() { _sideIdx += 1; return _sideIdx < 4; } } #endregion #region Runtime Gizmos public void DrawLines(Action drawLineFunc, int divisions = 0) { Vector3 b = localCorner01, c = localCorner11, a = localCorner00, d = localCorner10; a = matrix.MultiplyPoint3x4(a); b = matrix.MultiplyPoint3x4(b); c = matrix.MultiplyPoint3x4(c); d = matrix.MultiplyPoint3x4(d); divisions = Mathf.Max(1, divisions); if (divisions > 1) { var frac = 1 / divisions; drawDividedLines(drawLineFunc, step: frac, a: a, b: b); drawDividedLines(drawLineFunc, step: frac, a: b, b: c); drawDividedLines(drawLineFunc, step: frac, a: c, b: d); drawDividedLines(drawLineFunc, step: frac, a: d, b: a); } else { drawLineFunc(a, b); drawLineFunc(b, c); drawLineFunc(c, d); drawLineFunc(d, a); } } private void drawDividedLines(System.Action draw, float step, Vector3 a, Vector3 b) { step = Mathf.Max(0.01f, step); var a_t = a; for (var t = step; t <= 1f; t += step) { var b_t = Vector3.Lerp(a, b, t); draw(a_t, b_t); a_t = b_t; } } public void DrawRuntimeGizmos(RuntimeGizmoDrawer drawer) { drawer.PushMatrix(); drawer.matrix = this.matrix; Vector3 b = localCorner01, c = localCorner11, a = localCorner00, d = localCorner10; Vector2 shrink; var invRadii = Vector3.one.CompDiv(radii.Abs()).NaNOrInfTo(0f); if (radii == Vector2.zero) { shrink = Vector2.zero; } else { shrink = Vector2.one * (radii * 0.02f).Abs().CompMin(); } int numRectLines = 8; for (int i = 0; i < numRectLines; i++) { var shrinkMul = (Vector2.one - shrink.CompMul(invRadii) * i); drawer.DrawLine(shrinkMul.CompMul(a), shrinkMul.CompMul(b)); drawer.DrawLine(shrinkMul.CompMul(b), shrinkMul.CompMul(c)); drawer.DrawLine(shrinkMul.CompMul(c), shrinkMul.CompMul(d)); drawer.DrawLine(shrinkMul.CompMul(d), shrinkMul.CompMul(a)); } drawer.DrawLine(a, c); drawer.DrawLine(b, d); //drawer.matrix = Matrix4x4.Scale(new Vector3(1f, 1f, 0f)) * this.matrix; //drawer.DrawCube(Vector3.zero, new Vector3(radii.x, radii.y, 1f)); drawer.PopMatrix(); } #endregion } }