取消

AutoCAD面域转多段线

AutoCAD面域转多段线


问题

AutoCAD2014版本没有面域转为多段线的直接方法,需要自己实现。

解决

主要逻辑,通过Brep获取到面域的边界线,再根据边界线重新生成多段线,把曲线(Arc)转为多段线时bulge的计算方法。

这个算法有问题,在边界线的方向不一致时(一部分顺时针绘制,一部分逆时针绘制),计算的边界不对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/// <summary>
/// 面域转为多段线
/// </summary>
/// <param name="region"></param>
/// <returns></returns>
public static Polyline ToPolylines(this Region region)
{
    Polyline pl = new Polyline();
    var brep = new Brep(region);
    var edges = brep.Edges.ToList();
    var list = new List<Curve>();
    //获取边界线
    foreach (var edge in edges)
    {
        var eCurve = (ExternalCurve3d)edge.Curve;

        if (eCurve.IsCircularArc || eCurve.IsLineSegment)
        {
            var curve = Polyline.CreateFromGeCurve(eCurve.NativeCurve);
            list.Add(curve);
            //var angle = arc.EndAngle - arc.StartAngle;
            //if (angle < 0)
            //    angle += Math.PI * 2.0;
            //double bulge = Math.Tan(angle / 4.0);
        }
    }
    //获取到的边界可能不是第二条线的起点对应第一条线的终点,而是第一条线的起点对应第二条线的终点,线的顺序是反向,
    //这里重新排序,保证边界线是顺序相接的
    if (list.Count > 1 && list[0].StartPoint == list[1].EndPoint)
    {
        list.Reverse();
    }
    //重新绘制多段线
    foreach (var curve in list)
    {
        if (curve is Line)
        {
            if (pl.NumberOfVertices == 0)
            {
                pl.AddVertexAt(pl.NumberOfVertices, curve.StartPoint.ToPoint2d(), 0, 0, 0);
            }
            pl.AddVertexAt(pl.NumberOfVertices, curve.EndPoint.ToPoint2d(), 0, 0, 0);
        }
        else if (curve is Arc)
        {
            var arc = curve as Arc;
            var diffAngle = arc.EndAngle - arc.StartAngle;
            double bulge;
            if (diffAngle < 0)
            {
                diffAngle += 2 * Math.PI;
                bulge = Math.Tan(diffAngle / 4) * -1;
            }
            else
            {
                bulge = Math.Tan(diffAngle / 4);
            }
            //弧线要根据正向或反向弧计算bulge
            bulge *= arc.Normal.Z;
            if (bulge != 0)
            {
                if (pl.NumberOfVertices != 0)
                {
                    pl.RemoveVertexAt(pl.NumberOfVertices - 1);
                }
                pl.AddVertexAt(pl.NumberOfVertices, curve.StartPoint.ToPoint2d(), bulge, 0, 0);
            }
            pl.AddVertexAt(pl.NumberOfVertices, curve.EndPoint.ToPoint2d(), 0, 0, 0);
        }
    }
    return pl;
}

修正后的算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/// <summary>
/// 面域转为多段线
/// </summary>
/// <param name="region"></param>
/// <returns></returns>
public static Polyline ToPolyline(this Region region)
{
    Polyline pl = new Polyline();
    var brep = new Brep(region);
    var edges = brep.Edges.ToList();
    var list = new List<PolylinePoint>();
    var curEdge = edges[0];
    Point3d? endPoint = curEdge.Curve.StartPoint;
    //获取边界线点
    do
    {
        Point3d? startPoint = null;
        curEdge = edges.FirstOrDefault(i => endPoint == i.Curve.StartPoint);
        //可能出现线段首尾相反的情况,这里做一个标记,如果首尾相反,需要把线段反向,如果是直线可以直接替换起始点,曲线还需要修改法向量相反。
        var isReverse = 1;
        if (curEdge != null)
        {
            endPoint = curEdge.Curve.EndPoint;
            startPoint = curEdge.Curve.StartPoint;
        }
        else
        {
            curEdge = edges.FirstOrDefault(i => i != curEdge && endPoint == i.Curve.EndPoint);
            endPoint = curEdge?.Curve.StartPoint;
            startPoint = curEdge?.Curve.EndPoint;
            isReverse = -1;
        }

        if (curEdge == null)
        {
            break;
        }
        //每次查找到之后剔除边界,否则查找到最后一条后又循环第一条
        edges.Remove(curEdge);
        double bulge;
        var eCurve = (ExternalCurve3d)curEdge.Curve;
        //曲线需要计算角度
        if (eCurve.IsCircularArc)
        {
            var arc = Arc.CreateFromGeCurve(eCurve.NativeCurve) as Arc;
            bulge = GetBulge(arc) * isReverse;
        }
        else
        {
            bulge = 0;
        }

        if (bulge != 0 || list.Count == 0)
        {
            if (list.Count != 0)
            {
                list.RemoveAt(list.Count - 1);
            }
            list.Add(new PolylinePoint { Point2d = startPoint.Value.ToPoint2d(), Bulge = bulge });
        }

        list.Add(new PolylinePoint { Point2d = endPoint.Value.ToPoint2d(), Bulge = 0 });

    } while (true);
    //重新绘制多段线
    foreach (var polylinePoint in list)
    {
        pl.AddVertexAt(pl.NumberOfVertices, polylinePoint.Point2d, polylinePoint.Bulge, 0, 0);
    }
    pl.Closed = true;
    return pl;
}
/// <summary>
/// 获取角度
/// </summary>
/// <param name="arc"></param>
/// <returns></returns>
public static double GetBulge(this Arc arc)
{
    var diffAngle = arc.EndAngle - arc.StartAngle;
    double bulge;
    if (diffAngle < 0)
    {
        diffAngle += 2 * Math.PI;
        bulge = Math.Tan(diffAngle / 4) * -1;
    }
    else
    {
        bulge = Math.Tan(diffAngle / 4);
    }

    //弧线要根据正向或反向弧计算bulge
    bulge *= arc.Normal.Z;
    return bulge;
}

参考资料

本文会经常更新,请阅读原文: https://dashenxian.github.io/post/AutoCAD%E9%9D%A2%E5%9F%9F%E8%BD%AC%E5%A4%9A%E6%AE%B5%E7%BA%BF ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 小神仙 (包含链接: https://dashenxian.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (125880321@qq.com)

登录 GitHub 账号进行评论