Пример #1
0
        /// <summary>
        /// Determine of the provided point lies within the defined rectangle.
        /// </summary>
        /// <param name="point">The point to check</param>
        /// <returns>
        /// True if the point is within the rectangle's bounds, false otherwise.
        /// </returns>
        /// <exception cref="InvalidOperationException">The rectangle is not valid.</exception>
        public bool IsInsideBoundary(Vector2 point)
        {
            if (!IsValid)
            {
                throw new InvalidOperationException("A point cannot be within an invalid rectangle.");
            }

            return(EdgeUtilities.IsInsideBoundary(Edges, point));
        }
        /// <inheritdoc/>
        public bool Contains(Vector3 location, UnityBoundary.Type boundaryType = UnityBoundary.Type.TrackedArea)
        {
            if (!EdgeUtilities.IsValidPoint(location))
            {
                // Invalid location.
                return(false);
            }

            if (!FloorHeight.HasValue)
            {
                // No floor.
                return(false);
            }

            // Handle the user teleporting (boundary moves with them).
            location = MixedRealityPlayspace.InverseTransformPoint(location);

            if (FloorHeight.Value > location.y ||
                BoundaryHeight < location.y)
            {
                // Location below the floor or above the boundary height.
                return(false);
            }

            // Boundary coordinates are always "on the floor"
            Vector2 point = new Vector2(location.x, location.z);

            if (boundaryType == UnityBoundary.Type.PlayArea)
            {
                // Check the inscribed rectangle.
                if (rectangularBounds != null)
                {
                    return(rectangularBounds.IsInsideBoundary(point));
                }
            }
            else if (boundaryType == UnityBoundary.Type.TrackedArea)
            {
                // Check the geometry
                return(EdgeUtilities.IsInsideBoundary(Bounds, point));
            }

            // Not in either boundary type.
            return(false);
        }
