AutoCAD VBA二次开发地形图多边形裁剪

    日常工作中,经常需要结地形图进行裁剪,如地形图的分幅,或者在地形图中裁出一块来使用。本文介绍利用AutoCAD二次开发工具VBA进行编程,实现地形图的多边形裁切。

1基础函数

Public filtertype As Variant, filterdata As Variant
Public xy1(0 To 2) As Double, xy2(0 To 2) As Double
Global Const PI As Double = 3.14159265358979
Global Const 格式 As String = "0.00"

Public Sub acaddoc()               '自动运行的宏  acaddoc
Dim MenuGroup0 As AcadMenuGroup          '菜单组
Dim newMenu As AcadPopupMenu             '菜单项
Dim newMenuItem As AcadPopupMenuItem     '菜单子项
On Error Resume Next
Set MenuGroup0 = ThisDrawing.Application.MenuGroups.Item(0)
Set newMenu = MenuGroup0.Menus.Add("地形图裁切")
Set newMenuItem = newMenu.AddMenuItem(newMenu.Count + 1, "裁切点状图元", "-vbarun TrimmingPoint" & vbCr)
Set newMenuItem = newMenu.AddMenuItem(newMenu.Count + 1, "裁切直线", "-vbarun TrimmingLine" & vbCr)
Set newMenuItem = newMenu.AddMenuItem(newMenu.Count + 1, "裁切折线", "-vbarun TrimmingPLine" & vbCr)
Set MenuItem = newMenu.AddSeparator(newMenu.Count + 1)
Set newMenuItem = newMenu.AddMenuItem(newMenu.Count + 1, "合并裁切命令(保留内部)", "-vbarun 合并裁切命令内" & vbCr)
Set newMenuItem = newMenu.AddMenuItem(newMenu.Count + 1, "合并裁切命令(保留外部)", "-vbarun 合并裁切命令外" & vbCr)
newMenu.InsertInMenuBar (ThisDrawing.Application.MenuBar.Count + 1)  ' 菜单条上显示菜单
End Sub

Function VBApath() As String
Dim fs, objFile
Set fs = CreateObject("Scripting.FileSystemObject")
Set objFile = fs.GetFile(ThisDrawing.Application.VBE.ActiveVBProject.FileName)
VBApath = objFile.ParentFolder & "\"
End Function

'计算距离的函数
Function 距离(ByVal x1 As Double, ByVal y1 As Double, ByVal z1 As Double, ByVal x2 As Double, ByVal y2 As Double, ByVal z2 As Double) As Double
  距离 = Sqr((x1 - x2) ^ 2 + (y1 - y2) ^ 2 + (z1 - z2) ^ 2)
End Function

'计算距离的函数
Function 距离_2P(ByVal Axy As Variant, ByVal Bxy As Variant) As Double
Dim X As Double, Y As Double, Z As Double
X = Bxy(0) - Axy(0): Y = Bxy(1) - Axy(1)
If UBound(Axy) = 2 And UBound(Bxy) = 2 Then
    Z = Bxy(2) - Axy(2)
Else
    Z = 0
End If
距离_2P = Sqr(X ^ 2 + Y ^ 2 + Z ^ 2)
End Function

'
Public Function 多段线坐标集合(PLine As AcadEntity) As Collection
Dim i As Integer, j As Integer
Dim pts As New Collection
Dim pt(0 To 4) As Double     '0-2记录坐标x、y、z;3记录节点到起点的距离;4记录其他信息
Dim pt1 As Variant
xy = PLine.Coordinates
r = IIf(PLine.ObjectName = "AcDbPolyline", 2, 3)
For i = 0 To UBound(xy) Step r
    pt(0) = xy(i): pt(1) = xy(i + 1): pt(2) = 0
    If i > 0 Then
        pt1 = pts(i / r)
        pt(3) = pt1(3) + 距离_2P(pt1, pt)   '节点到起点的距离
    End If
    pts.Add pt
Next
Set 多段线坐标集合 = pts
End Function

