• C# CAD 几何图形周围创建尽可能小的圆 使用 .NET 在 2D AutoCAD


     

    using Autodesk.AutoCAD.ApplicationServices;
    
    using Autodesk.AutoCAD.DatabaseServices;
    
    using Autodesk.AutoCAD.EditorInput;
    
    using Autodesk.AutoCAD.Runtime;
    
    using Autodesk.AutoCAD.Geometry;
    
    using System.Collections.Generic;
    
     
    
    namespace MinimumEnclosingCircle
    
    {
    
      public class Commands
    
      {
    
        [CommandMethod("MEC", CommandFlags.UsePickSet)]
    
        public void MinimumEnclosingCircle()
    
        {
    
          Document doc =
    
              Application.DocumentManager.MdiActiveDocument;
    
          Database db = doc.Database;
    
          Editor ed = doc.Editor;
    
     
    
          // Ask user to select entities
    
     
    
          PromptSelectionOptions pso =
    
            new PromptSelectionOptions();
    
          pso.MessageForAdding = "\nSelect objects to enclose: ";
    
          pso.AllowDuplicates = false;
    
          pso.AllowSubSelections = true;
    
          pso.RejectObjectsFromNonCurrentSpace = true;
    
          pso.RejectObjectsOnLockedLayers = false;
    
     
    
          PromptSelectionResult psr = ed.GetSelection(pso);
    
          if (psr.Status != PromptStatus.OK)
    
            return;
    
     
    
          bool oneCircPerEnt = false;
    
     
    
          if (psr.Value.Count > 1)
    
          {
    
            PromptKeywordOptions pko =
    
              new PromptKeywordOptions(
    
                "\nMultiple objects selected: create " +
    
                "individual circles around each one?"
    
              );
    
            pko.AllowNone = true;
    
            pko.Keywords.Add("Yes");
    
            pko.Keywords.Add("No");
    
            pko.Keywords.Default = "No";
    
     
    
            PromptResult pkr = ed.GetKeywords(pko);
    
            if (pkr.Status != PromptStatus.OK)
    
              return;
    
     
    
            oneCircPerEnt = (pkr.StringResult == "Yes");
    
          }
    
     
    
          // There may be a SysVar defining the buffer
    
          // to add to our radius
    
     
    
          double buffer = 0.0;
    
          try
    
          {
    
            object bufvar =
    
              Application.GetSystemVariable(
    
                "ENCLOSINGCIRCLEBUFFER"
    
              );
    
            if (bufvar != null)
    
            {
    
              short bufval = (short)bufvar;
    
              buffer = bufval / 100.0;
    
            }
    
          }
    
          catch
    
          {
    
            object bufvar =
    
              Application.GetSystemVariable("USERI1");
    
            if (bufvar != null)
    
            {
    
              short bufval = (short)bufvar;
    
              buffer = bufval / 100.0;
    
            }
    
          }
    
     
    
          // Get the current UCS
    
     
    
          CoordinateSystem3d ucs =
    
            ed.CurrentUserCoordinateSystem.CoordinateSystem3d;
    
     
    
          // Collect points on the component entities
    
     
    
          Point3dCollection pts = new Point3dCollection();
    
     
    
          Transaction tr =
    
            db.TransactionManager.StartTransaction();
    
          using (tr)
    
          {
    
            BlockTableRecord btr =
    
              (BlockTableRecord)tr.GetObject(
    
                db.CurrentSpaceId,
    
                OpenMode.ForWrite
    
              );
    
     
    
            for (int i = 0; i < psr.Value.Count; i++)
    
            {
    
              Entity ent =
    
                (Entity)tr.GetObject(
    
                  psr.Value[i].ObjectId,
    
                  OpenMode.ForRead
    
                );
    
     
    
              // Collect the points for each selected entity
    
     
    
              Point3dCollection entPts = CollectPoints(tr, ent);
    
              foreach (Point3d pt in entPts)
    
              {
    
                /*
    
                * Create a DBPoint, for testing purposes
    
                *
    
                DBPoint dbp = new DBPoint(pt);
    
                btr.AppendEntity(dbp);
    
                tr.AddNewlyCreatedDBObject(dbp, true);
    
                */
    
     
    
                pts.Add(pt);
    
              }
    
     
    
              // Create a circle for each entity (if so chosen) or
    
              // just once after collecting all the points
    
     
    
              if (oneCircPerEnt || i == psr.Value.Count - 1)
    
              {
    
                try
    
                {
    
                  Circle cir =
    
                    CircleFromPoints(pts, ucs, buffer);
    
                  btr.AppendEntity(cir);
    
                  tr.AddNewlyCreatedDBObject(cir, true);
    
                }
    
                catch
    
                {
    
                  ed.WriteMessage(
    
                    "\nUnable to calculate enclosing circle."
    
                  );
    
                }
    
     
    
                pts.Clear();
    
              }
    
            }
    
     
    
            tr.Commit();
    
          }
    
        }
    
     
    
        private Point3dCollection CollectPoints(
    
          Transaction tr, Entity ent
    
        )
    
        {
    
          // The collection of points to populate and return
    
     
    
          Point3dCollection pts = new Point3dCollection();
    
     
    
          // We'll start by checking a block reference for
    
          // attributes, getting their bounds and adding
    
          // them to the point list. We'll still explode
    
          // the BlockReference later, to gather points
    
          // from other geometry, it's just that approach
    
          // doesn't work for attributes (we only get the
    
          // AttributeDefinitions, which don't have bounds)
    
     
    
          BlockReference br = ent as BlockReference;
    
          if (br != null)
    
          {
    
            foreach (ObjectId arId in br.AttributeCollection)
    
            {
    
              DBObject obj = tr.GetObject(arId, OpenMode.ForRead);
    
              if (obj is AttributeReference)
    
              {
    
                AttributeReference ar = (AttributeReference)obj;
    
                ExtractBounds(ar, pts);
    
              }
    
            }
    
          }
    
     
    
          // If we have a curve - other than a polyline, which
    
          // we will want to explode - we'll get points along
    
          // its length
    
     
    
          Curve cur = ent as Curve;
    
          if (cur != null &&
    
              !(cur is Polyline ||
    
                cur is Polyline2d ||
    
                cur is Polyline3d))
    
          {
    
            // Two points are enough for a line, we'll go with
    
            // a higher number for other curves
    
     
    
            int segs = (ent is Line ? 2 : 20);
    
     
    
            double param = cur.EndParam - cur.StartParam;
    
            for (int i = 0; i < segs; i++)
    
            {
    
              try
    
              {
    
                Point3d pt =
    
                  cur.GetPointAtParameter(
    
                    cur.StartParam + (i * param / (segs - 1))
    
                  );
    
                pts.Add(pt);
    
              }
    
              catch { }
    
            }
    
          }
    
          else if (ent is DBPoint)
    
          {
    
            // Points are easy
    
     
    
            pts.Add(((DBPoint)ent).Position);
    
          }
    
          else if (ent is DBText)
    
          {
    
            // For DBText we use the same approach as
    
            // for AttributeReferences
    
     
    
            ExtractBounds((DBText)ent, pts);
    
          }
    
          else if (ent is MText)
    
          {
    
            // MText is also easy - you get all four corners
    
            // returned by a function. That said, the points
    
            // are of the MText's box, so may well be different
    
            // from the bounds of the actual contents
    
     
    
            MText txt = (MText)ent;
    
            Point3dCollection pts2 = txt.GetBoundingPoints();
    
            foreach (Point3d pt in pts2)
    
            {
    
              pts.Add(pt);
    
            }
    
          }
    
          else if (ent is Face)
    
          {
    
            Face f = (Face)ent;
    
            try
    
            {
    
              for (short i = 0; i < 4; i++)
    
              {
    
                pts.Add(f.GetVertexAt(i));
    
              }
    
            }
    
            catch { }
    
          }
    
          else if (ent is Solid)
    
          {
    
            Solid sol = (Solid)ent;
    
            try
    
            {
    
              for (short i = 0; i < 4; i++)
    
              {
    
                pts.Add(sol.GetPointAt(i));
    
              }
    
            }
    
            catch { }
    
          }
    
          else
    
          {
    
            // Here's where we attempt to explode other types
    
            // of object
    
     
    
            DBObjectCollection oc = new DBObjectCollection();
    
            try
    
            {
    
              ent.Explode(oc);
    
              if (oc.Count > 0)
    
              {
    
                foreach (DBObject obj in oc)
    
                {
    
                  Entity ent2 = obj as Entity;
    
                  if (ent2 != null && ent2.Visible)
    
                  {
    
                    foreach (Point3d pt in CollectPoints(tr, ent2))
    
                    {
    
                      pts.Add(pt);
    
                    }
    
                  }
    
                  obj.Dispose();
    
                }
    
              }
    
            }
    
            catch { }
    
          }
    
          return pts;
    
        }
    
     
    
        private void ExtractBounds(
    
          DBText txt, Point3dCollection pts
    
        )
    
        {
    
          // We have a special approach for DBText and
    
          // AttributeReference objects, as we want to get
    
          // all four corners of the bounding box, even
    
          // when the text or the containing block reference
    
          // is rotated
    
     
    
          if (txt.Bounds.HasValue && txt.Visible)
    
          {
    
            // Create a straight version of the text object
    
            // and copy across all the relevant properties
    
            // (stopped copying AlignmentPoint, as it would
    
            // sometimes cause an eNotApplicable error)
    
     
    
            // We'll create the text at the WCS origin
    
            // with no rotation, so it's easier to use its
    
            // extents
    
     
    
            DBText txt2 = new DBText();
    
            txt2.Normal = Vector3d.ZAxis;
    
            txt2.Position = Point3d.Origin;
    
     
    
            // Other properties are copied from the original
    
     
    
            txt2.TextString = txt.TextString;
    
            txt2.TextStyleId = txt.TextStyleId;
    
            txt2.LineWeight = txt.LineWeight;
    
            txt2.Thickness = txt2.Thickness;
    
            txt2.HorizontalMode = txt.HorizontalMode;
    
            txt2.VerticalMode = txt.VerticalMode;
    
            txt2.WidthFactor = txt.WidthFactor;
    
            txt2.Height = txt.Height;
    
            txt2.IsMirroredInX = txt2.IsMirroredInX;
    
            txt2.IsMirroredInY = txt2.IsMirroredInY;
    
            txt2.Oblique = txt.Oblique;
    
     
    
            // Get its bounds if it has them defined
    
            // (which it should, as the original did)
    
     
    
            if (txt2.Bounds.HasValue)
    
            {
    
              Point3d maxPt = txt2.Bounds.Value.MaxPoint;
    
     
    
              // Place all four corners of the bounding box
    
              // in an array
    
     
    
              Point2d[] bounds =
    
                new Point2d[] {
    
                  Point2d.Origin,
    
                  new Point2d(0.0, maxPt.Y),
    
                  new Point2d(maxPt.X, maxPt.Y),
    
                  new Point2d(maxPt.X, 0.0)
    
                };
    
     
    
              // We're going to get each point's WCS coordinates
    
              // using the plane the text is on
    
     
    
              Plane pl = new Plane(txt.Position, txt.Normal);
    
     
    
              // Rotate each point and add its WCS location to the
    
              // collection
    
     
    
              foreach (Point2d pt in bounds)
    
              {
    
                pts.Add(
    
                  pl.EvaluatePoint(
    
                    pt.RotateBy(txt.Rotation, Point2d.Origin)
    
                  )
    
                );
    
              }
    
            }
    
          }
    
        }
    
     
    
        private Circle CircleFromPoints(
    
          Point3dCollection pts, CoordinateSystem3d ucs, double buffer
    
        )
    
        {
    
          // Get the plane of the UCS
    
     
    
          Plane pl = new Plane(ucs.Origin, ucs.Zaxis);
    
     
    
          // We will project these (possibly 3D) points onto
    
          // the plane of the current UCS, as that's where
    
          // we will create our circle
    
     
    
          // Project the points onto it
    
     
    
          List<Point2d> pts2d = new List<Point2d>(pts.Count);
    
          for (int i = 0; i < pts.Count; i++)
    
          {
    
            pts2d.Add(pl.ParameterOf(pts[i]));
    
          }
    
     
    
          // Assuming we have some points in our list...
    
     
    
          if (pts.Count > 0)
    
          {
    
            // We need the center and radius of our circle
    
     
    
            Point2d center;
    
            double radius = 0;
    
     
    
            // Use our fast approximation algorithm to
    
            // calculate the center and radius of our
    
            // circle to within 1% (calling the function
    
            // with 100 iterations gives 10%, calling it
    
            // with 10K gives 1%)
    
     
    
            BadoiuClarksonIteration(
    
              pts2d, 10000, out center, out radius
    
            );
    
     
    
            // Get our center point in WCS (on the plane
    
            // of our UCS)
    
     
    
            Point3d cen3d = pl.EvaluatePoint(center);
    
     
    
            // Create the circle and add it to the drawing
    
     
    
            return new Circle(
    
              cen3d, ucs.Zaxis, radius * (1.0 + buffer)
    
            );
    
          }
    
          return null;
    
        }
    
     
    
        // Algorithm courtesy (and copyright of) Frank Nielsen
    
        // http://blog.informationgeometry.org/article.php?id=164
    
     
    
        public void BadoiuClarksonIteration(
    
          List<Point2d> set, int iter,
    
          out Point2d cen, out double rad
    
        )
    
        {
    
          // Choose any point of the set as the initial
    
          // circumcenter
    
     
    
          cen = set[0];
    
          rad = 0;
    
     
    
          for (int i = 0; i < iter; i++)
    
          {
    
            int winner = 0;
    
            double distmax = (cen - set[0]).Length;
    
     
    
            // Maximum distance point
    
     
    
            for (int j = 1; j < set.Count; j++)
    
            {
    
              double dist = (cen - set[j]).Length;
    
              if (dist > distmax)
    
              {
    
                winner = j;
    
                distmax = dist;
    
              }
    
            }
    
            rad = distmax;
    
     
    
            // Update
    
     
    
            cen =
    
              new Point2d(
    
                cen.X + (1.0 / (i + 1.0)) * (set[winner].X - cen.X),
    
                cen.Y + (1.0 / (i + 1.0)) * (set[winner].Y - cen.Y)
    
              );
    
          }
    
        }
    
      }
    
    } 
  • 相关阅读:
    几个常用排序的代码实现堆排序|快排|归并排序 Marathon
    0647回文子串 Marathon
    任意输入一个日期输出是当年的第几天星期几
    从输入URL到浏览器显示页面发生了什么
    常用链接整理
    computed 与 method
    将博客搬至CSDN
    leetcode_Two Sum
    VC++6.0与Office2010冲突解决方案
    C&C++_malloc函数
  • 原文地址:https://www.cnblogs.com/shangdishijiao/p/15675066.html
Copyright © 2020-2023  润新知