主要实现了在模型空间下的得到一个包围所有图元的最小圆,该算法的思路是这样:
1.从点集中随机选出两个点作为直径对圆进行初始化。
2.判断下一个点p是否在圆中,如果在则继续本步骤,如果不在则进行步骤3。
3.使用p作为新圆的一个边界点,另一个边界点为距离p最远的圆上的点,使用这两个点作为直径构造新圆。
4.继续步骤2,直到遍历完所有点。
参考:https://blog.csdn.net/u010559586/article/details/90903896
实现出来的效果如图所示:
首先是获得所有的点,包括参照的点和普通实体的点,获取点之后得到的点集去重。如果是块参照要看它的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<Point3d>().ToList(); trans.Commit(); } }
得到点集之后,就可以写算法了,这里,我先得到第一个圆,如果模型空间上只有一个图元,我就已这个图元的中心做圆心,边界对角线的一半作为半径 构成一个圆返回;如果是只有两个图元,我就以这两个图元的中心点做直径,直径的中点做圆心构成一个圆返回;如果是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; } }
最后是第二步和第三步的算法:
Database Db = Application.DocumentManager.MdiActiveDocument.Database; //所有的点集 List<Point3d> listPts = new List<Point3d>(); 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(); }