Пример #1
0
        /// <summary>
        /// Initializes a polygon with the specified vertices
        /// </summary>
        /// <param name="vertices">Vertices</param>
        /// <exception cref="ArgumentNullException">If vertices is null</exception>
        public Polygon2(Vector2[] vertices)
        {
            if (vertices == null)
            {
                throw new ArgumentNullException(nameof(vertices));
            }

            Vertices = vertices;

            Normals = new List <Vector2>();
            Vector2 tmp;

            for (int i = 1; i < vertices.Length; i++)
            {
                tmp = Math2.MakeStandardNormal(Vector2.Normalize(Math2.Perpendicular(vertices[i] - vertices[i - 1])));
                if (!Normals.Contains(tmp))
                {
                    Normals.Add(tmp);
                }
            }

            tmp = Math2.MakeStandardNormal(Vector2.Normalize(Math2.Perpendicular(vertices[0] - vertices[vertices.Length - 1])));
            if (!Normals.Contains(tmp))
            {
                Normals.Add(tmp);
            }

            var min = new Vector2(vertices[0].X, vertices[0].Y);
            var max = new Vector2(min.X, min.Y);

            for (int i = 1; i < vertices.Length; i++)
            {
                min.X = Math.Min(min.X, vertices[i].X);
                min.Y = Math.Min(min.Y, vertices[i].Y);
                max.X = Math.Max(max.X, vertices[i].X);
                max.Y = Math.Max(max.Y, vertices[i].Y);
            }
            AABB = new Rect2(min, max);

            Center = new Vector2(0, 0);
            foreach (var vert in Vertices)
            {
                Center += vert;
            }
            Center *= (1.0f / Vertices.Length);

            // Find longest axis
            float longestAxisLenSq = -1;

            for (int i = 1; i < vertices.Length; i++)
            {
                var vec = vertices[i] - vertices[i - 1];
                longestAxisLenSq = Math.Max(longestAxisLenSq, vec.LengthSquared());
            }
            longestAxisLenSq  = Math.Max(longestAxisLenSq, (vertices[0] - vertices[vertices.Length - 1]).LengthSquared());
            LongestAxisLength = (float)Math.Sqrt(longestAxisLenSq);

            // Area and lines
            float area = 0;

            Lines = new Line2[Vertices.Length];
            var last = Vertices[Vertices.Length - 1];

            for (int i = 0; i < Vertices.Length; i++)
            {
                var next = Vertices[i];
                Lines[i] = new Line2(last, next);
                area    += Math2.AreaOfTriangle(last, next, Center);
                last     = next;
            }
            Area = area;

            last = Vertices[Vertices.Length - 1];
            var centToLast            = (last - Center);
            var angLast               = Math.Atan2(centToLast.Y, centToLast.X);
            var cwCounter             = 0;
            var ccwCounter            = 0;
            var foundDefinitiveResult = false;

            for (int i = 0; i < Vertices.Length; i++)
            {
                var curr       = Vertices[i];
                var centToCurr = (curr - Center);
                var angCurr    = Math.Atan2(centToCurr.Y, centToCurr.X);

                var clockwise = angCurr < angLast;
                if (clockwise)
                {
                    cwCounter++;
                }
                else
                {
                    ccwCounter++;
                }

                Clockwise = clockwise;
                if (Math.Abs(angLast - angCurr) > Math2.DEFAULT_EPSILON)
                {
                    foundDefinitiveResult = true;
                    break;
                }

                last       = curr;
                centToLast = centToCurr;
                angLast    = angCurr;
            }
            if (!foundDefinitiveResult)
            {
                Clockwise = cwCounter > ccwCounter;
            }
        }