Public Function 点集合转2d序列(pts As Collection) As Double()
Dim xyList() As Double, i As Integer
ReDim xyList(pts.Count * 2 - 1)
Dim pt As Variant
For Each pt In pts
    xyList(i) = pt(0)
    xyList(i + 1) = pt(1)
    i = i + 2
Next
点集合转2d序列 = xyList
End Function

Public Function 点集合转3d序列(pts As Collection) As Double()
Dim xyList() As Double, i As Integer
ReDim xyList(pts.Count * 3 - 1)
Dim pt As Variant
For Each pt In pts
    xyList(i) = pt(0)
    xyList(i + 1) = pt(1)
    xyList(i + 2) = pt(2)
    i = i + 3
Next
点集合转3d序列 = xyList
End Function

Public Sub 点集合创建多段线(pts As Collection, Layer As String, Color As Long, Width As Double, Closed As Boolean)
Dim xyList() As Double
Dim Polyobj As AcadPolyline
xyList = 点集合转3d序列(pts)
Set Polyobj = ThisDrawing.ModelSpace.AddPolyline(xyList)
    Polyobj.Layer = Layer
    Polyobj.Color = Color
    Polyobj.ConstantWidth = Width
    Polyobj.Closed = Closed
    Polyobj.Update
End Sub

Public Sub 点集合创建轻便线(pts As Collection, Layer As String, Color As Long, Width As Double, Closed As Boolean)
Dim xyList() As Double
Dim LWpolyline As AcadLWPolyline
xyList = 点集合转2d序列(pts)
Set LWpolyline = ThisDrawing.ModelSpace.AddLightWeightPolyline(xyList)
    LWpolyline.Layer = Layer
    LWpolyline.Color = Color
    LWpolyline.ConstantWidth = Width
    LWpolyline.Closed = Closed
    LWpolyline.Update
End Sub

Function 点在线段中间(xyA As Variant, xyB As Variant, xyc As Variant) As Boolean
Dim a As Double, b As Double, c As Double
c = 距离_2P(xyA, xyB)
a = 距离_2P(xyA, xyc)
b = 距离_2P(xyB, xyc)
If Abs(c - a - b) < 0.0001 Then
    点在线段中间 = True
Else
    点在线段中间 = False
End If
End Function

Public Function 点在多边形内(pts As Collection, xyP As Variant) As Boolean
Dim xyA As Variant, xyB As Variant
Dim xyc(0 To 1) As Double, xyJ(0 To 1) As Double
Dim xyW(0 To 1) As Double
Dim i As Integer, r As Integer
Dim 交点数 As Integer
xyc(0) = xyP(0) + 100000: xyc(1) = xyP(1)     'xyp-xyc 为假定射线
xyW(0) = xyP(0): xyW(1) = xyP(1)
For i = 1 To pts.Count
    r = IIf(i = pts.Count, 1, i + 1)
    xyA = pts(i)
    xyB = pts(r)
    '纵坐标不在边(i)两点区间内,边(i)与射线无交点
    If Abs(xyP(1) - xyA(1)) + Abs(xyP(1) - xyB(1)) <> Abs(xyA(1) - xyB(1)) Then GoTo 50
    
    '边(i)与射线平行时,只有当边(i)与射线重合,且横坐标在边(i)两点区间内时才有交点。
    If xyA(1) = xyB(1) Then
       If xyP(1) = xyB(1) And Abs(xyP(0) - xyA(0)) + Abs(xyP(0) - xyB(0)) = Abs(xyA(0) - xyB(0)) Then 交点数 = 交点数 + 1
       GoTo 50
    End If
    
    '交点在xyp点的左边,边(i)与射线无交点
    If 两直线交点(xyA, xyB, xyW, xyc, xyJ) = True Then
        If xyJ(0) >= xyP(0) Then 交点数 = 交点数 + 1
    End If
50: Next
If 交点数 Mod 2 = 0 Then
    点在多边形内 = False
Else
    点在多边形内 = True
End If
End Function

