lanbior 发表于 2023-9-20 00:34:29

C#实现多段线偏移的算法(例子自动创建240mm的墙体)

前端给媳妇编写一个快速偏移多段线并填充图案的函数,刚开始以为多段线偏移的开发还比较简单,但是弄了以后发现多段线的偏移的处理算法设计到凸角的算法和一些特殊端点的处理。弄好以后,想着有类似需求的朋友们可以参考参考。里面可能还有情况没有考虑到,有新发现的,可以多多交流
还是录一个屏说明一下吧。本人非专业程序人员,只是一名机械设计者。函数有不足的地方多多交流。




   //快速设计墙体,默认墙体厚度240mm
    public class CreateMYWallCommand
    {
      
      public void CreateWallCommand()
      {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            PromptIntegerOptions options = new PromptIntegerOptions("\n请选择向外偏移还是向内偏移 (1 向外偏移, 2 向内偏移): ");
            options.AllowNone = false;
            options.AllowZero = false;
            options.DefaultValue = 1;
            PromptIntegerResult result = ed.GetInteger(options);

            if (result.Status != PromptStatus.OK)
            {
                ed.WriteMessage("Invalid input or command canceled.");
                return; // 退出命令
            }

            int userInput = result.Value;

            PromptEntityResult peResult = ed.GetEntity("请选折一条多段线: ");
            if (peResult.Status != PromptStatus.OK)
                return;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // Open the selected polyline
                Entity ent = tr.GetObject(peResult.ObjectId, OpenMode.ForRead) as Entity;
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                BlockTableRecord btr = tr.GetObject(bt, OpenMode.ForWrite) as BlockTableRecord;

                if (ent is Polyline polyline)
                {
                  // Specify the offset distance
                  double offsetDistance = 240; // Modify as needed

                  // 定义并初始化 actualOffsetDistance 变量
                  double actualOffsetDistance = 0.0;

                  // Create a new polyline for offset
                  Polyline newPolyline = new Polyline();

                  for (int i = 0; i < polyline.NumberOfVertices; i++)
                  {
                        Point2d vertex = polyline.GetPoint2dAt(i);
                        Vector2d offsetVector = new Vector2d(0, 0); // Initialize a zero vector

                        int nextIndex = (i + 1) % polyline.NumberOfVertices;
                        int prevIndex = (i + polyline.NumberOfVertices - 1) % polyline.NumberOfVertices;

                        Point2d nextVertex = polyline.GetPoint2dAt(nextIndex);
                        Point2d prevVertex = polyline.GetPoint2dAt(prevIndex);

                        Vector2d nextDir = (nextVertex - vertex).GetNormal();
                        Vector2d prevDir = (prevVertex - vertex).GetNormal();

                        offsetVector = (nextDir + prevDir).GetNormal();

                        // 处理多边形内角大于180度的情况
                        double crossProduct = nextDir.X * prevDir.Y - nextDir.Y * prevDir.X;

                        // 计算夹角的一半的正弦值
                        double angle = Math.Acos(nextDir.DotProduct(prevDir));
                        double halfAngleSin = Math.Sin(angle / 2.0);

                        // 计算 actualOffsetDistance
                        actualOffsetDistance = offsetDistance / halfAngleSin;

                        if (crossProduct <0)
                        {
                            offsetVector = -offsetVector;
                        }

                        // 处理多边形内角180度的情况
                        if (crossProduct == 0) // 夹角为180度的情况
                        {
                            offsetVector = nextDir.GetPerpendicularVector();
                            actualOffsetDistance = offsetDistance;
                        }

                        // 处理起点和终点所在线段方向
                        if (i == 0)
                        {
                            offsetVector = nextDir.GetPerpendicularVector();// 垂直于起点线段方向的向量
                            actualOffsetDistance = offsetDistance;
                        }
                        else if (i == polyline.NumberOfVertices - 1)
                        {
                            offsetVector = -prevDir.GetPerpendicularVector();// 垂直于起点线段方向的向量
                            actualOffsetDistance = offsetDistance;
                        }


                        // 获取起始向量
                        Vector2d startVector = polyline.GetPoint2dAt(1) - polyline.GetPoint2dAt(0);
                        // 判断起点向量方向并选择不同命令

                        if (startVector.X >= 0)
                        {
                            if (userInput == 1)
                            {
                              offsetVector = offsetVector * actualOffsetDistance; // 向内偏移
                            }
                            else
                            {
                              offsetVector = -offsetVector * actualOffsetDistance;

                            }
                        }
                        else
                        {

                            if (userInput == 2)
                            {
                              offsetVector = -offsetVector * actualOffsetDistance; // 向内偏移
                            }
                            else
                            {
                              offsetVector = offsetVector * actualOffsetDistance;
                            }
                        }


                        // Offset the vertex position
                        Point2d offsetVertex = vertex + offsetVector;
                        newPolyline.AddVertexAt(i, offsetVertex, 0, 0, 0);
                  }

                  // Add the new polyline to the model space
                  BlockTableRecord currentSpace = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                  currentSpace.AppendEntity(newPolyline);
                  tr.AddNewlyCreatedDBObject(newPolyline, true);

                  // 连接起点和终点形成封闭区域
                  Point2d startVertex = newPolyline.GetPoint2dAt(0);
                  Point2d endVertex = newPolyline.GetPoint2dAt(newPolyline.NumberOfVertices - 1);

                  Line closeStartLine = new Line(
                        new Point3d(polyline.GetPoint2dAt(0).X, polyline.GetPoint2dAt(0).Y, 0),
                        new Point3d(startVertex.X, startVertex.Y, 0));

                  Line closeEndLine = new Line(
                        new Point3d(polyline.GetPoint2dAt(polyline.NumberOfVertices - 1).X, polyline.GetPoint2dAt(polyline.NumberOfVertices - 1).Y, 0),
                        new Point3d(endVertex.X, endVertex.Y, 0));

                  btr.AppendEntity(closeStartLine);
                  btr.AppendEntity(closeEndLine);
                  tr.AddNewlyCreatedDBObject(closeStartLine, true);
                  tr.AddNewlyCreatedDBObject(closeEndLine, true);

                  Point2d startLineStartPoint = new Point2d(closeStartLine.StartPoint.X, closeStartLine.StartPoint.Y);
                  Point2d startLineEndPoint = new Point2d(closeStartLine.EndPoint.X, closeStartLine.EndPoint.Y);
                  Point2d endLineStartPoint = new Point2d(closeEndLine.StartPoint.X, closeEndLine.StartPoint.Y);
                  Point2d endLineEndPoint = new Point2d(closeEndLine.EndPoint.X, closeEndLine.EndPoint.Y);

                  //创建一个新的封闭多段线,用于填充图案
                  Polyline closedPolyline = new Polyline();
                  closedPolyline.Closed = true;

                  //添加开始线的起点和终点,closedPolyline.NumberOfVertices指的是添加到集合的最后
                  closedPolyline.AddVertexAt(closedPolyline.NumberOfVertices, startLineStartPoint, 0, 0, 0);
                  closedPolyline.AddVertexAt(closedPolyline.NumberOfVertices, startLineEndPoint, 0, 0, 0);

                  //添加偏移多段线的各个点
                  //1获取偏移多段线的各个点坐标
                  List<Point2d> newPolylineVertices = new List<Point2d>();
                  for (int i = 0; i < newPolyline.NumberOfVertices; i++)
                  {
                        Point2d vertex = new Point2d(newPolyline.GetPoint3dAt(i).X, newPolyline.GetPoint3dAt(i).Y);
                        newPolylineVertices.Add(vertex);
                  }
                  //2循环添加偏移多段线的各个点坐标
                  for (int i = 0; i < newPolylineVertices.Count; i++)
                  {
                        closedPolyline.AddVertexAt(closedPolyline.NumberOfVertices, newPolylineVertices, 0, 0, 0);
                  }

                  //添加终点线的起点和终点,
                  closedPolyline.AddVertexAt(closedPolyline.NumberOfVertices, endLineEndPoint, 0, 0, 0);
                  closedPolyline.AddVertexAt(closedPolyline.NumberOfVertices, endLineStartPoint, 0, 0, 0);

                  //添加原多段线的各个点
                  //1获取原多段线的各个点坐标
                  List<Point2d> polylineVertices = new List<Point2d>();
                  for (int i = 0; i < polyline.NumberOfVertices; i++)
                  {
                        Point2d vertex = new Point2d(polyline.GetPoint3dAt(i).X, polyline.GetPoint3dAt(i).Y);
                        polylineVertices.Add(vertex);
                  }

                  //2循环添加原多段线的各个点坐标
                  for (int i = polylineVertices.Count - 1; i >= 0; i--)
                  {
                        closedPolyline.AddVertexAt(closedPolyline.NumberOfVertices, polylineVertices, 0, 0, 0);
                  }

                  // 将封闭多段线添加到模型空间中
                  ObjectId closedPolylineId = btr.AppendEntity(closedPolyline);
                  tr.AddNewlyCreatedDBObject(closedPolyline, true);

                  // 创建填充对象
                  Hatch hatch = new Hatch();
                  ObjectIdCollection objectIdCollection = new ObjectIdCollection();
                  objectIdCollection.Add(closedPolylineId);
                  hatch.SetHatchPattern(HatchPatternType.PreDefined, "AR-CONC"); // 设置填充图案类型
                  hatch.AppendLoop(HatchLoopTypes.Default, objectIdCollection);

                  // 添加填充到模型空间中
                  btr.AppendEntity(hatch);
                  tr.AddNewlyCreatedDBObject(hatch, true);

                }
                tr.Commit();
            }
      }
    }