Пример #2
0
        /// <summary>
        /// Initializes a polygon with the specified vertices
        /// </summary>
        /// <param name="vertices">Vertices</param>
        /// <exception cref="ArgumentNullException">If vertices is null</exception>
        public Polygon2(Vector2[] vertices)
        {
            Vertices = vertices ?? throw new ArgumentNullException(nameof(vertices));

            Normals = new List <Vector2>();
            Vector2 tmp;

            for (int i = 1; i < vertices.Length; i++)
            {
                tmp = Math2.MakeStandardNormal(Vector2.Normalize(Math2.Perpendicular(vertices[i] - vertices[i - 1])));
                if (!Normals.Contains(tmp))
                {
                    Normals.Add(tmp);
                }
            }

            tmp = Math2.MakeStandardNormal(Vector2.Normalize(Math2.Perpendicular(vertices[0] - vertices[vertices.Length - 1])));
            if (!Normals.Contains(tmp))
            {
                Normals.Add(tmp);
            }

            var min = new Vector2(vertices[0].X, vertices[0].Y);
            var max = new Vector2(min.X, min.Y);

            for (int i = 1; i < vertices.Length; i++)
            {
                min.X = Math.Min(min.X, vertices[i].X);
                min.Y = Math.Min(min.Y, vertices[i].Y);
                max.X = Math.Max(max.X, vertices[i].X);
                max.Y = Math.Max(max.Y, vertices[i].Y);
            }
            AABB = new Rect2(min, max);

            _LongestAxisLength = -1;

            // Center, area, and lines
            TrianglePartition = new Triangle2[Vertices.Length - 2];
            float[] triangleSortKeys = new float[TrianglePartition.Length];
            float   area             = 0;

            Lines    = new Line2[Vertices.Length];
            Lines[0] = new Line2(Vertices[Vertices.Length - 1], Vertices[0]);
            var last = Vertices[0];

            Center = new Vector2(0, 0);
            for (int i = 1; i < Vertices.Length - 1; i++)
            {
                var next  = Vertices[i];
                var next2 = Vertices[i + 1];
                Lines[i] = new Line2(last, next);
                var tri = new Triangle2(new Vector2[] { Vertices[0], next, next2 });
                TrianglePartition[i - 1] = tri;
                triangleSortKeys[i - 1]  = -tri.Area;
                area   += tri.Area;
                Center += tri.Center * tri.Area;
                last    = next;
            }
            Lines[Vertices.Length - 1] = new Line2(Vertices[Vertices.Length - 2], Vertices[Vertices.Length - 1]);

            Array.Sort(triangleSortKeys, TrianglePartition);

            Area    = area;
            Center /= area;

            last = Vertices[Vertices.Length - 1];
            var centToLast            = (last - Center);
            var angLast               = Rotation2.Standardize((float)Math.Atan2(centToLast.Y, centToLast.X));
            var cwCounter             = 0;
            var ccwCounter            = 0;
            var foundDefinitiveResult = false;

            for (int i = 0; i < Vertices.Length; i++)
            {
                var curr       = Vertices[i];
                var centToCurr = (curr - Center);
                var angCurr    = Rotation2.Standardize((float)Math.Atan2(centToCurr.Y, centToCurr.X));


                var clockwise = (angCurr < angLast && (angCurr - angLast) < Math.PI) || (angCurr - angLast) > Math.PI;
                if (clockwise)
                {
                    cwCounter++;
                }
                else
                {
                    ccwCounter++;
                }

                Clockwise = clockwise;
                if (Math.Abs(angLast - angCurr) > Math2.DEFAULT_EPSILON)
                {
                    foundDefinitiveResult = true;
                    break;
                }

                last       = curr;
                centToLast = centToCurr;
                angLast    = angCurr;
            }
            if (!foundDefinitiveResult)
            {
                Clockwise = cwCounter > ccwCounter;
            }
        }
Пример #3
0
 /// <summary>
 /// Determines if the specified rectangle at pos1 intersects the specified polygon at pos2 with
 /// no rotation.
 /// </summary>
 /// <param name="rect">The rectangle</param>
 /// <param name="poly">The polygon</param>
 /// <param name="pos1">Origin of rectangle</param>
 /// <param name="pos2">Origin of polygon</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns>If rect at pos1 no rotation intersects poly at pos2</returns>
 public static bool Intersects(Rect2 rect, Polygon2 poly, Vector2 pos1, Vector2 pos2, bool strict)
 {
     return(Intersects(rect, poly, pos1, pos2, Rotation2.Zero, strict));
 }