Public Function 两直线交点(L1sarP As Variant, L1endP As Variant, L2sarP As Variant, L2endP As Variant, Rxy() As Double) As Boolean
Dim A1 As Double, B1 As Double, C1 As Double
Dim A2 As Double, B2 As Double, C2 As Double, D As Double
Dim xy3(0 To 2) As Double
A1 = L1endP(0) - L1sarP(0): B1 = L1sarP(1) - L1endP(1): C1 = L1endP(1) * L1sarP(0) - L1sarP(1) * L1endP(0)
A2 = L2endP(0) - L2sarP(0): B2 = L2sarP(1) - L2endP(1): C2 = L2endP(1) * L2sarP(0) - L2sarP(1) * L2endP(0)
D = A1 * B2 - A2 * B1
If Abs(D) < 0.00001 Then
    两直线交点 = False
    Exit Function
End If
两直线交点 = True
Rxy(1) = (B1 * C2 - B2 * C1) / D
Rxy(0) = (C1 * A2 - C2 * A1) / D
End Function

2点与文字

    点和文字如果使用插入点进行判断简单很多,但是,地形图分幅时,有时需保留范围线周边的符号与文字,由人工判断移位或破幅注记。

2.1流程

1)创建一个点与文字的选择集;

2)利用多边形创建一个范围内的点与文字的选择集;

3)计算二个选择集的差集;

4)删除差集内的所有图元。

1.2代码

Sub TrimmingPoint()    '裁切点状图元
Dim mode As Boolean, n As Integer
Dim WPline As AcadEntity
Dim basePnt As Variant
n = InputBox("1:保留多边形内部;" & vbCrLf & "-1:保留多边形外部;", "请输入保留图元的类型!", 1, 1)
mode = IIf(n = 1, True, False)
ThisDrawing.Utility.GetEntity WPline, basePnt, "选取范围线"
'地形图分幅时,可利用设定条件(图层、颜色等)创建选择集自动获取
Call 点状图元范围修剪(WPline, mode)
ThisDrawing.Regen acAllViewports   '刷新
End Sub

Public Sub 点状图元范围修剪(WPline As AcadEntity, mode As Boolean)   '符号与文字
mode =True时,保留多边形内的图元,删除多边形外的图元。
mode =False时,保留多边形外的图元,删除多边形内的图元。
Dim FType(0 To 3) As Integer, FData(0 To 3)
Dim Lobj(0) As AcadEntity
Dim 范围内选择集 As AcadSelectionSet
Dim 全部选择集 As AcadSelectionSet
On Error Resume Next
ThisDrawing.SelectionSets.Item("全部选择集").Delete
Err.Clear
Set 全部选择集 = ThisDrawing.SelectionSets.Add("全部选择集")
    FType(0) = -4: FData(0) = "<or"
    FType(1) = 0: FData(1) = "Text"
    FType(2) = 0: FData(2) = "INSERT"
    FType(3) = -4: FData(3) = "or>"
    filtertype = FType: filterdata = FData
全部选择集.Select acSelectionSetAll, , , filtertype, filterdata
'Debug.Print 全部选择集.Count
'2)利用多边形创建一个范围内的点与文字的选择集;
ThisDrawing.SelectionSets.Item("范围内选择集").Delete
Set 范围内选择集 = ThisDrawing.SelectionSets.Add("范围内选择集")
Dim pts As New Collection
Set pts = 多段线坐标集合(WPline)
Dim xyList() As Double
xyList = 点集合转3d序列(pts)

范围内选择集.SelectByPolygon acSelectionSetCrossingPolygon, xyList, filtertype, filterdata
'Debug.Print 范围内选择集.Count
Select Case mode
       Case True   '保存内部、删除外部对象
            '全部选择集移出范围内对象
            For Each Lobj(0) In 范围内选择集
                 全部选择集.RemoveItems Lobj
            Next
            'Debug.Print 全部选择集.Count
            '册除全部选择集的对象
            For Each Lobj(0) In 全部选择集
                Lobj(0).Delete
            Next
       Case False   '保存外部、删除内部对象
            '册除范围内选择集中的对象
            For Each Lobj(0) In 范围内选择集
                Lobj(0).Delete
            Next
