Пример #1
0
        /// <summary>
        ///     Describes whether validate polygon
        /// </summary>
        /// <param name="polygon">The polygon</param>
        /// <returns>The bool</returns>
        private static bool ValidatePolygon(Vertices polygon)
        {
            PolygonError errorCode = polygon.CheckPolygon();

            if (errorCode == PolygonError.InvalidAmountOfVertices || errorCode == PolygonError.AreaTooSmall ||
                errorCode == PolygonError.SideTooSmall || errorCode == PolygonError.NotSimple)
            {
                return(false);
            }

            if (
                errorCode ==
                PolygonError
                .NotCounterClockWise)     //NotCounterCloseWise is the last check in CheckPolygon(), thus we don't need to call ValidatePolygon again.
            {
                polygon.Reverse();
            }

            if (errorCode == PolygonError.NotConvex)
            {
                polygon = GiftWrap.GetConvexHull(polygon);
                return(ValidatePolygon(polygon));
            }

            return(true);
        }
Пример #2
0
        public override void Update(GameSettings settings, GameTime gameTime)
        {
            DrawString("Use the mouse to create a polygon.");
            DrawString("Simple: " + _vertices.IsSimple());
            DrawString("Convex: " + _vertices.IsConvex());
            DrawString("CCW: " + _vertices.IsCounterClockWise());
            DrawString("Area: " + _vertices.GetArea());

            PolygonError returnCode = _vertices.CheckPolygon();

            if (returnCode == PolygonError.NoError)
                DrawString("Polygon is supported in Velcro Physics");
            else
                DrawString("Polygon is NOT supported in Velcro Physics. Reason: " + returnCode);

            DebugView.BeginCustomDraw(ref GameInstance.Projection, ref GameInstance.View);

            for (int i = 0; i < _vertices.Count; i++)
            {
                Vector2 currentVertex = _vertices[i];
                Vector2 nextVertex = _vertices.NextVertex(i);

                DebugView.DrawPoint(currentVertex, 0.1f, Color.Yellow);
                DebugView.DrawSegment(currentVertex, nextVertex, Color.Red);
            }

            DebugView.DrawPoint(_vertices.GetCentroid(), 0.1f, Color.Green);

            AABB aabb = _vertices.GetAABB();
            DebugView.DrawAABB(ref aabb, Color.HotPink);

            DebugView.EndCustomDraw();
            base.Update(settings, gameTime);
        }
Пример #3
0
        private static bool ValidatePolygon(Vertices polygon)
        {
            PolygonError polygonError = polygon.CheckPolygon();
            bool         flag         = polygonError == PolygonError.InvalidAmountOfVertices || polygonError == PolygonError.AreaTooSmall || polygonError == PolygonError.SideTooSmall || polygonError == PolygonError.NotSimple;
            bool         result;

            if (flag)
            {
                result = false;
            }
            else
            {
                bool flag2 = polygonError == PolygonError.NotCounterClockWise;
                if (flag2)
                {
                    polygon.Reverse();
                }
                bool flag3 = polygonError == PolygonError.NotConvex;
                if (flag3)
                {
                    polygon = GiftWrap.GetConvexHull(polygon);
                    result  = Triangulate.ValidatePolygon(polygon);
                }
                else
                {
                    result = true;
                }
            }
            return(result);
        }
        public static string GetErrorString(PolygonError error)
        {
            StringBuilder sb = new StringBuilder(256);

            if (error == PolygonError.None)
            {
                sb.AppendFormat("No errors.\n");
            }
            else
            {
                if ((error & PolygonError.NotEnoughVertices) == PolygonError.NotEnoughVertices)
                {
                    sb.AppendFormat("NotEnoughVertices: must have between 3 and {0} vertices.\n", kMaxPolygonVertices);
                }
                if ((error & PolygonError.NotConvex) == PolygonError.NotConvex)
                {
                    sb.AppendFormat("NotConvex: Polygon is not convex.\n");
                }
                if ((error & PolygonError.NotSimple) == PolygonError.NotSimple)
                {
                    sb.AppendFormat("NotSimple: Polygon is not simple (i.e. it intersects itself).\n");
                }
                if ((error & PolygonError.AreaTooSmall) == PolygonError.AreaTooSmall)
                {
                    sb.AppendFormat("AreaTooSmall: Polygon's area is too small.\n");
                }
                if ((error & PolygonError.SidesTooCloseToParallel) == PolygonError.SidesTooCloseToParallel)
                {
                    sb.AppendFormat("SidesTooCloseToParallel: Polygon's sides are too close to parallel.\n");
                }
                if ((error & PolygonError.TooThin) == PolygonError.TooThin)
                {
                    sb.AppendFormat("TooThin: Polygon is too thin or core shape generation would move edge past centroid.\n");
                }
                if ((error & PolygonError.Degenerate) == PolygonError.Degenerate)
                {
                    sb.AppendFormat("Degenerate: Polygon is degenerate (contains collinear points or duplicate coincident points).\n");
                }
                if ((error & PolygonError.Unknown) == PolygonError.Unknown)
                {
                    sb.AppendFormat("Unknown: Unknown Polygon error!.\n");
                }
            }

            return(sb.ToString());
        }