Пример #4
0
 /// <summary>
 /// Determines the minimum translation vector to be applied to the rect to prevent
 /// intersection with the specified polygon, when they are at the given positions.
 /// </summary>
 /// <param name="rect">The rect</param>
 /// <param name="poly">The polygon</param>
 /// <param name="pos1">The origin of the rect</param>
 /// <param name="pos2">The origin of the polygon</param>
 /// <returns>MTV to move rect at pos1 to prevent overlap with poly at pos2</returns>
 public static Tuple <Vector2, float> IntersectMTV(Rect2 rect, Polygon2 poly, Vector2 pos1, Vector2 pos2)
 {
     return(IntersectMTV(rect, poly, pos1, pos2, Rotation2.Zero));
 }
Пример #5
0
        /// <summary>
        /// Determines the minimum translation vector to be applied to the circle to
        /// prevent overlap with the rectangle, when they are at their given positions.
        /// </summary>
        /// <param name="circle">The circle</param>
        /// <param name="rect">The rectangle</param>
        /// <param name="pos1">The top-left of the circles bounding box</param>
        /// <param name="pos2">The rectangles origin</param>
        /// <returns>MTV for circle at pos1 to prevent overlap with rect at pos2</returns>
        public static Tuple <Vector2, float> IntersectMTV(Circle2 circle, Rect2 rect, Vector2 pos1, Vector2 pos2)
        {
            // Same as polygon rect, just converted to rects points
            HashSet <Vector2> checkedAxis = new HashSet <Vector2>();

            Vector2 bestAxis        = Vector2.Zero;
            float   shortestOverlap = float.MaxValue;

            Func <Vector2, bool> checkAxis = (axis) =>
            {
                var standard = Math2.MakeStandardNormal(axis);
                if (!checkedAxis.Contains(standard))
                {
                    checkedAxis.Add(standard);
                    var circleProj = Circle2.ProjectAlongAxis(circle, pos1, axis);
                    var rectProj   = Rect2.ProjectAlongAxis(rect, pos2, axis);

                    var mtv = AxisAlignedLine2.IntersectMTV(circleProj, rectProj);
                    if (!mtv.HasValue)
                    {
                        return(false);
                    }

                    if (Math.Abs(mtv.Value) < Math.Abs(shortestOverlap))
                    {
                        bestAxis        = axis;
                        shortestOverlap = mtv.Value;
                    }
                }
                return(true);
            };

            var circleCenter = new Vector2(pos1.X + circle.Radius, pos1.Y + circle.Radius);
            int last         = 4;
            var lastVec      = rect.UpperRight + pos2;

            for (int curr = 0; curr < 4; curr++)
            {
                Vector2 currVec = Vector2.Zero;
                switch (curr)
                {
                case 0:
                    currVec = rect.Min + pos2;
                    break;

                case 1:
                    currVec = rect.LowerLeft + pos2;
                    break;

                case 2:
                    currVec = rect.Max + pos2;
                    break;

                case 3:
                    currVec = rect.UpperRight + pos2;
                    break;
                }

                // Test along circle center -> vector
                if (!checkAxis(Vector2.Normalize(currVec - circleCenter)))
                {
                    return(null);
                }

                // Test along line normal
                if (!checkAxis(Vector2.Normalize(Math2.Perpendicular(currVec - lastVec))))
                {
                    return(null);
                }

                last    = curr;
                lastVec = currVec;
            }

            return(Tuple.Create(bestAxis, shortestOverlap));
        }