End Select
范围内选择集.Delete
全部选择集.Delete
End Sub

3直线

 

3.1流程

1)创建一个直线的选择集;

2)历遍选择集中的每一根直线与范围线相交,如上图黑色线为范围线,红色线为被剪切直线,直线与范围线有4个交点,形成了5根新的直线。

3)历遍每根新直线,中点是否在范转内。

中点在范围内A

保留范围内的图元B

A Eqv B

动作

True

True

True

创建直线

True

false

false

false

true

false

false

false

true

创建直线

4)删除原直线

3.2代码

Sub TrimmingLine()    '裁切直线
Dim mode As Boolean, n As Integer
Dim WPline As AcadEntity
Dim basePnt As Variant
n = InputBox("1:保留多边形内部;" & vbCrLf & "-1:保留多边形外部;", "请输入保留图元的类型!", 1, 1)
mode = IIf(n = 1, True, False)
ThisDrawing.Utility.GetEntity WPline, basePnt, "选取范围线"
'地形图分幅时,可利用设定条件(图层、颜色等)创建选择集自动获取
Call 裁切直线(WPline, mode)
ThisDrawing.Regen acAllViewports   '刷新
End Sub

Public Sub 裁切直线(WPline As AcadEntity, mode As Boolean)
Dim FType(0) As Integer, FData(0)
Dim Lobj As AcadLine
Dim 直线选择集 As AcadSelectionSet
Dim n As Integer, i As Integer
Dim 交点 As Variant, xy0 As Variant
Dim Plpts As New Collection
Dim pts As New Collection
Dim pt(0 To 4) As Double, pt1 As Variant
On Error Resume Next
Set Plpts = 多段线坐标集合(WPline)
ThisDrawing.SelectionSets.Item("直线选择集").Delete
Err.Clear
Set 直线选择集 = ThisDrawing.SelectionSets.Add("直线选择集")
    FType(0) = 0: FData(0) = "Line"
    filtertype = FType: filterdata = FData
直线选择集.Select acSelectionSetAll, , , filtertype, filterdata

For Each Lobj In 直线选择集
    交点 = Lobj.IntersectWith(WPline, acExtendNone)
    '创建直线分段的坐标集合,以点到直线起点的距离进行排序
    Do While pts.Count > 0
        pts.Remove Index:=1
    Loop
    xy0 = Lobj.StartPoint
    pt(0) = xy0(0): pt(1) = xy0(1): pt(3) = 0
    pts.Add pt
    For n = 0 To UBound(交点) Step 3
        pt(0) = 交点(n): pt(1) = 交点(n + 1)
        pt(3) = 距离(xy0(0), xy0(1), 0, 交点(n), 交点(n + 1), 0)
        For i = 1 To pts.Count
            pt1 = pts(i)
            If pt(3) < pt1(3) Then
                pts.Add pt, Before:=i
                GoTo 20
            End If
        Next
        pts.Add pt
20: Next
    xy0 = Lobj.EndPoint
    pt(0) = xy0(0): pt(1) = xy0(1)
    pts.Add pt
    
    For n = 1 To pts.Count - 1
        pt1 = pts(n)
        xy1(0) = pt1(0): xy1(1) = pt1(1)
        pt1 = pts(n + 1)
        xy2(0) = pt1(0): xy2(1) = pt1(1)
        Dim xy3(0 To 2) As Double
        xy3(0) = (xy1(0) + xy2(0)) / 2
        xy3(1) = (xy1(1) + xy2(1)) / 2
        If 点在多边形内(Plpts, xy3) Eqv mode = True Then
            Dim LineObj As AcadLine
            Set LineObj = ThisDrawing.ModelSpace.AddLine(xy1, xy2)
                LineObj.Layer = Lobj.Layer
                LineObj.Color = Lobj.Color
           '根据需要,可以设置新直线的其他特性,还可以继承原直线的扩展属性。
        End If
    Next
    Lobj.Delete
