Saturday, May 9, 2020

Cleaning Up Duplicate Vertices of MPolygon

MPolygon, which behaves like a closed Polyline (LwPolyline) with hatch, is often used in AutoCAD for GIS/Mapping type of business operation, representing as an area. One of the common GIS/Mapping requirements for polygon as area is that the polygon should not have duplicate vertices (i.e. 2 vertices should not be too close to each other). Therefore, most GIS/Mapping tools need to have ways to examine polygons for duplicate vertices and remove them, if found.

There is recently a question asked in AutoCAD Map discussion forum on how to do this with MPolygon. There are very few discussions/code samples on MPolygon that one can find online. So, I post the code out of my study here.

Here are things related to solving this issue:

1. An MPolygon has, just like a Hatch, one or more loops (class of MPolygonLoop);
2. MPolygonLoop can be accessed by MPolygon.GetMPolygonLoopAt(int) method;
3. An MPolygonLoop is a collection of BulgeVertex object (class of BulgeVertexCollection);
4. With BulgeVertex object, one can calculate whether 2 BulgeVertex is duplicated (too closed);
5. If duplicate is found, one of the BulgeVertex can be removed from the MPolygonLoop;
6. HOWEVER, simply remove a BulgeVertex from an MPolygonLoop of an MPolygon DOES NOT change the MPolygon, because MPolygon's loop is somehow "immutable". One need to remove the loop from the MPolygon, change the loop (adding/removing/changing one or more BulgeVertices as needed), then append the loop back to the MPolygon.

Here is the code of cleaning duplicate vertices in MPolygon, which is an extension class:

using Autodesk.AutoCAD.DatabaseServices;
 
namespace MPolyUtil
{
    public static class MPolygonExtension
    {
        public static int RemoveDuplicateVertices(
            this MPolygon mpolydouble tolerance=1.0)
        {
            if (!mpoly.IsWriteEnabled) mpoly.UpgradeOpen();
 
            var count = 0;
            var hasDup = true;
            while(hasDup)
            {
                hasDup = PurgeDuplicatedVertex(mpolytolerance);
                if (hasDupcount++;
            }
 
            return count;
        }
        public static int GetVertexCount(this MPolygon mpoly)
        {
            var count = 0;
 
            for (int i=0; i<mpoly.NumMPolygonLoops; i++)
            {
                var loop = mpoly.GetMPolygonLoopAt(i);
                count += loop.Count;
            }
 
            return count;
        }
        private static bool PurgeDuplicatedVertex(
            MPolygon mpolydouble tolerance)
        {
            var purged = false;
 
            for (int i = 0; i < mpoly.NumMPolygonLoops; i++)
            {
                var loop = mpoly.GetMPolygonLoopAt(i);
                if (LoopChanged(looptolerance))
                {
                    mpoly.RemoveMPolygonLoopAt(i);
                    mpoly.AppendMPolygonLoop(looptrue, 0.0);
                    purged = true;
                    break;
                }
            }
 
            return purged;
        }
        private static bool LoopChanged(
            MPolygonLoop loopdouble tolerance)
        {
            var changed = false;
 
            for (int i = 0; i < loop.Count - 1; i++)
            {
                var vertex1 = loop[i];
                var vertex2 = loop[i + 1];
 
                var dist = vertex1.Vertex.GetDistanceTo(vertex2.Vertex);
                if (dist < tolerance)
                {
                    loop.Remove(vertex2);
                    changed = true;
                }
            }
 
            return changed;
        }
    }
}



Here is the CommandClass that put above code into work:

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
 
[assemblyCommandClass(typeof(MPolyUtil.MyCommands))]
 
namespace MPolyUtil
{
    public class MyCommands
    {
        [CommandMethod("CleanMPoly")]
        public static void RunMyCommand()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
 
            try
            {
                var mpolyId = SelectMPolygon(ed);
                if (!mpolyId.IsNull)
                {
                    var removedCount = 0;
                    using (var tran = dwg.TransactionManager.StartTransaction())
                    {
                        var mpoly = (MPolygon)tran.GetObject(
                            mpolyIdOpenMode.ForWrite);
                        removedCount = mpoly.RemoveDuplicateVertices(10.0);
 
                        tran.Commit();
                    }
 
                    ed.WriteMessage(
                        $"\nDuplicate vertices found and removed: {removedCount}");
                }
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage($"\nInitializing error:\n{ex.Message}\n");
            }
        }
 
        private static ObjectId SelectMPolygon(Editor ed)
        {
            var opt = new PromptEntityOptions(
                "\nSelect an MPolygon:");
            opt.SetRejectMessage("\nInvalid: not an MPolygon!");
            opt.AddAllowedClass(typeof(MPolygon), true);
            var res = ed.GetEntity(opt);
            if (res.Status == PromptStatus.OK)
                return res.ObjectId;
            else
                return ObjectId.Null;
        }
    }
}

The video clip below shows the effect of running the code:



No comments:

Followers

About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.