/// <inheritdoc/>
        public GameObject GetPlayAreaVisualization()
        {
            if (!Application.isPlaying)
            {
                return(null);
            }

            if (currentPlayAreaObject != null)
            {
                return(currentPlayAreaObject);
            }

            MixedRealityBoundaryVisualizationProfile profile = ConfigurationProfile as MixedRealityBoundaryVisualizationProfile;

            if (profile == null)
            {
                return(null);
            }

            // Get the rectangular bounds.
            Vector2 center;
            float   angle;
            float   width;
            float   height;

            if (!TryGetRectangularBoundsParams(out center, out angle, out width, out height))
            {
                // No rectangular bounds, therefore cannot create the play area.
                return(null);
            }

            // Render the rectangular bounds.
            if (!EdgeUtilities.IsValidPoint(center))
            {
                // Invalid rectangle / play area not found
                return(null);
            }

            currentPlayAreaObject       = GameObject.CreatePrimitive(PrimitiveType.Quad);
            currentPlayAreaObject.name  = "Play Area";
            currentPlayAreaObject.layer = PlayAreaPhysicsLayer;
            currentPlayAreaObject.transform.Translate(new Vector3(center.x, boundaryObjectRenderOffset, center.y));
            currentPlayAreaObject.transform.Rotate(new Vector3(90, -angle, 0));
            currentPlayAreaObject.transform.localScale = new Vector3(width, height, 1.0f);
            currentPlayAreaObject.GetComponent <Renderer>().sharedMaterial = profile.PlayAreaMaterial;

            currentPlayAreaObject.transform.parent = BoundaryVisualizationParent.transform;

            return(currentPlayAreaObject);
        }
        /// <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>
        /// Check to see if a rectangle centered at the specified point and oriented at
        /// the specified angle will fit within the geometry.
        /// </summary>
        /// <param name="geometryEdges">The boundary geometry.</param>
        /// <param name="centerPoint">The center point of the rectangle.</param>
        /// <param name="angleRadians">The orientation, in radians, of the rectangle.</param>
        /// <param name="width">The width of the rectangle.</param>
        /// <param name="height">The height of the rectangle.</param>
        private bool CheckRectangleFit(
            Edge[] geometryEdges,
            Vector2 centerPoint,
            float angleRadians,
            float width,
            float height)
        {
            float halfWidth  = width * 0.5f;
            float halfHeight = height * 0.5f;

            // Calculate the rectangle corners.
            Vector2 topLeft     = new Vector2(centerPoint.x - halfWidth, centerPoint.y + halfHeight);
            Vector2 topRight    = new Vector2(centerPoint.x + halfWidth, centerPoint.y + halfHeight);
            Vector2 bottomLeft  = new Vector2(centerPoint.x - halfWidth, centerPoint.y - halfHeight);
            Vector2 bottomRight = new Vector2(centerPoint.x + halfWidth, centerPoint.y - halfHeight);

            // Rotate the rectangle.
            topLeft     = RotatePoint(topLeft, centerPoint, angleRadians);
            topRight    = RotatePoint(topRight, centerPoint, angleRadians);
            bottomLeft  = RotatePoint(bottomLeft, centerPoint, angleRadians);
            bottomRight = RotatePoint(bottomRight, centerPoint, angleRadians);

            // Get the rectangle edges.
            Edge topEdge    = new Edge(topLeft, topRight);
            Edge rightEdge  = new Edge(topRight, bottomRight);
            Edge bottomEdge = new Edge(bottomLeft, bottomRight);
            Edge leftEdge   = new Edge(topLeft, bottomLeft);

            // Check for collisions with the boundary geometry. If any of our edges collide,
            // the rectangle will not fit within the playspace.
            for (int i = 0; i < geometryEdges.Length; i++)
            {
                if (EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], topEdge)) ||
                    EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], rightEdge)) ||
                    EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], bottomEdge)) ||
                    EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], leftEdge)))
                {
                    return(false);
                }
            }

            // No collisions found with the rectangle. Success!
            return(true);
        }
예제 #4
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));
        }
예제 #5
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;
                        }
                    }
                }
            }
        }