Пример #5
0
        public override void Update(FarseerPhysicsGameSettings settings)
        {
            DrawString("Use the mouse to create a polygon.");
            DrawString("Simple: " + _vertices.IsSimple());
            DrawString("Convex: " + _vertices.IsConvex());
            DrawString("CCW: " + _vertices.IsCounterClockWise());
            DrawString("Area: " + _vertices.GetArea());

            PolygonError returnCode = _vertices.CheckPolygon();

            if (returnCode == PolygonError.NoError)
            {
                DrawString("Polygon is supported in Farseer Physics Engine");
            }
            else
            {
                DrawString("Polygon is NOT supported in Farseer Physics Engine. Reason: " + returnCode);
            }



            for (int i = 0; i < _vertices.Count; i++)
            {
                Vector2 currentVertex = _vertices[i];
                Vector2 nextVertex    = _vertices.NextVertex(i);

                DebugView.DrawPoint(currentVertex, 0.1f, Color.Yellow);
                DebugView.DrawSegment(currentVertex, nextVertex, Color.Red);
            }

            DebugView.DrawPoint(_vertices.GetCentroid(), 0.1f, Color.Green);

            AABB aabb = _vertices.GetAABB();

            DebugView.DrawAABB(ref aabb, Color.HotPink);


            base.Update(settings);
        }
        /// <summary>
        /// Checks if polygon is valid for use in Box2d engine.
        /// Last ditch effort to ensure no invalid polygons are
        /// added to world geometry.
        ///
        /// Performs a full check, for simplicity, convexity,
        /// orientation, minimum angle, and volume.  This won't
        /// be very efficient, and a lot of it is redundant when
        /// other tools in this section are used.
        ///
        /// From Eric Jordan's convex decomposition library
        /// </summary>
        /// <param name="printErrors"></param>
        /// <returns></returns>
        public PolygonError CheckPolygon()
        {
            PolygonError error = PolygonError.None;

            if (Count < 3 || Count > Point2DList.kMaxPolygonVertices)
            {
                error |= PolygonError.NotEnoughVertices;
                // no other tests will be valid at this point, so just return
                return(error);
            }
            if (IsDegenerate())
            {
                error |= PolygonError.Degenerate;
            }
            //bool bIsConvex = IsConvex();
            //if (!IsConvex())
            //{
            //    error |= PolygonError.NotConvex;
            //}
            if (!IsSimple())
            {
                error |= PolygonError.NotSimple;
            }
            if (GetArea() < MathUtil.EPSILON)
            {
                error |= PolygonError.AreaTooSmall;
            }

            // the following tests don't make sense if the polygon is not simple
            if ((error & PolygonError.NotSimple) != PolygonError.NotSimple)
            {
                bool             bReversed            = false;
                WindingOrderType expectedWindingOrder = WindingOrderType.CCW;
                WindingOrderType reverseWindingOrder  = WindingOrderType.CW;
                if (WindingOrder == reverseWindingOrder)
                {
                    WindingOrder = expectedWindingOrder;
                    bReversed    = true;
                }

                //Compute normals
                Point2D[]   normals  = new Point2D[Count];
                Point2DList vertices = new Point2DList(Count);
                for (int i = 0; i < Count; ++i)
                {
                    vertices.Add(new Point2D(this [i].X, this [i].Y));
                    int     i1   = i;
                    int     i2   = NextIndex(i);
                    Point2D edge = new Point2D(this [i2].X - this [i1].X, this [i2].Y - this [i1].Y);
                    normals [i] = Point2D.Perpendicular(edge, 1.0);
                    normals [i].Normalize();
                }

                //Required side checks
                for (int i = 0; i < Count; ++i)
                {
                    int iminus = PreviousIndex(i);

                    //Parallel sides check
                    double cross = Point2D.Cross(normals [iminus], normals [i]);
                    cross = MathUtil.Clamp(cross, -1.0f, 1.0f);
                    float angle = (float)Math.Asin(cross);
                    if (Math.Abs(angle) <= Point2DList.kAngularSlop)
                    {
                        error |= PolygonError.SidesTooCloseToParallel;
                        break;
                    }

                    // For some reason, the following checks do not seem to work
                    // correctly in all cases - they return false positives.
                    //    //Too skinny check - only valid for convex polygons
                    //    if (bIsConvex)
                    //    {
                    //        for (int j = 0; j < Count; ++j)
                    //        {
                    //            if (j == i || j == NextIndex(i))
                    //            {
                    //                continue;
                    //            }
                    //            Point2D testVector = vertices[j] - vertices[i];
                    //            testVector.Normalize();
                    //            double s = Point2D.Dot(testVector, normals[i]);
                    //            if (s >= -Point2DList.kLinearSlop)
                    //            {
                    //                error |= PolygonError.TooThin;
                    //            }
                    //        }

                    //        Point2D centroid = vertices.GetCentroid();
                    //        Point2D n1 = normals[iminus];
                    //        Point2D n2 = normals[i];
                    //        Point2D v = vertices[i] - centroid;

                    //        Point2D d = new Point2D();
                    //        d.X = Point2D.Dot(n1, v); // - toiSlop;
                    //        d.Y = Point2D.Dot(n2, v); // - toiSlop;

                    //        // Shifting the edge inward by toiSlop should
                    //        // not cause the plane to pass the centroid.
                    //        if ((d.X < 0.0f) || (d.Y < 0.0f))
                    //        {
                    //            error |= PolygonError.TooThin;
                    //        }
                    //    }
                }

                if (bReversed)
                {
                    WindingOrder = reverseWindingOrder;
                }
            }

            //if (error != PolygonError.None)
            //{
            //    Console.WriteLine("Found invalid polygon: {0} {1}\n", Point2DList.GetErrorString(error), this.ToString());
            //}

            return(error);
        }