Пример #6
0
        /// <summary>
        /// Determines the vector, if any, to move poly at pos1 rotated rot1 to prevent intersection of rect
        /// at pos2.
        /// </summary>
        /// <param name="poly">Polygon</param>
        /// <param name="rect">Rectangle</param>
        /// <param name="pos1">Origin of polygon</param>
        /// <param name="pos2">Origin of rectangle</param>
        /// <param name="rot1">Rotation of the polygon.</param>
        /// <returns>The vector to move pos1 by or null</returns>
        public static Tuple <Vector2, float> IntersectMTV(Polygon2 poly, Rect2 rect, Vector2 pos1, Vector2 pos2, Rotation2 rot1)
        {
            bool checkedX = false, checkedY = false;

            Vector2 bestAxis = Vector2.Zero;
            float   bestMagn = float.MaxValue;

            for (int i = 0; i < poly.Normals.Count; i++)
            {
                var norm = Math2.Rotate(poly.Normals[i], Vector2.Zero, rot1);
                var mtv  = IntersectMTVAlongAxis(poly, rect, pos1, pos2, rot1, norm);
                if (!mtv.HasValue)
                {
                    return(null);
                }

                if (Math.Abs(mtv.Value) < Math.Abs(bestMagn))
                {
                    bestAxis = norm;
                    bestMagn = mtv.Value;
                }

                if (norm.X == 0)
                {
                    checkedY = true;
                }
                if (norm.Y == 0)
                {
                    checkedX = true;
                }
            }

            if (!checkedX)
            {
                var mtv = IntersectMTVAlongAxis(poly, rect, pos1, pos2, rot1, Vector2.UnitX);
                if (!mtv.HasValue)
                {
                    return(null);
                }

                if (Math.Abs(mtv.Value) < Math.Abs(bestMagn))
                {
                    bestAxis = Vector2.UnitX;
                    bestMagn = mtv.Value;
                }
            }

            if (!checkedY)
            {
                var mtv = IntersectMTVAlongAxis(poly, rect, pos1, pos2, rot1, Vector2.UnitY);
                if (!mtv.HasValue)
                {
                    return(null);
                }

                if (Math.Abs(mtv.Value) < Math.Abs(bestMagn))
                {
                    bestAxis = Vector2.UnitY;
                    bestMagn = mtv.Value;
                }
            }

            return(Tuple.Create(bestAxis, bestMagn));
        }
Пример #7
0
 /// <summary>
 /// Determines if the specified rectangle and polygon where rect is at pos1 and poly is at pos2 intersect
 /// along the specified axis.
 /// </summary>
 /// <param name="rect">Rectangle</param>
 /// <param name="poly">Polygon</param>
 /// <param name="pos1">Origin of rectangle</param>
 /// <param name="pos2">Origin of polygon</param>
 /// <param name="rot2">Rotation of polygon</param>
 /// <param name="strict"></param>
 /// <param name="axis"></param>
 /// <returns></returns>
 public static bool IntersectsAlongAxis(Rect2 rect, Polygon2 poly, Vector2 pos1, Vector2 pos2, Rotation2 rot2, bool strict, Vector2 axis)
 {
     return(IntersectsAlongAxis(poly, rect, pos2, pos1, rot2, strict, axis));
 }
Пример #8
0
 /// <summary>
 /// Determines if the specified rectangle and circle intersect at their given positions.
 /// </summary>
 /// <param name="rect">The rectangle</param>
 /// <param name="circle">The circle</param>
 /// <param name="pos1">The origin of the rectangle</param>
 /// <param name="pos2">The top-left of the circles bounding box</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns></returns>
 public static bool Intersects(Rect2 rect, Circle2 circle, Vector2 pos1, Vector2 pos2, bool strict)
 {
     return(Intersects(circle, rect, pos2, pos1, strict));
 }
Пример #9
0
 /// <summary>
 /// Multiply our min with original min and our max with original max and return
 /// as a rect
 /// </summary>
 /// <param name="original">the original</param>
 /// <returns>scaled rect</returns>
 public Rect2 ToRect(Rect2 original)
 {
     return(new Rect2(original.Min * Min, original.Max * Max));
 }
Пример #10
0
        /// <summary>
        /// Determines the vector to move pos1 to get rect not to intersect poly at pos2 rotated
        /// by rot2 radians.
        /// </summary>
        /// <param name="rect">The rectangle</param>
        /// <param name="poly">The polygon</param>
        /// <param name="pos1">Origin of rectangle</param>
        /// <param name="pos2">Origin of </param>
        /// <param name="rot2">Rotation of the polygon</param>
        /// <returns>Offset of pos1 to get rect not to intersect poly</returns>
        public static Tuple <Vector2, float> IntersectMTV(Rect2 rect, Polygon2 poly, Vector2 pos1, Vector2 pos2, Rotation2 rot2)
        {
            var res = IntersectMTV(poly, rect, pos2, pos1, rot2);

            return(res != null?Tuple.Create(-res.Item1, res.Item2) : res);
        }