Next
直线选择集.Delete
End Sub

4折线

 

4.1流程

1)创建一个多段线的选择集;

2)历遍选择集中的每一根多段线与范围线相交

3)把交点插入到多段线的坐标集合中,每二个交点之间为一条新的多段线,如上图,黑色线为范围线,红色线为被剪切线,被剪切线原有8个节点,插入交点后有10个点,形成了三条线。

4)定义关键点:

当交点-交点之间只有二个节点时,如上图中的1-2,关键点为二点的中点

当交点-交点之间多于二个节点时,如上图中的3-8关键点为第二点,即3号点

5)历遍每条新线,用关键点判断新线是否在在范转内

关键点在范围内A

保留范围内的图元B

A Eqv B

动作

True

True

True

创建新线

True

false

false

false

true

false

false

false

true

创建新线

6)删除原折线

4.2代码

Sub TrimmingPLine()    '裁切折线
Dim mode As Boolean, n As Integer
Dim WPline As AcadEntity
Dim basePnt As Variant
n = InputBox("1:保留多边形内部;" & vbCrLf & "-1:保留多边形外部;", "请输入保留图元的类型!", 1, 1)
mode = IIf(n = 1, True, False)
ThisDrawing.Utility.GetEntity WPline, basePnt, "选取范围线"
'地形图分幅时,可利用设定条件(图层、颜色等)创建选择集自动获取
Call 裁切折线(WPline, mode)
ThisDrawing.Regen acAllViewports   '刷新
End Sub

Public Sub 裁切折线(WPline As AcadEntity, mode As Boolean)
Dim FType(0) As Integer, FData(0)
Dim Lobj As AcadEntity
Dim 折线选择集 As AcadSelectionSet
Dim n As Integer, i As Integer, j As Integer
Dim 交点 As Variant, xy0 As Variant
Dim Plpts As New Collection
Dim pt(0 To 4) As Double, pt1 As Variant, pt2 As Variant
On Error Resume Next
Set Plpts = 多段线坐标集合(WPline)
ThisDrawing.SelectionSets.Item("折线选择集").Delete
Err.Clear
Set 折线选择集 = ThisDrawing.SelectionSets.Add("折线选择集")
    FType(0) = 0: FData(0) = "*PolyLine"
    filtertype = FType: filterdata = FData
折线选择集.Select acSelectionSetAll, , , filtertype, filterdata

For Each Lobj In 折线选择集
    '如果这条线是范围线则跳过
    If Lobj.Handle = WPline.Handle Then GoTo 100
    交点 = Lobj.IntersectWith(WPline, acExtendNone)
    '创建折线分段的坐标集合,以点到折线起点的距离进行排序
    Dim pts As New Collection
    Set pts = 多段线坐标集合(Lobj)
    '1、无交点:
    If UBound(交点) < 1 Then
         pt2 = pts(1)
         xy1(0) = pt2(0)
         xy1(1) = pt2(1)
         If 点在多边形内(Plpts, xy1) Eqv mode = True Then
             GoTo 100   '保留对象
         Else
             GoTo 50    '删除对象
         End If
    End If
      
    '2、有交点:把交点插入到多段线坐标集合
    For n = 0 To UBound(交点) Step 3
        pt(0) = 交点(n): pt(1) = 交点(n + 1)
        For i = 2 To pts.Count
            pt1 = pts(i - 1)
            pt2 = pts(i)
            If 点在线段中间(pt1, pt2, pt) Then
                pt(3) = pt1(3) + 距离_2P(pt1, pt)
                pt(4) = 1  '标设这个点是分段点
                For j = 1 To pts.Count
                    pt1 = pts(i)
                    If pt(3) < pt1(3) Then
                        pts.Add pt, Before:=i
                        GoTo 20
                    End If
                Next
            End If
        Next
        pts.Add pt