Пример #7
0
        // Assumes that points being passed in the list are connected and form a polygon.
        // Note that some error checking is done for robustness, but for the most part,
        // we have to rely on the user to feed us "correct" data
        public bool AddHole(List <TriangulationPoint> points)
        {
            if (points == null)
            {
                return(false);
            }

            //// split our self-intersection sections into their own lists
            List <Contour> pts     = new List <Contour>();
            int            listIdx = 0;

            {
                Contour c = new Contour(this, points, WindingOrderType.Unknown);
                pts.Add(c);

                // only constrain the points if we actually HAVE a bounding rect
                if (MPoints.Count > 1)
                {
                    // constrain the points to bounding rect
                    int numPoints = pts[listIdx].Count;
                    for (int i = 0; i < numPoints; ++i)
                    {
                        ConstrainPointToBounds(pts[listIdx][i]);
                    }
                }
            }

            while (listIdx < pts.Count)
            {
                // simple sanity checking - remove duplicate coincident points before
                // we check the polygon: fast, simple algorithm that eliminate lots of problems
                // that only more expensive checks will find
                pts[listIdx].RemoveDuplicateNeighborPoints();
                pts[listIdx].WindingOrder = WindingOrderType.Default;

                bool         bListOk = true;
                PolygonError err     = pts[listIdx].CheckPolygon();
                while (bListOk && err != PolygonError.None)
                {
                    if ((err & PolygonError.NotEnoughVertices) == PolygonError.NotEnoughVertices)
                    {
                        bListOk = false;
                        continue;
                    }
                    if ((err & PolygonError.NotSimple) == PolygonError.NotSimple)
                    {
                        // split the polygons, remove the current list and add the resulting list to the end
                        //List<Point2DList> l = TriangulationUtil.SplitSelfIntersectingPolygon(pts[listIdx], pts[listIdx].Epsilon);
                        IEnumerable <Point2DList> l = PolygonUtil.SplitComplexPolygon(pts[listIdx], pts[listIdx].Epsilon);
                        pts.RemoveAt(listIdx);
                        foreach (Point2DList newList in l)
                        {
                            Contour c = new Contour(this);
                            c.AddRange(newList);
                            pts.Add(c);
                        }
                        err = pts[listIdx].CheckPolygon();
                        continue;
                    }
                    if ((err & PolygonError.Degenerate) == PolygonError.Degenerate)
                    {
                        pts[listIdx].Simplify(Epsilon);
                        err = pts[listIdx].CheckPolygon();
                        continue;
                        //err &= ~(PolygonError.Degenerate);
                        //if (pts[listIdx].Count < 3)
                        //{
                        //    err |= PolygonError.NotEnoughVertices;
                        //    bListOK = false;
                        //    continue;
                        //}
                    }
                    if ((err & PolygonError.AreaTooSmall) == PolygonError.AreaTooSmall ||
                        (err & PolygonError.SidesTooCloseToParallel) == PolygonError.SidesTooCloseToParallel ||
                        (err & PolygonError.TooThin) == PolygonError.TooThin ||
                        (err & PolygonError.Unknown) == PolygonError.Unknown)
                    {
                        bListOk = false;
                    }
                    // non-convex polygons are ok
                    //if ((err & PolygonError.NotConvex) == PolygonError.NotConvex)
                    //{
                    //}
                }
                if (!bListOk && pts[listIdx].Count != 2)
                {
                    pts.RemoveAt(listIdx);
                }
                else
                {
                    ++listIdx;
                }
            }

            bool bOk = true;

            listIdx = 0;
            while (listIdx < pts.Count)
            {
                int numPoints = pts[listIdx].Count;
                if (numPoints < 2)
                {
                    // should not be possible by this point...
                    ++listIdx;
                    bOk = false;
                    continue;
                }
                else if (numPoints == 2)
                {
                    uint constraintCode = TriangulationConstraint.CalculateContraintCode(pts[listIdx][0], pts[listIdx][1]);
                    TriangulationConstraint tc;
                    if (!_constraintMap.TryGetValue(constraintCode, out tc))
                    {
                        tc = new TriangulationConstraint(pts[listIdx][0], pts[listIdx][1]);
                        AddConstraint(tc);
                    }
                }
                else
                {
                    Contour ph = new Contour(this, pts[listIdx], WindingOrderType.Unknown)
                    {
                        WindingOrder = WindingOrderType.Default,
                    };
                    _holes.Add(ph);
                }
                ++listIdx;
            }

            return(bOk);
        }