Пример #3
0
        /// <summary>
        /// Find points at which there are collisions with the geometry around a given point.
        /// </summary>
        /// <param name="geometryEdges">The boundary geometry.</param>
        /// <param name="point">The point around which collisions will be identified.</param>
        /// <param name="angleRadians">The angle, in radians, at which the collision points will be oriented.</param>
        /// <param name="topCollisionPoint">Receives the coordinates of the upper collision point.</param>
        /// <param name="bottomCollisionPoint">Receives the coordinates of the lower collision point.</param>
        /// <param name="leftCollisionPoint">Receives the coordinates of the left collision point.</param>
        /// <param name="rightCollisionPoint">Receives the coordinates of the right collision point.</param>
        /// <returns>
        /// True if all of the required collision points are located, false otherwise.
        /// If a point is unable to be found, the appropriate out parameter will be set to <see cref="EdgeUtilities.InvalidPoint"/>.
        /// </returns>
        private static bool FindSurroundingCollisionPoints(
            Edge[] geometryEdges,
            Vector2 point,
            float angleRadians,
            out Vector2 topCollisionPoint,
            out Vector2 bottomCollisionPoint,
            out Vector2 leftCollisionPoint,
            out Vector2 rightCollisionPoint)
        {
            // Initialize out parameters.
            topCollisionPoint    = EdgeUtilities.InvalidPoint;
            bottomCollisionPoint = EdgeUtilities.InvalidPoint;
            leftCollisionPoint   = EdgeUtilities.InvalidPoint;
            rightCollisionPoint  = EdgeUtilities.InvalidPoint;

            // Check to see if the point is inside the geometry.
            if (!EdgeUtilities.IsInsideBoundary(geometryEdges, point))
            {
                return(false);
            }

            // Define values that are outside of the maximum boundary size.
            float largeValue = EdgeUtilities.MaxWidth;
            float smallValue = -largeValue;

            // 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    = topEndpoint.RotateAroundPoint(point, angleRadians);
            bottomEndpoint = bottomEndpoint.RotateAroundPoint(point, angleRadians);
            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 = rightEndpoint.RotateAroundPoint(point, angleRadians);
            leftEndpoint  = leftEndpoint.RotateAroundPoint(point, angleRadians);
            var horizontalLine = new Edge(rightEndpoint, leftEndpoint);

            for (int i = 0; i < geometryEdges.Length; i++)
            {
                // Look for a vertical collision
                var verticalIntersectionPoint = EdgeUtilities.GetIntersectionPoint(geometryEdges[i], verticalLine);

                if (EdgeUtilities.IsValidPoint(verticalIntersectionPoint))
                {
                    // Is the intersection above or below the point?
                    if (verticalIntersectionPoint.RotateAroundPoint(point, -angleRadians).y > point.y)
                    {
                        // Update the top collision point
                        if (!EdgeUtilities.IsValidPoint(topCollisionPoint) ||
                            (Vector2.SqrMagnitude(point - verticalIntersectionPoint) < Vector2.SqrMagnitude(point - topCollisionPoint)))
                        {
                            topCollisionPoint = verticalIntersectionPoint;
                        }
                    }
                    else
                    {
                        // Update the bottom collision point
                        if (!EdgeUtilities.IsValidPoint(bottomCollisionPoint) ||
                            Vector2.SqrMagnitude(point - verticalIntersectionPoint) < Vector2.SqrMagnitude(point - bottomCollisionPoint))
                        {
                            bottomCollisionPoint = verticalIntersectionPoint;
                        }
                    }
                }

                // Look for a horizontal collision
                var horizontalIntersection = EdgeUtilities.GetIntersectionPoint(geometryEdges[i], horizontalLine);

                if (!EdgeUtilities.IsValidPoint(horizontalIntersection))
                {
                    continue;
                }

                // Is this intersection to the left or the right of the point?
                if (horizontalIntersection.RotateAroundPoint(point, -angleRadians).x < point.x)
                {
                    // Update the left collision point
                    if (!EdgeUtilities.IsValidPoint(leftCollisionPoint) ||
                        (Vector2.SqrMagnitude(point - horizontalIntersection) < Vector2.SqrMagnitude(point - leftCollisionPoint)))
                    {
                        leftCollisionPoint = horizontalIntersection;
                    }
                }
                else
                {
                    // Update the right collision point
                    if (!EdgeUtilities.IsValidPoint(rightCollisionPoint) ||
                        (Vector2.SqrMagnitude(point - horizontalIntersection) < Vector2.SqrMagnitude(point - rightCollisionPoint)))
                    {
                        rightCollisionPoint = horizontalIntersection;
                    }
                }
            }

            // Each corner of the rectangle must intersect with the geometry.
            return(EdgeUtilities.IsValidPoint(topCollisionPoint) &&
                   EdgeUtilities.IsValidPoint(leftCollisionPoint) &&
                   EdgeUtilities.IsValidPoint(rightCollisionPoint) &&
                   EdgeUtilities.IsValidPoint(bottomCollisionPoint));
        }