20: Next
    Dim 起点 As Integer, 终点 As Integer
    Dim 新线 As New Collection
    起点 = 1
    For n = 1 To pts.Count
        pt1 = pts(n)
        If pt1(4) = 1 Or n = pts.Count Then
            终点 = n
            Do While 新线.Count > 0
                新线.Remove Index:=1
            Loop
            For i = 起点 To 终点
                pt1 = pts(i)
                新线.Add pt1
            Next
            起点 = 终点
            '如果新线只有二个点,判断二个点的中点是否在范围内
            pt1 = 新线(1)
            pt2 = 新线(2)
            If 新线.Count = 2 Then
                xy1(0) = (pt1(0) + pt2(0)) / 2
                xy1(1) = (pt1(1) + pt2(1)) / 2
            Else  '如果新线多于二个点,判断第二个点是否在范围内
                xy1(0) = pt2(0)
                xy1(1) = pt2(1)
            End If
            If 点在多边形内(Plpts, xy1) Eqv mode = True Then
                Select Case Lobj.ObjectName
                       Case "AcDbPolyline"
                            Call 点集合创建多段线(新线, Lobj.Layer, Lobj.Color, Lobj.ConstantWidth, False)
                       Case "AcDbWlPolyline"
                            Call 点集合创建轻便线(新线, Lobj.Layer, Lobj.Color, Lobj.ConstantWidth, False)
                End Select
            End If
            起点 = 终点
           '根据需要,可以设置新直线的其他特性,还可以继承原直线的扩展属性。
        End If
    Next
50: Lobj.Delete
100:
Next
折线选择集.Delete
End Sub

5合并裁切命令

Public Sub 合并裁切命令内()
Dim WPline As AcadEntity
Dim basePnt As Variant
On Error Resume Next
ThisDrawing.Utility.GetEntity WPline, xy, "选取范围线"
Call 点状图元范围修剪(WPline, True)
Call 裁切直线(WPline, True)
Call 裁切折线(WPline, True)
ThisDrawing.Regen acAllViewports   '刷新
End Sub


Public Sub 合并裁切命令外()
Dim WPline As AcadEntity
Dim basePnt As Variant
On Error Resume Next
ThisDrawing.Utility.GetEntity WPline, xy, "选取范围线"
Call 点状图元范围修剪(WPline, False)
Call 裁切直线(WPline, False)
Call 裁切折线(WPline, False)
ThisDrawing.Regen acAllViewports   '刷新
End Sub

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AutoCAD VBA二次开发是指在AutoCAD软件基础上,利用VBA(Visual Basic for Applications)语言进行自动化编程开发的过程。VBA是一种用于编写宏的编程语言,它结合了Visual Basic语言的特性和AutoCAD的API接口,可以实现对AutoCAD进行扩展和定制。 AutoCAD VBA二次开发能够实现很多功能,比如自动化生成和修改绘图,快速批量处理绘图数据,自定义绘图命令等。通过编写VBA代码,可以利用AutoCAD中的对象模型进行绘图元素的创建、编辑和删除,也可以通过控制命令行和图形界面实现交互操作。 在进行AutoCAD VBA二次开发时,需要熟悉AutoCAD的对象模型和API接口。通过编写VBA宏,可以直接在AutoCAD中运行代码,实现自动化操作。VBA还提供了丰富的内置函数和方法,用于处理各种图形和数据,可以让开发者根据自己的需求来编写功能齐全的代码。 AutoCAD VBA二次开发的优势在于可以利用AutoCAD强大的绘图和编辑功能,结合VBA的简洁易用性进行快速开发。开发者可以根据自己的需求制定开发计划和目标,用VBA代码实现自定义的功能和工具,提高工作效率和图纸质量。 总而言之,AutoCAD VBA二次开发是一种基于AutoCAD软件和VBA语言的自动化编程开发方法,可以实现自定义功能,提高工作效率。通过学习和熟练掌握AutoCAD的对象模型和VBA语言,可以进行自主开发和定制,满足不同用户的绘图需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值