autocad 二次开发 最小包围圆算法

 

主要实现了在模型空间下的得到一个包围所有图元的最小圆,该算法的思路是这样:
1.从点集中随机选出两个点作为直径对圆进行初始化。
2.判断下一个点p是否在圆中,如果在则继续本步骤,如果不在则进行步骤3。
3.使用p作为新圆的一个边界点,另一个边界点为距离p最远的圆上的点,使用这两个点作为直径构造新圆。
4.继续步骤2,直到遍历完所有点。
参考:https://blog.csdn.net/u010559586/article/details/90903896
实现出来的效果如图所示:

autocad 二次开发 最小包围圆算法_第1张图片

首先是获得所有的点,包括参照的点和普通实体的点,获取点之后得到的点集去重。如果是块参照要看它的Bounds属性是否有值,有值就取边界值,如果是普通实体就取Entity的Extends属性的边界点。还有如果是标注,就不计入点,因为标注的边界属性得出来的点不准确。我先得到BlockRecord的Bounds边界,然后继续把这个blockRecord遍历了一遍,得到实体。这样做,我是想把块参照也遍历进去,但是我不知道如何区分普通的实体所在的块和有名块,还有可能有匿名的块参照,我区分不了,,就重复遍历了,最后得到的点集去个重就行了。
代码:

 public void GetAllPts()
        {

            using (var trans = Db.TransactionManager.StartTransaction())
            {

                BlockTable blkTbl = (BlockTable)trans.GetObject(Db.BlockTableId, OpenMode.ForRead);

                foreach (ObjectId oId in blkTbl)
                {

                    var rec = trans.GetObject(oId, OpenMode.ForRead) as BlockTableRecord;

                    if (rec != null)
                    {
                        //块参照
                        if (rec.Bounds.HasValue)
                        {
                            var ptMin = rec.Bounds.Value.MinPoint;
                            var ptMax = rec.Bounds.Value.MaxPoint;
                            var radius = (ptMax - ptMin).Length / 2.0;
                            listPts.Add(new Point3d((ptMin.X + ptMax.X) / 2, (ptMin.Y + ptMax.Y) / 2, 0));
                            listRadius.Add(radius);
                        }
                        //实体
                        foreach (ObjectId entId in rec)
                        {
                            var ent = trans.GetObject(entId, OpenMode.ForRead) as Entity;

                            //在计算边界属性时,dimension的不准确,我就跳过了
                            if ((ent as Dimension) != null)
                            {
                                continue;
                            }

                            if (ent != null)
                            {
                                var ptMin = ent.GeometricExtents.MinPoint;
                                var ptMax = ent.GeometricExtents.MaxPoint;

                                var radius = (ptMax - ptMin).Length / 2.0;

                                listPts.Add(new Point3d((ptMin.X + ptMax.X) / 2, (ptMin.Y + ptMax.Y) / 2, 0));
                                listRadius.Add(radius);
                            }
                        }
                    }
                }
                listPts = listPts.Distinct().ToList();
                trans.Commit();
            }
        }
View Code

得到点集之后,就可以写算法了,这里,我先得到第一个圆,如果模型空间上只有一个图元,我就已这个图元的中心做圆心,边界对角线的一半作为半径 构成一个圆返回;如果是只有两个图元,我就以这两个图元的中心点做直径,直径的中点做圆心构成一个圆返回;如果是3个或者3个以上,我就以点集的第一个点,和点集的中间点构成一个圆返回。代码如下:

 public Circle GetFirstCircle()
        {
            //如果只有一个图,就直接返回这个图元的边界圆
            if (listPts.Count == 1)
            {
                Circle c = new Circle(listPts[0], Vector3d.ZAxis,  listRadius[0]);
                return c;
            }
            else if (listPts.Count == 2)
            {
                var ptMin = listPts[0];
                var ptMax = listPts[1];
                var radius = (ptMax - ptMin).Length / 2.0;
                var ptCenter = new Point3d((ptMin.X + ptMax.X) / 2, (ptMin.Y + ptMax.Y) / 2, 0);

                Circle c = new Circle(ptCenter, Vector3d.ZAxis, radius);

                return c;

            }
            else
            {
                var ptMin = listPts[0];
                var ptMax = listPts[listPts.Count / 2];
                var radius = (ptMax - ptMin).Length / 2.0;
                var ptCenter = new Point3d((ptMin.X + ptMax.X) / 2, (ptMin.Y + ptMax.Y) / 2, 0);

                Circle c = new Circle(ptCenter, Vector3d.ZAxis, radius);

                listPts.Remove(ptMin);
                listPts.Remove(ptMax);

                return c;
            }
        }
View Code

最后是第二步和第三步的算法:

  Database Db = Application.DocumentManager.MdiActiveDocument.Database;
       //所有的点集
        List listPts = new List();
        List<double> listRadius = new List<double>();

        [CommandMethod("GetMinC")]
        public void GetCircle()
        {
            listPts.Clear();
            listRadius.Clear();

            GetAllPts();
            
            Circle minCircle = null;
            if (listPts.Count >= 3)
            {
                Circle c= GetFirstCircle();

                for (int i = 0; i < listPts.Count; i++)
                {
                    var pt = listPts[i];

                    var len = c.Radius;

                    var cCen = c.Center;

                    var len2 = (pt - cCen).Length;

                    //如果pt在圆内,继续下一个点
                    if (len > len2)
                    {
                        continue;
                    }
                    else
                    {
                        //求圆心和pt点构成的直线和圆的交点,
                        //并求出pt点离圆最远的那个点pt1或者是Pt2,最后用这两个点构成一个新的圆,继续循环,直到所有的点遍历完
                        var line = new Line(pt, cCen);

                        Point3dCollection pt3Coll = new Point3dCollection();

                        c.IntersectWith(line, Intersect.ExtendBoth, pt3Coll, IntPtr.Zero, IntPtr.Zero);

                        var pt1 = pt3Coll[0];
                        var pt2 = pt3Coll[1];

                        var l1 = (pt1 - pt).Length;
                        var l2 = (pt2 - pt).Length;

                        if (l1 > l2)
                        {
                            var center = new Point3d((pt1.X + pt.X) / 2, (pt1.Y + pt.Y) / 2, 0);

                            c = new Circle(center, Vector3d.ZAxis, l1/2);
                        }
                        else
                        {
                            var center = new Point3d((pt2.X + pt.X) / 2, (pt2.Y + pt.Y) / 2, 0);

                            c = new Circle(center, Vector3d.ZAxis, l2 / 2);
                        }
                    }
                }
                minCircle = c;
            }
            else
            {
                minCircle = GetFirstCircle();
            }
            if (minCircle != null)
                //加入模型空间
                minCircle.ToSpace();
            minCircle.Dispose();
        }

你可能感兴趣的:(autocad 二次开发 最小包围圆算法)