xj6019 发表于 2023-9-20 08:05:24

感谢分享,想学点c#无奈可以参考学习的代码实在太少了,只能懵逼中摸索一点点

acoff 发表于 2023-9-20 08:11:29

大神真厉害,学习了

lxl217114 发表于 2023-9-22 11:30:02

这个前端很优秀

9609759 发表于 2023-9-22 18:12:10

如果不考虑偏移后顶点数量减少的情况下,算出多段线上每一个顶点法向垂直方向的角度,将角度和偏移距离计算出新的点,新点连成一个新的线段,再将原线段的凸度赋值给新的线段,就能得到偏移后的线段了

d1742647821 发表于 2023-10-3 23:18:49

是自带的偏移函数不好用吗

tiancao100 发表于 2024-8-9 20:30:22

机械 配 建筑

gzxl 发表于 2024-8-9 23:22:05

本帖最后由 gzxl 于 2024-8-9 23:23 编辑

xj6019 发表于 2023-9-20 08:05
感谢分享,想学点c#无奈可以参考学习的代码实在太少了,只能懵逼中摸索一点点
我虽然接触c#比较短期,c#的教程和学习代码很多的。

tender138 发表于 2024-11-6 02:26:08

感谢分享,想学点c#无奈可以参考学习的代码实在太少了
页: [1]
查看完整版本: C#实现多段线偏移的算法(例子自动创建240mm的墙体)