Пример #8
0
        public PolygonError CheckPolygon()
        {
            PolygonError error = PolygonError.None;

            if (Count < 3 || Count > Point2DList.kMaxPolygonVertices)
            {
                error |= PolygonError.NotEnoughVertices;
                return(error);
            }
            if (IsDegenerate())
            {
                error |= PolygonError.Degenerate;
            }

            if (!IsSimple())
            {
                error |= PolygonError.NotSimple;
            }

            if (GetArea() < MathUtil.EPSILON)
            {
                error |= PolygonError.AreaTooSmall;
            }

            if ((error & PolygonError.NotSimple) != PolygonError.NotSimple)
            {
                bool             bReversed            = false;
                WindingOrderType expectedWindingOrder = WindingOrderType.CCW;
                WindingOrderType reverseWindingOrder  = WindingOrderType.CW;
                if (WindingOrder == reverseWindingOrder)
                {
                    WindingOrder = expectedWindingOrder;
                    bReversed    = true;
                }

                Point2D[]   normals  = new Point2D[Count];
                Point2DList vertices = new Point2DList(Count);
                for (int i = 0; i < Count; ++i)
                {
                    vertices.Add(new Point2D(this[i].X, this[i].Y));
                    int     i1   = i;
                    int     i2   = NextIndex(i);
                    Point2D edge = new Point2D(this[i2].X - this[i1].X, this[i2].Y - this[i1].Y);
                    normals[i] = Point2D.Perpendicular(edge, 1.0);
                    normals[i].Normalize();
                }

                for (int i = 0; i < Count; ++i)
                {
                    int iminus = PreviousIndex(i);

                    double cross = Point2D.Cross(normals[iminus], normals[i]);
                    cross = MathUtil.Clamp(cross, -1.0f, 1.0f);
                    float angle = (float)Math.Asin(cross);
                    if (Math.Abs(angle) <= Point2DList.kAngularSlop)
                    {
                        error |= PolygonError.SidesTooCloseToParallel;
                        break;
                    }
                }

                if (bReversed)
                {
                    WindingOrder = reverseWindingOrder;
                }
            }

            return(error);
        }
