Привожу ужасно-кошмарный пример, когда производится перебор сегментов полилинии и проверяется, лежит ли точка на сегменте. Пара процедур VertexAdd и VertexDel добавляет и удаляет вершинку в полилинию. Линейный сегмент разбивается на два линейных сегмента. Дуговой – на два дуговых с сохранением центра и радиуса дуг. Полилиния должна быть нулевой ширины. Для указания точки и выбора объекта здесь используются чисто ВБА-шные методы Utility.GetPoint и SelectAtPoint это накладывает некоторые ограничения и неудобства по сравнению с возможностями ЛИСПа. Впрочем, без ЛИСПа не обошлось. Для большей наглядности, можно нажав Enter выбрать полилинию, тогда будут подсвечены крестиками все вершинки (это делает ЛИСП процедура), а затем можно более целенаправленно указать точку на полилинии для добавления или удаления вершинки.
Вторая пара процедур PolySegmentArcToLine и PolySegmentLineToArc работают соответственно своему названию. Принцип у всех четырёх процедур схожий, и много повторяющегося кода. Может кому захочется ужать это дело. А у меня пока это занимает почти двадцать страниц кода. Бесплатный подарок братьям по разуму!
PS-->У меня AC2002.
Sub VertexAdd()
‘JS
‘jackstone@mail.ru
‘Добавление вершинки в полилинию.
Dim pLWP As AcadLWPolyline, pT, pE As AcadEntity
Dim pACD As AcadDocument
Dim pSS As AcadSelectionSet
Dim pAV() As Double, pNV As Long, i As Long, pNN As Long
Dim pX As Double, pY As Double, pXs As Double, pYs As Double, pXe As Double, pYe As Double
Dim pB As Double, pL As Double, pB1 As Double, pB2 As Double
Dim pF As Boolean, pFGR As Boolean, pSR As Long
Dim pTS() As Double
Set pACD = ThisDrawing
On Error Resume Next
pT = pACD.Utility.GetPoint(, vbCrLf & "Укажи полилинию в точке или Enter для выбора полилинии, а затем указания точки...")
If Err Then
Err.Clear
pACD.Utility.GetEntity pE, pT, vbCrLf & "Укажи полилинию "
If Err Then
Err.Clear: Exit Sub
Else
If TypeName(pE) = "IAcadLWPolyline" Then
Set pLWP = pE
GrMarkVertexes pLWP, 2
pFGR = True
pT = pACD.Utility.GetPoint(, vbCrLf & "Укажи точку ")
If Err Then Err.Clear: beep: GrClear pACD: Exit Sub
Else
PrintMessage vbCrLf & "Указанный объект не полилиния!"
Exit Sub
End If
End If
Else
Prepare.SelSet pACD, "AA", pSS
pSS.SelectAtPoint pT
If pSS.Count > 0 And (TypeName(pSS.Item(0)) = "IAcadLWPolyline") Then
Set pLWP = pSS(0): pSS.Delete
Else
PrintMessage vbCrLf & "Указанный объект не полилиния!"
pSS.Delete: Exit Sub
End If
End If
JSGet.ArrVertexes pLWP, pAV, pNV
pX = pT(0): pY = pT(1)
For i = 0 To pNV - 1
pXs = pAV(i, 0): pYs = pAV(i, 1)
pXe = pAV(i + 1, 0): pYe = pAV(i + 1, 1)
pB = pAV(i, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
pNN = i
pF = True
Exit For
Case 1, 2
PrintMessage vbCrLf & "Точка совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
End Select
Next i
If Not pF And pLWP.Closed Then
pXs = pAV(pNV, 0): pYs = pAV(pNV, 1)
pXe = pAV(0, 0): pYe = pAV(0, 1)
pB = pAV(pNV, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
pNN = pNV
pF = True
Case 1, 2
PrintMessage vbCrLf & " Точка совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
End Select
End If
If Not pF Then
PrintMessage vbCrLf & "Не смог найти точку."
If pFGR Then GrClear pACD
Exit Sub
End If
ReDim pTS(0 To pNV * 2 + 3)
For i = 0 To pNN
pTS(i * 2) = pAV(i, 0)
pTS(i * 2 + 1) = pAV(i, 1)
Next i
pTS((pNN + 1) * 2) = pX
pTS((pNN + 1) * 2 + 1) = pY
For i = pNN + 1 To pNV
pTS(i * 2 + 2) = pAV(i, 0)
pTS(i * 2 + 3) = pAV(i, 1)
Next i
pLWP.Coordinates = pTS
For i = 0 To pNN - 1
pLWP.SetBulge i, pAV(i, 4)
Next i
pLWP.SetBulge pNN, pB1
pLWP.SetBulge pNN + 1, pB2
For i = pNN + 1 To pNV
pLWP.SetBulge i + 1, pAV(i, 4)
Next i
If pFGR Then GrClear pACD
End Sub
Sub VertexDel()
‘JS
‘jackstone@mail.ru
‘Удаление вершинки из полилинии.
Dim pLWP As AcadLWPolyline, pT, pE As AcadEntity
Dim pACD As AcadDocument
Dim pSS As AcadSelectionSet
Dim pAV() As Double, pNV As Long, i As Long, pNN As Long
Dim pX As Double, pY As Double, pXs As Double, pYs As Double, pXe As Double, pYe As Double
Dim pB As Double, pL As Double, pB1 As Double, pB2 As Double
Dim pF As Boolean, pFGR As Boolean, pSR As Long, pCmnt As String
Dim pTS() As Double
Dim p1Xs As Double, p1Ys As Double, p1Xe As Double, p1Ye As Double, p1B As Double
Dim p2Xs As Double, p2Ys As Double, p2Xe As Double, p2Ye As Double, p2B As Double
Const pPrec As Double = 0.000001
Dim pRet As String
Set pACD = ThisDrawing
On Error Resume Next
pT = pACD.Utility.GetPoint(, vbCrLf & "Укажи полилинию в точке или Enter для выбора полилинии, а затем указания точки...")
If Err Then
Err.Clear
pACD.Utility.GetEntity pE, pT, vbCrLf & "Укажи полилинию "
If Err Then
Err.Clear: Exit Sub
Else
If TypeName(pE) = "IAcadLWPolyline" Then
Set pLWP = pE
GrMarkVertexes pLWP, 2
pFGR = True
pT = pACD.Utility.GetPoint(, vbCrLf & "Укажи точку ")
If Err Then Err.Clear: beep: GrClear pACD: Exit Sub
Else
PrintMessage vbCrLf & " Указанный объект не полилиния!"
Exit Sub
End If
End If
Else
PrepareSelSet pACD, "AA", pSS
pSS.SelectAtPoint pT
If pSS.Count > 0 And (TypeName(pSS.Item(0)) = "IAcadLWPolyline") Then
Set pLWP = pSS(0): pSS.Delete
Else
PrintMessage vbCrLf & " Указанный объект не полилиния!"
pSS.Delete: Exit Sub
End If
End If
JSGet.ArrVertexes pLWP, pAV, pNV
If pNV = 1 Then
PrintMessage vbCrLf & "В полилинии только две вершинки. Нечего удалять!"
If pFGR Then GrClear pACD
Exit Sub
End If
pX = pT(0): pY = pT(1)
For i = 0 To pNV - 1
pXs = pAV(i, 0): pYs = pAV(i, 1)
pXe = pAV(i + 1, 0): pYe = pAV(i + 1, 1)
pB = pAV(i, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
PrintMessage vbCrLf & "Точка не совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
Case 1
pNN = i
pF = True
Exit For
End Select
Next i
If Not pF Then
pXs = pAV(pNV, 0): pYs = pAV(pNV, 1)
pXe = pAV(0, 0): pYe = pAV(0, 1)
pB = pAV(pNV, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
PrintMessage vbCrLf & "Точка не совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
Case 1
pNN = pNV
pF = True
End Select
End If
If Not pF Then
PrintMessage vbCrLf & "Не смог найти вершинку."
If pFGR Then GrClear pACD
Exit Sub
End If
If pNN = 0 Then
'Требуется удалить первую вершинку.
If Not pLWP.Closed Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNV - 1
pTS(i * 2) = pAV(i + 1, 0)
pTS(i * 2 + 1) = pAV(i + 1, 1)
Next i
pLWP.Coordinates = pTS
For i = 1 To pNV
pLWP.SetBulge i - 1, pAV(i, 4)
Next i
If pFGR Then GrClear pACD
Exit Sub
End If
p1Xs = pAV(pNV, 0): p1Ys = pAV(pNV, 1)
p1Xe = pAV(0, 0): p1Ye = pAV(0, 1)
p1B = pAV(pNV, 4)
p2Xs = pAV(0, 0): p2Ys = pAV(0, 1)
p2Xe = pAV(1, 0): p2Ye = pAV(1, 1)
p2B = pAV(0, 4)
If SegmentsUnite(pPrec, p1Xs, p1Ys, p1Xe, p1Ye, p2Xe, p2Ye, p1B, p2B, pB, pCmnt) Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNV - 1
pTS(i * 2) = pAV(i + 1, 0)
pTS(i * 2 + 1) = pAV(i + 1, 1)
Next i
pLWP.Coordinates = pTS
For i = 1 To pNV - 1
pLWP.SetBulge i - 1, pAV(i, 4)
Next i
pLWP.SetBulge pNV - 1, pB
If pFGR Then GrClear pACD
Exit Sub
Else
PrintMessage vbCrLf & pCmnt
pACD.Utility.InitializeUserInput 128, "Yes"
pRet = pACD.Utility.GetKeyword(vbCrLf & "Удалить? ")
If Err Then Err.Clear: Exit Sub
If pRet = "Yes" Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNV - 1
pTS(i * 2) = pAV(i + 1, 0)
pTS(i * 2 + 1) = pAV(i + 1, 1)
Next i
pLWP.Coordinates = pTS
For i = 1 To pNV
pLWP.SetBulge i - 1, pAV(i, 4)
Next i
If pFGR Then GrClear pACD
Exit Sub
End If
End If
ElseIf pNN = pNV Then
'Требуется удалить последнюю вершинку.
If Not pLWP.Closed Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNV - 1
pTS(i * 2) = pAV(i, 0)
pTS(i * 2 + 1) = pAV(i, 1)
Next i
pLWP.Coordinates = pTS
For i = 1 To pNV - 1
pLWP.SetBulge i, pAV(i, 4)
Next i
If pFGR Then GrClear pACD
Exit Sub
End If
p1Xs = pAV(pNV - 1, 0): p1Ys = pAV(pNV - 1, 1)
p1Xe = pAV(pNV, 0): p1Ye = pAV(pNV, 1)
p1B = pAV(pNV - 1, 4)
p2Xs = pAV(pNV, 0): p2Ys = pAV(pNV, 1)
p2Xe = pAV(0, 0): p2Ye = pAV(0, 1)
p2B = pAV(pNV, 4)
If SegmentsUnite(pPrec, p1Xs, p1Ys, p1Xe, p1Ye, p2Xe, p2Ye, p1B, p2B, pB, pCmnt) Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNV - 1
pTS(i * 2) = pAV(i, 0)
pTS(i * 2 + 1) = pAV(i, 1)
Next i
pLWP.Coordinates = pTS
For i = 0 To pNV - 1
pLWP.SetBulge i, pAV(i, 4)
Next i
pLWP.SetBulge pNV - 1, pB
If pFGR Then GrClear pACD
Exit Sub
Else
PrintMessage vbCrLf & pCmnt
pACD.Utility.InitializeUserInput 128, "Yes"
pRet = pACD.Utility.GetKeyword(vbCrLf & "Удалить?(Y-да / Enter - нет) ")
If Err Then Err.Clear: Exit Sub
If pRet = "Yes" Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNV - 1
pTS(i * 2) = pAV(i, 0)
pTS(i * 2 + 1) = pAV(i, 1)
Next i
pLWP.Coordinates = pTS
For i = 1 To pNV - 1
pLWP.SetBulge i, pAV(i, 4)
Next i
If pFGR Then GrClear pACD
Exit Sub
End If
End If
Else
'Удаление вершинки из серединки
p1Xs = pAV(pNN - 1, 0): p1Ys = pAV(pNN - 1, 1)
p1Xe = pAV(pNN, 0): p1Ye = pAV(pNN, 1)
p1B = pAV(pNN - 1, 4)
p2Xs = pAV(pNN, 0): p2Ys = pAV(pNN, 1)
p2Xe = pAV(pNN + 1, 0): p2Ye = pAV(pNN + 1, 1)
p2B = pAV(pNN, 4)
If SegmentsUnite(pPrec, p1Xs, p1Ys, p1Xe, p1Ye, p2Xe, p2Ye, p1B, p2B, pB, pCmnt) Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNN - 1
pTS(i * 2) = pAV(i, 0)
pTS(i * 2 + 1) = pAV(i, 1)
Next i
For i = pNN + 1 To pNV
pTS(i * 2 - 2) = pAV(i, 0)
pTS(i * 2 - 1) = pAV(i, 1)
Next i
pLWP.Coordinates = pTS
For i = 0 To pNN - 2
pLWP.SetBulge i, pAV(i, 4)
Next i
pLWP.SetBulge pNN - 1, pB
For i = pNN To pNV - 1
pLWP.SetBulge i, pAV(i + 1, 4)
Next i
If pFGR Then GrClear pACD
Exit Sub
Else
PrintMessage vbCrLf & pCmnt
pACD.Utility.InitializeUserInput 128, "Yes"
pRet = pACD.Utility.GetKeyword(vbCrLf & "Удалить?(Y-да / Enter - нет) ")
If Err Then Err.Clear: Exit Sub
If pRet = "Yes" Then
ReDim pTS(0 To pNV * 2 - 1)
For i = 0 To pNN - 1
pTS(i * 2) = pAV(i, 0)
pTS(i * 2 + 1) = pAV(i, 1)
Next i
For i = pNN To pNV - 1
pTS(i * 2) = pAV(i + 1, 0)
pTS(i * 2 + 1) = pAV(i + 1, 1)
Next i
pLWP.Coordinates = pTS
For i = 0 To pNN
pLWP.SetBulge i, pAV(i, 4)
Next i
For i = pNN + 1 To pNV
pLWP.SetBulge i - 1, pAV(i, 4)
Next i
If pFGR Then GrClear pACD
Exit Sub
End If
End If
End If
If pFGR Then GrClear pACD
End Sub
Sub PolySegmentArcToLine()
‘JS
‘jackstone@mail.ru
‘Преобразование дугового сегмента в отрезок.
Dim pLWP As AcadLWPolyline, pT, pE As AcadEntity
Dim pACD As AcadDocument
Dim pSS As AcadSelectionSet
Dim pAV() As Double, pNV As Long, i As Long, pNN As Long
Dim pX As Double, pY As Double, pXs As Double, pYs As Double, pXe As Double, pYe As Double
Dim pB As Double, pL As Double, pB1 As Double, pB2 As Double
Dim pF As Boolean, pFGR As Boolean, pSR As Long
Dim pTS() As Double
Set pACD = ThisDrawing
On Error Resume Next
pT = pACD.Utility.GetPoint(, vbCrLf & " Укажи полилинию в точке или Enter для выбора полилинии, а затем указания точки...")
If Err Then
Err.Clear
pACD.Utility.GetEntity pE, pT, vbCrLf & " Укажи полилинию "
If Err Then
Err.Clear: Exit Sub
Else
If TypeName(pE) = "IAcadLWPolyline" Then
Set pLWP = pE
GrMarkVertexes pLWP, 2
pFGR = True
pT = pACD.Utility.GetPoint(, vbCrLf & "Укажи точку ")
If Err Then Err.Clear: beep: Exit Sub
Else
PrintMessage vbCrLf & "Указанный объект не полилиния!"
Exit Sub
End If
End If
Else
PrepareSelSet pACD, "AA", pSS
pSS.SelectAtPoint pT
If pSS.Count > 0 And (TypeName(pSS.Item(0)) = "IAcadLWPolyline") Then
Set pLWP = pSS(0): pSS.Delete
Else
PrintMessage vbCrLf & "Указанный объект не полилиния!"
pSS.Delete: Exit Sub
End If
End If
JSGet.ArrVertexes pLWP, pAV, pNV
pX = pT(0): pY = pT(1)
For i = 0 To pNV - 1
pXs = pAV(i, 0): pYs = pAV(i, 1)
pXe = pAV(i + 1, 0): pYe = pAV(i + 1, 1)
pB = pAV(i, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
pNN = i
pF = True
Exit For
Case 1, 2
PrintMessage vbCrLf & " Точка совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
End Select
Next i
If Not pF And pLWP.Closed Then
pXs = pAV(pNV, 0): pYs = pAV(pNV, 1)
pXe = pAV(0, 0): pYe = pAV(0, 1)
pB = pAV(pNV, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
pNN = pNV
pF = True
Case 1, 2
PrintMessage vbCrLf & "Точка совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
End Select
End If
If Not pF Then
PrintMessage vbCrLf & "Не смог найти точку."
If pFGR Then GrClear pACD
Exit Sub
End If
pLWP.SetBulge pNN, 0
If pFGR Then GrClear pACD
End Sub
Sub PolySegmentLineToArc()
‘JS
‘jackstone@mail.ru
‘Преобразование линейного сегмента в дуговой.
Dim pLWP As AcadLWPolyline, pE As AcadEntity
Dim pT As Variant, pTA As Variant, t0(1) As Double, t1(1) As Double
Dim pACD As AcadDocument
Dim pSS As AcadSelectionSet
Dim pAV() As Double, pNV As Long, i As Long, pNN As Long
Dim pX As Double, pY As Double, pXs As Double, pYs As Double, pXe As Double, pYe As Double
Dim pXa As Double, pYa As Double, pXc As Double, pYc As Double, pXx As Double, pYy As Double
Dim pB As Double, pL As Double, pB1 As Double, pB2 As Double, pD As Double
Dim pAS As Double, pAe As Double, pAa As Double, Alfa As Double, Gamma As Double
Dim pF As Boolean, pFGR As Boolean, pSR As Long
Dim pTS() As Double
Set pACD = ThisDrawing
On Error Resume Next
pT = pACD.Utility.GetPoint(, vbCrLf & "Укажи полилинию в точке или Enter для выбора полилинии, а затем указания точки...")
If Err Then
Err.Clear
pACD.Utility.GetEntity pE, pT, vbCrLf & "Укажи полилинию "
If Err Then
Err.Clear: Exit Sub
Else
If TypeName(pE) = "IAcadLWPolyline" Then
Set pLWP = pE
GrMarkVertexes pLWP, 2
pFGR = True
pT = pACD.Utility.GetPoint(, vbCrLf & "Укажи точку ")
If Err Then Err.Clear: beep: GrClear pACD: Exit Sub
Else
PrintMessage vbCrLf & "Указанный объект не полилиния!"
Exit Sub
End If
End If
Else
PrepareSelSet pACD, "AA", pSS
pSS.SelectAtPoint pT
If pSS.Count > 0 And (TypeName(pSS.Item(0)) = "IAcadLWPolyline") Then
Set pLWP = pSS(0): pSS.Delete
Else
PrintMessage vbCrLf & "Указанный объект не полилиния!"
pSS.Delete: Exit Sub
End If
End If
JSGet.ArrVertexes pLWP, pAV, pNV
pX = pT(0): pY = pT(1)
For i = 0 To pNV - 1
pXs = pAV(i, 0): pYs = pAV(i, 1)
pXe = pAV(i + 1, 0): pYe = pAV(i + 1, 1)
pB = pAV(i, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
pNN = i
pF = True
Exit For
Case 1, 2
PrintMessage vbCrLf & "Точка совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
End Select
Next i
If Not pF And pLWP.Closed Then
pXs = pAV(pNV, 0): pYs = pAV(pNV, 1)
pXe = pAV(0, 0): pYe = pAV(0, 1)
pB = pAV(pNV, 4)
pSR = PointLaysOnSegment(pX, pY, pXs, pYs, pXe, pYe, pB, pL, pB1, pB2)
Select Case pSR
Case -1
pNN = pNV
pF = True
Case 1, 2
PrintMessage vbCrLf & "Точка совпадает с вершиной."
If pFGR Then GrClear pACD
Exit Sub
End Select
End If
If Not pF Then
PrintMessage vbCrLf & "Не смог найти точку."
If pFGR Then GrClear pACD
Exit Sub
End If
pTA = pACD.Utility.GetPoint(, vbCrLf & "Укажи точку через которую провести дугу: ")
If Err Then
Err.Clear
If pFGR Then GrClear pACD
Exit Sub
End If
pXa = pTA(0): pYa = pTA(1)
pD = -pXs * pYe + pXs * pYa + pYs * pXe - pYs * pXa + pXa * pYe - pYa * pXe
If Abs(pD) < Prec Then
PrintMessage vbCrLf & "Нельзя построить дугу."
If pFGR Then GrClear pACD
Exit Sub
End If
pXc = 0.5 * ((pXa ^ 2 + pYa ^ 2) * (pYe - pYs) + (pXe ^ 2 + pYe ^ 2) * (pYs - pYa) + (pXs ^ 2 + pYs ^ 2) * (pYa - pYe)) / pD
pYc = -0.5 * ((pXs ^ 2 + pYs ^ 2) * (pXa - pXe) + (pXe ^ 2 + pYe ^ 2) * (pXs - pXa) + (pXa ^ 2 + pYa ^ 2) * (pXe - pXs)) / pD
t0(0) = pXs: t0(1) = pYs
t1(0) = pXe: t1(1) = pYe
Alfa = AngleTo_0_2pi(AngleFromXAxis(t0, t1))
t0(0) = pXc: t0(1) = pYc
t1(0) = pXs: t1(1) = pYs
pAS = AngleTo_0_2pi(AngleFromXAxis(t0, t1))
t1(0) = pXe: t1(1) = pYe
pAe = AngleTo_0_2pi(AngleFromXAxis(t0, t1))
t1(0) = pXa: t1(1) = pYa
pAa = AngleTo_0_2pi(AngleFromXAxis(t0, t1))
pXx = pXa - pXs: pYy = pYa - pYs
pYy = -pXx * Sin(Alfa) + pYy * Cos(Alfa)
If pYy < 0 Then
'Положительная кривизна
If pAS < pAe Then Gamma = pAe - pAS Else Gamma = Pi2 - pAS + pAe
pB = Tan(Gamma / 4)
Else
'Отрицательная кривизна
If pAS > pAe Then Gamma = pAS - pAe Else Gamma = Pi2 - pAe + pAS
pB = -Tan(Gamma / 4)
End If
pLWP.SetBulge pNN, pB
If pFGR Then GrClear pACD
End Sub
Далее идут вспомогательные процедуры и функции. Некоторые вынужден повторять. Что-то может забыл. Если чего не хватает - дошлю.
Продолжение следует...