/// <summary>
        /// Pass in the game object's position to check if it's within
        /// the specified boundary space.
        /// </summary>
        /// <param name="gameObjectPosition">The position of the GameObject to check.</param>
        /// <param name="boundaryType">The type of the boundary. Use PlayArea for the inscribed rectangle or TrackedArea for the bounds containing the whole space.</param>
        /// <returns>True if the point is in the boundary type's bounds.</returns>
        public bool ContainsObject(Vector3 gameObjectPosition, UnityEngine.Experimental.XR.Boundary.Type boundaryType)
        {
            gameObjectPosition = CameraCache.Main.transform.parent.InverseTransformPoint(gameObjectPosition);

            if (gameObjectPosition.y < boundaryFloor || gameObjectPosition.y > boundaryHeight)
            {
                return(false);
            }

            if (boundaryType == UnityEngine.Experimental.XR.Boundary.Type.PlayArea)
            {
                if (inscribedRectangle == null || !inscribedRectangle.IsRectangleValid)
                {
                    return(false);
                }

                return(inscribedRectangle.IsPointInRectangleBounds(new Vector2(gameObjectPosition.x, gameObjectPosition.z)));
            }
            else if (boundaryType == UnityEngine.Experimental.XR.Boundary.Type.TrackedArea)
            {
                // Check if the supplied game object's position is within the bounds volume.
                return(EdgeHelpers.IsInside(boundaryGeometryEdges, new Vector2(gameObjectPosition.x, gameObjectPosition.z)));
            }

            return(false);
        }