Пример #9
0
        public static List <Vertices> ConvexPartition(Vertices vertices, TriangulationAlgorithm algorithm, bool discardAndFixInvalid = true, float tolerance = 0.001f)
        {
            if (vertices.Count <= 3)
            {
                return new List <Vertices> {
                           vertices
                }
            }
            ;

            List <Vertices> results;

            switch (algorithm)
            {
            case TriangulationAlgorithm.Earclip:
                if (Settings.SkipSanityChecks)
                {
                    Debug.Assert(!vertices.IsCounterClockWise(), "The Earclip algorithm expects the polygon to be clockwise.");
                }
                else
                {
                    if (vertices.IsCounterClockWise())
                    {
                        Vertices temp = new Vertices(vertices);
                        temp.Reverse();
                        results = EarclipDecomposer.ConvexPartition(temp, tolerance);
                    }
                    else
                    {
                        results = EarclipDecomposer.ConvexPartition(vertices, tolerance);
                    }
                }
                break;

            case TriangulationAlgorithm.Bayazit:
                if (Settings.SkipSanityChecks)
                {
                    Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly.");
                }
                else
                {
                    if (!vertices.IsCounterClockWise())
                    {
                        Vertices temp = new Vertices(vertices);
                        temp.Reverse();
                        results = BayazitDecomposer.ConvexPartition(temp);
                    }
                    else
                    {
                        results = BayazitDecomposer.ConvexPartition(vertices);
                    }
                }
                break;

            case TriangulationAlgorithm.Flipcode:
                if (Settings.SkipSanityChecks)
                {
                    Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly.");
                }
                else
                {
                    if (!vertices.IsCounterClockWise())
                    {
                        Vertices temp = new Vertices(vertices);
                        temp.Reverse();
                        results = FlipcodeDecomposer.ConvexPartition(temp);
                    }
                    else
                    {
                        results = FlipcodeDecomposer.ConvexPartition(vertices);
                    }
                }
                break;

            case TriangulationAlgorithm.Seidel:
                results = SeidelDecomposer.ConvexPartition(vertices, tolerance);
                break;

            case TriangulationAlgorithm.SeidelTrapezoids:
                results = SeidelDecomposer.ConvexPartitionTrapezoid(vertices, tolerance);
                break;

            case TriangulationAlgorithm.Delauny:
                results = CDTDecomposer.ConvexPartition(vertices);
                break;

            default:
                throw new ArgumentOutOfRangeException("algorithm");
            }

            if (discardAndFixInvalid)
            {
                for (int i = results.Count - 1; i >= 0; i--)
                {
                    Vertices polygon = results[i];

                    PolygonError errorCode = polygon.CheckPolygon();

                    if (errorCode == PolygonError.InvalidAmountOfVertices || errorCode == PolygonError.AreaTooSmall || errorCode == PolygonError.SideTooSmall || errorCode == PolygonError.NotSimple)
                    {
                        results.RemoveAt(i);
                    }
                    else if (errorCode == PolygonError.NotCounterClockWise)
                    {
                        polygon.Reverse();
                    }
                    else if (errorCode == PolygonError.NotConvex)
                    {
                        results[i] = GiftWrap.GetConvexHull(polygon);
                    }
                }
            }

            return(results);
        }
    }