Пример #11
0
 /// <summary>
 /// Determines if the box when at pos contains point.
 /// </summary>
 /// <param name="box">The box</param>
 /// <param name="pos">Origin of box</param>
 /// <param name="point">Point to check</param>
 /// <param name="strict">true if the edges do not count</param>
 /// <returns>If the box at pos contains point</returns>
 public static bool Contains(Rect2 box, Vector2 pos, Vector2 point, bool strict)
 {
     return(AxisAlignedLine2.Contains(box.Min.X + pos.X, box.Max.X + pos.X, point.X, strict, false) &&
            AxisAlignedLine2.Contains(box.Min.Y + pos.Y, box.Max.Y + pos.Y, point.Y, strict, false));
 }
Пример #12
0
 /// <summary>
 /// Determines if box1 with origin pos1 intersects box2 with origin pos2.
 /// </summary>
 /// <param name="box1">Box 1</param>
 /// <param name="box2">Box 2</param>
 /// <param name="pos1">Origin of box 1</param>
 /// <param name="pos2">Origin of box 2</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns>If box1 intersects box2 when box1 is at pos1 and box2 is at pos2</returns>
 public static bool Intersects(Rect2 box1, Rect2 box2, Vector2 pos1, Vector2 pos2, bool strict)
 {
     return(AxisAlignedLine2.Intersects(box1.Min.X + pos1.X, box1.Max.X + pos1.X, box2.Min.X + pos2.X, box2.Max.X + pos2.X, strict, false) &&
            AxisAlignedLine2.Intersects(box1.Min.Y + pos1.Y, box1.Max.Y + pos1.Y, box2.Min.Y + pos2.Y, box2.Max.Y + pos2.Y, strict, false));
 }
Пример #13
0
 /// <summary>
 /// Projects the rectangle at pos along axis.
 /// </summary>
 /// <param name="rect">The rectangle to project</param>
 /// <param name="pos">The origin of the rectangle</param>
 /// <param name="axis">The axis to project on</param>
 /// <returns>The projection of rect at pos along axis</returns>
 public static AxisAlignedLine2 ProjectAlongAxis(Rect2 rect, Vector2 pos, Vector2 axis)
 {
     return(ProjectAlongAxis(axis, pos, Rotation2.Zero, rect.Center, rect.Min, rect.UpperRight, rect.LowerLeft, rect.Max));
 }
Пример #14
0
 /// <summary>
 /// Deterimines in the box contains the specified polygon
 /// </summary>
 /// <param name="box">The box</param>
 /// <param name="poly">The polygon</param>
 /// <param name="boxPos">Where the box is located</param>
 /// <param name="polyPos">Where the polygon is located</param>
 /// <param name="strict">true if we return false if the any part of the polygon is on the edge, false otherwise</param>
 /// <returns>true if the poly is contained in box, false otherwise</returns>
 public static bool Contains(Rect2 box, Polygon2 poly, Vector2 boxPos, Vector2 polyPos, bool strict)
 {
     return(Contains(box, poly.AABB, boxPos, polyPos, strict));
 }
Пример #15
0
 /// <summary>
 /// Determines if innerBox is contained entirely in outerBox
 /// </summary>
 /// <param name="outerBox">the (bigger) box that you want to check contains the inner box</param>
 /// <param name="innerBox">the (smaller) box that you want to check is contained in the outer box</param>
 /// <param name="posOuter">where the outer box is located</param>
 /// <param name="posInner">where the inner box is located</param>
 /// <param name="strict">true to return false if innerBox touches an edge of outerBox, false otherwise</param>
 /// <returns>true if innerBox is contained in outerBox, false otherwise</returns>
 public static bool Contains(Rect2 outerBox, Rect2 innerBox, Vector2 posOuter, Vector2 posInner, bool strict)
 {
     return(Contains(outerBox, posOuter, innerBox.Min + posInner, strict) && Contains(outerBox, posOuter, innerBox.Max + posInner, strict));
 }