Exemple #2
0
        /// <summary>
        /// Finds a large inscribed rectangle. Tries to be maximal but this is
        /// best effort. The algorithm used was inspired by the blog post
        /// https://d3plus.org/blog/behind-the-scenes/2014/07/08/largest-rect/
        /// Random points within the polygon are chosen, and then 2 lines are
        /// drawn through those points. The midpoints of those lines are
        /// used as the center of various rectangles, using a binary search to
        /// vary the size, until the largest fit-able rectangle is found.
        /// This is then repeated for predefined angles (0-180 in steps of 15)
        /// and aspect ratios (1 to 15 in steps of 0.5).
        /// </summary>
        private void FindInscribedRectangle(Edge[] edges, int randomSeed, out Vector2 center, out float angle, out float width, out float height)
        {
            center = EdgeHelpers.InvalidPoint;
            angle  = width = height = 0;

            // Find min x, min y, max x, max y and generate random
            // points in this range until we have randomPointCount
            // random starting points
            float minX = largeValue;
            float minY = largeValue;
            float maxX = smallValue;
            float maxY = smallValue;

            foreach (var edge in edges)
            {
                if (edge.Ax < minX || edge.Bx < minX)
                {
                    minX = Math.Min(edge.Ax, edge.Bx);
                }
                if (edge.Ay < minY || edge.By < minY)
                {
                    minY = Math.Min(edge.Ay, edge.By);
                }
                if (edge.Ax > maxX || edge.Bx > maxX)
                {
                    maxX = Math.Max(edge.Ax, edge.Bx);
                }
                if (edge.Ay > maxY || edge.By > maxY)
                {
                    maxY = Math.Max(edge.Ay, edge.By);
                }
            }

            // Generate random points
            Vector2[] startingPoints = new Vector2[randomPointCount];
            {
                var random = new System.Random(randomSeed);

                for (int pointIndex = 0; pointIndex < randomPointCount; ++pointIndex)
                {
                    Vector2 candidatePoint;

                    do
                    {
                        candidatePoint.x = ((float)random.NextDouble() * (maxX - minX)) + minX;
                        candidatePoint.y = ((float)random.NextDouble() * (maxY - minY)) + minY;
                    }while (!EdgeHelpers.IsInside(edges, candidatePoint));

                    startingPoints[pointIndex] = candidatePoint;
                }
            }

            float[] angles = { 0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165 };

            foreach (var candidateAngle in angles)
            {
                // For each randomly generated starting point
                foreach (var startingPoint in startingPoints)
                {
                    // Find the collision point of a cross through the given point at the given angle
                    // Ignore the return value. A return value of false indicates one of the points is bad.
                    // We will check each point's validity ourselves anyway.
                    Vector2 topCollisionPoint;
                    Vector2 bottomCollisionPoint;
                    Vector2 leftCollisionPoint;
                    Vector2 rightCollisionPoint;
                    FindSurroundingCollisionPoints(edges, startingPoint, DegreesToRadians(candidateAngle),
                                                   out topCollisionPoint, out bottomCollisionPoint, out leftCollisionPoint, out rightCollisionPoint);

                    // Now calculate the midpoint between top and bottom (the "vertical midpoint") and left and right (the "horizontal midpoint")
                    if (EdgeHelpers.IsValidPoint(topCollisionPoint) && EdgeHelpers.IsValidPoint(bottomCollisionPoint))
                    {
                        var verticalMidpoint = new Vector2((topCollisionPoint.x + bottomCollisionPoint.x) / 2,
                                                           (topCollisionPoint.y + bottomCollisionPoint.y) / 2);
                        float aspectRatio;
                        float w;
                        float h;
                        if (TryFitMaximumRectangleAtAngle(edges, verticalMidpoint, DegreesToRadians(candidateAngle), width * height, out aspectRatio, out w, out h))
                        {
                            center = verticalMidpoint;
                            angle  = candidateAngle;
                            width  = w;
                            height = h;
                        }
                    }

                    if (EdgeHelpers.IsValidPoint(leftCollisionPoint) && EdgeHelpers.IsValidPoint(rightCollisionPoint))
                    {
                        var horizontalMidpoint = new Vector2((leftCollisionPoint.x + rightCollisionPoint.x) / 2,
                                                             (leftCollisionPoint.y + rightCollisionPoint.y) / 2);
                        float aspectRatio;
                        float w;
                        float h;
                        if (TryFitMaximumRectangleAtAngle(edges, horizontalMidpoint, DegreesToRadians(candidateAngle), width * height, out aspectRatio, out w, out h))
                        {
                            center = horizontalMidpoint;
                            angle  = candidateAngle;
                            width  = w;
                            height = h;
                        }
                    }
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Given a point inside of the boundary, finds the points on the
        /// boundary directly above, below, left and right of the point,
        /// with respect to the given angle.
        /// </summary>
        private bool FindSurroundingCollisionPoints(
            Edge[] edges,
            Vector2 point, float angleRad, out Vector2 topCollisionPoint,
            out Vector2 bottomCollisionPoint, out Vector2 leftCollisionPoint,
            out Vector2 rightCollisionPoint)
        {
            topCollisionPoint    = EdgeHelpers.InvalidPoint;
            bottomCollisionPoint = EdgeHelpers.InvalidPoint;
            leftCollisionPoint   = EdgeHelpers.InvalidPoint;
            rightCollisionPoint  = EdgeHelpers.InvalidPoint;

            bool isInside = EdgeHelpers.IsInside(edges, point);

            if (!isInside)
            {
                return(false);
            }

            // Find the top and bottom collision points by creating a large line segment that goes through the point to MAX and MIN values on Y
            var topEndpoint    = new Vector2(point.x, largeValue);
            var bottomEndpoint = new Vector2(point.x, smallValue);

            topEndpoint    = RotatePoint(point, angleRad, topEndpoint);
            bottomEndpoint = RotatePoint(point, angleRad, bottomEndpoint);
            var verticalLine = new Edge(topEndpoint, bottomEndpoint);
            // Find the left and right collision points by creating a large line segment that goes through the point to MAX and Min values on X
            var rightEndpoint = new Vector2(largeValue, point.y);
            var leftEndpoint  = new Vector2(smallValue, point.y);

            rightEndpoint = RotatePoint(point, angleRad, rightEndpoint);
            leftEndpoint  = RotatePoint(point, angleRad, leftEndpoint);
            var horizontalLine = new Edge(rightEndpoint, leftEndpoint);

            // Loop the edges and find the nearest intersection point
            foreach (var edge in edges)
            {
                Vector2 verticalIntersection = EdgeHelpers.GetIntersection(edge, verticalLine);
                if (EdgeHelpers.IsValidPoint(verticalIntersection))
                {
                    // Is this intersection above or below the point?
                    bool isAbove = RotatePoint(point, -angleRad, verticalIntersection).y > point.y;
                    if (isAbove)
                    {
                        // If this collision point is closer than the previous one
                        if (!EdgeHelpers.IsValidPoint(topCollisionPoint) ||
                            Vector2.SqrMagnitude(point - verticalIntersection) < Vector2.SqrMagnitude(point - topCollisionPoint))
                        {
                            topCollisionPoint = verticalIntersection;
                        }
                    }
                    else
                    {
                        if (!EdgeHelpers.IsValidPoint(bottomCollisionPoint) ||
                            Vector2.SqrMagnitude(point - verticalIntersection) < Vector2.SqrMagnitude(point - bottomCollisionPoint))
                        {
                            bottomCollisionPoint = verticalIntersection;
                        }
                    }
                }  // If vertical intersection found

                Vector2 horizontalIntersection = EdgeHelpers.GetIntersection(edge, horizontalLine);
                if (EdgeHelpers.IsValidPoint(horizontalIntersection))
                {
                    // Is this intersection left or right of the point?
                    bool isLeft = RotatePoint(point, -angleRad, horizontalIntersection).x < point.x;
                    if (isLeft)
                    {
                        // Is it closer than the previous intersection?
                        if (!EdgeHelpers.IsValidPoint(leftCollisionPoint) ||
                            Vector2.SqrMagnitude(point - horizontalIntersection) < Vector2.SqrMagnitude(point - leftCollisionPoint))
                        {
                            leftCollisionPoint = horizontalIntersection;
                        }
                    }
                    else
                    {
                        // Is it closer than the previous intersection?
                        if (!EdgeHelpers.IsValidPoint(rightCollisionPoint) ||
                            Vector2.SqrMagnitude(point - horizontalIntersection) < Vector2.SqrMagnitude(point - rightCollisionPoint))
                        {
                            rightCollisionPoint = horizontalIntersection;
                        }
                    }
                }
            }

            // Assert that any point inside should have intersection points on all sides with the polygon
            if (!EdgeHelpers.IsValidPoint(topCollisionPoint) || !EdgeHelpers.IsValidPoint(bottomCollisionPoint) ||
                !EdgeHelpers.IsValidPoint(leftCollisionPoint) || !EdgeHelpers.IsValidPoint(rightCollisionPoint))
            {
                return(false);
            }
            return(true);
        }