Пример #4
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>
        /// <param name="geometryEdges">The boundary geometry.</param>
        /// <param name="randomSeed">Random number generator seed.</param>
        /// <remarks>
        /// For the most reproducible results, use the same randomSeed value
        /// each time this method is called.
        /// </remarks>
        public InscribedRectangle(Edge[] geometryEdges, int randomSeed)
        {
            if (geometryEdges == null || geometryEdges.Length == 0)
            {
                Debug.LogError("InscribedRectangle requires an array of Edges. You passed in a null or empty array.");
                return;
            }

            Edges = geometryEdges;

            // Clear previous rectangle
            Center = EdgeUtilities.InvalidPoint;
            Width  = 0;
            Height = 0;
            Angle  = 0;

            float minX = EdgeUtilities.MaxWidth;
            float minY = EdgeUtilities.MaxWidth;
            float maxX = -EdgeUtilities.MaxWidth;
            float maxY = -EdgeUtilities.MaxWidth;

            // Find min x, min y, max x, max y
            for (int i = 0; i < geometryEdges.Length; i++)
            {
                var edge = geometryEdges[i];

                if ((edge.PointA.x < minX) || (edge.PointB.x < minX))
                {
                    minX = Mathf.Min(edge.PointA.x, edge.PointB.x);
                }

                if ((edge.PointA.y < minY) || (edge.PointB.y < minY))
                {
                    minY = Mathf.Min(edge.PointA.y, edge.PointB.y);
                }

                if ((edge.PointA.x > maxX) || (edge.PointB.x > maxX))
                {
                    maxX = Mathf.Max(edge.PointA.x, edge.PointB.x);
                }

                if ((edge.PointA.y > maxY) || (edge.PointB.y > maxY))
                {
                    maxY = Mathf.Min(edge.PointA.y, edge.PointB.y);
                }
            }

            // Generate random points until we have randomPointCount starting points
            var startingPoints = new Vector2[RANDOM_POINT_COUNT];
            var random         = new Random(randomSeed);

            for (int i = 0; i < startingPoints.Length; i++)
            {
                Vector2 candidatePoint;

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

                startingPoints[i] = candidatePoint;
            }

            for (int angleIndex = 0; angleIndex < FitAngles.Length; angleIndex++)
            {
                for (int pointIndex = 0; pointIndex < startingPoints.Length; pointIndex++)
                {
                    float angleRadians = MathUtilities.DegreesToRadians(FitAngles[angleIndex]);

                    // Find the collision point of a cross through the given point at the given angle.
                    // Note, we are ignoring the return value as we are checking each point's validity
                    // individually.
                    FindSurroundingCollisionPoints(
                        geometryEdges,
                        startingPoints[pointIndex],
                        angleRadians,
                        out var topCollisionPoint,
                        out var bottomCollisionPoint,
                        out var leftCollisionPoint,
                        out var rightCollisionPoint);

                    float newWidth;
                    float newHeight;

                    if (EdgeUtilities.IsValidPoint(topCollisionPoint) && EdgeUtilities.IsValidPoint(bottomCollisionPoint))
                    {
                        float aX = topCollisionPoint.x;
                        float aY = topCollisionPoint.y;
                        float bX = bottomCollisionPoint.x;
                        float bY = bottomCollisionPoint.y;

                        // Calculate the midpoint between the top and bottom collision points.
                        Vector2 verticalMidpoint = new Vector2((aX + bX) * 0.5f, (aY + bY) * 0.5f);
                        if (TryFixMaximumRectangle(
                                geometryEdges,
                                verticalMidpoint,
                                angleRadians,
                                Width * Height,
                                out newWidth,
                                out newHeight))
                        {
                            Center = verticalMidpoint;
                            Angle  = FitAngles[angleIndex];
                            Width  = newWidth;
                            Height = newHeight;
                        }
                    }

                    if (EdgeUtilities.IsValidPoint(leftCollisionPoint) && EdgeUtilities.IsValidPoint(rightCollisionPoint))
                    {
                        float aX = leftCollisionPoint.x;
                        float aY = leftCollisionPoint.y;
                        float bX = rightCollisionPoint.x;
                        float bY = rightCollisionPoint.y;

                        // Calculate the midpoint between the left and right collision points.
                        var horizontalMidpoint = new Vector2((aX + bX) * 0.5f, (aY + bY) * 0.5f);

                        if (TryFixMaximumRectangle(
                                geometryEdges,
                                horizontalMidpoint,
                                angleRadians,
                                Width * Height,
                                out newWidth,
                                out newHeight))
                        {
                            Center = horizontalMidpoint;
                            Angle  = FitAngles[angleIndex];
                            Width  = newWidth;
                            Height = newHeight;
                        }
                    }
                }
            }
        }