Тема: VBA. Программа поиска и индикации разрывов в контуре

Программа "CONTUR" предназначена для поиска и индикации свободных концов линий, что может пригодиться ,например, при выполнении штриховки.В качестве линий контура распознаются такие примитивы как: LINE,MLINE,PLINE,SPLINE,ARC.
   Для инсталляции программы необходимо в директории C\:Program Files создать фолдер Contur и поместить туда файлы: Contur.dvb, Start_Contur.lsp. Содержимое этих файлов приводится ниже.
   Для запуска программы следует загрузить в текущий чертеж файл:C:\Program Files\Contur\Start_Contur.lsp и ввести в командной строке команду BP(break point).В ответ на предложение "Select objects:" пользователь на экране выбирает те линии, которые должны составлять замкнутый контур. По окончании набора программа обозначает кружками свободные концы линий, цвет - "magenta" ,Layer - 0. Стереть эти кружки можно командой BPD(Break Point Delete).

'File: Contur.dvb
Option Explicit
Public Sub SelectLine()
    Dim PointSet As New Collection
    Dim BreakPoints As New Collection
    Dim BreakCircle As New Collection
    Call DeleteCircles
'Create the selection set
    Dim ssetObj As AcadSelectionSet
    On Error Resume Next
    Set ssetObj = ThisDrawing.SelectionSets.Add("SET_LINES")
    If (Err = -2145320851) Then 'set "SET_LINES" exist
        Set ssetObj = ThisDrawing.SelectionSets("SET_LINES")
    End If
    On Error GoTo 0
    ssetObj.Clear
    ssetObj.SelectOnScreen
    Call TerminPoint(ssetObj, PointSet)
    Set ssetObj = Nothing
    Call BreakPoint(PointSet, BreakPoints)
    Call IndicationPoint(BreakCircle, BreakPoints)
    Call SelSet(BreakCircle)
End Sub
Private Sub TerminPoint(ssetObj As AcadSelectionSet, PointSet As Collection)
    Dim Entity As AcadEntity
    Dim LineName As String
    For Each Entity In ssetObj
        LineName = Entity.ObjectName
        Select Case (LineName)
            Case "AcDbLine": Call LineTerminPoint(Entity, PointSet)
            Case "AcDbPolyline": Call PolylineTerminPoint(Entity, PointSet)
            Case "AcDbMline": Call MlineTerminPoint(Entity, PointSet)
            Case "AcDbSpline": Call SplineTerminPoint(Entity, PointSet)
            Case "AcDbArc": Call ArcTerminPoint(Entity, PointSet)
        End Select
    Next Entity
End Sub
Private Sub LineTerminPoint(Entity As AcadLine, PointSet As Collection)
    Dim TerminPoint() As Double
    TerminPoint = Entity.startPoint
    PointSet.Add TerminPoint()
    TerminPoint = Entity.EndPoint
    PointSet.Add TerminPoint()
End Sub
Private Sub PolylineTerminPoint(Entity As AcadLWPolyline, PointSet As Collection)
    Dim ArrayPoint() As Double
    Dim TerminPoint() As Double
    Dim nArray As Long
    ArrayPoint = Entity.Coordinates
    ReDim TerminPoint(2)
    TerminPoint(2) = 0
'start point:
    TerminPoint(0) = ArrayPoint(0)
    TerminPoint(1) = ArrayPoint(1)
    PointSet.Add TerminPoint()
'end point:
    nArray = UBound(ArrayPoint)
    TerminPoint(0) = ArrayPoint(nArray - 1)
    TerminPoint(1) = ArrayPoint(nArray)
    PointSet.Add TerminPoint()
End Sub
Private Sub MlineTerminPoint(Entity As AcadMLine, PointSet As Collection)
    Dim ArrayPoint() As Double
    Dim TerminPoint() As Double
    Dim nArray As Long
    ArrayPoint = Entity.Coordinates
    ReDim TerminPoint(2)
    TerminPoint(2) = 0
'start point:
    TerminPoint(0) = ArrayPoint(0)
    TerminPoint(1) = ArrayPoint(1)
    PointSet.Add TerminPoint()
'end point:
    nArray = UBound(ArrayPoint)
    TerminPoint(0) = ArrayPoint(nArray - 2)
    TerminPoint(1) = ArrayPoint(nArray - 1)
    PointSet.Add TerminPoint()
End Sub
Private Sub SplineTerminPoint(Entity As AcadSpline, PointSet As Collection)
    Dim ArrayPoint() As Double
    Dim TerminPoint() As Double
    Dim nArray As Long
    ArrayPoint = Entity.ControlPoints
    ReDim TerminPoint(2)
    TerminPoint(2) = 0
'start point:
    TerminPoint(0) = ArrayPoint(0)
    TerminPoint(1) = ArrayPoint(1)
    PointSet.Add TerminPoint()
'end point:
    nArray = UBound(ArrayPoint)
    TerminPoint(0) = ArrayPoint(nArray - 2)
    TerminPoint(1) = ArrayPoint(nArray - 1)
    PointSet.Add TerminPoint()
End Sub
Private Sub ArcTerminPoint(Entity As AcadArc, PointSet As Collection)
    Dim TerminPoint() As Double
    TerminPoint = Entity.startPoint
    PointSet.Add TerminPoint()
    TerminPoint = Entity.EndPoint
    PointSet.Add TerminPoint()
End Sub
Private Sub BreakPoint(PointSet As Collection, BreakPoints As Collection)
    Dim TerminPoint() As Double
    Dim Point() As Double
    Dim i As Integer
    Dim j As Integer
    Dim k As Boolean
    For i = 1 To PointSet.Count
        k = True
        TerminPoint() = PointSet(i)
'interior loop:
        For j = 1 To PointSet.Count
            Point() = PointSet(j)
            If (ToAgree(TerminPoint(), Point()) And (i <> j)) Then
                k = False
                Exit For
            End If
        Next j
        If (k) Then
            BreakPoints.Add TerminPoint()
        End If
   Next i
   Set PointSet = Nothing
End Sub
Private Function ToAgree(TerminPoint() As Double, Point() As Double) As Boolean
    ToAgree = False
    If ((CSng(TerminPoint(0)) = CSng(Point(0))) And (CSng(TerminPoint(1)) = CSng(Point(1)))) Then
        ToAgree = True
    End If
End Function
Private Sub IndicationPoint(BreakCircle As Collection, BreakPoints As Collection)
    Dim i As Integer
    Dim Point() As Double
'clear a circle set:
    For i = 1 To BreakCircle.Count
        BreakCircle.Remove (i)
    Next i
    For i = 1 To BreakPoints.Count
        Point() = BreakPoints(i)
        Call MakePoint(Point(), BreakCircle)
    Next i
End Sub
Private Sub MakePoint(BreakPoints() As Double, BreakCircle As Collection)
    Dim ObjectCircle As AcadCircle
    Dim radius As Double
    radius = ThisDrawing.ActiveViewport.Height / 100
    ' Create the point
    Set ObjectCircle = ThisDrawing.ModelSpace.AddCircle(BreakPoints, radius)
    ObjectCircle.Color = acMagenta
    ObjectCircle.Highlight True
    ObjectCircle.Layer = "0"
    BreakCircle.Add ObjectCircle
End Sub
Private Sub SelSet(BreakCircle As Collection)
    Dim i As Integer
'Create the selection set:
    Dim ssetCircle As AcadSelectionSet
    On Error Resume Next
    Set ssetCircle = ThisDrawing.SelectionSets.Add("SET_CIRCLES")
    If (Err = -2145320851) Then 'set "SET_CIRCLES" exist
        Set ssetCircle = ThisDrawing.SelectionSets("SET_CIRCLES")
    End If
    ssetCircle.Clear
'array circles:
    Dim CircleArray() As AcadEntity
    ReDim CircleArray(0 To BreakCircle.Count - 1)
    For i = 1 To (BreakCircle.Count)
       Set CircleArray(i - 1) = BreakCircle(i)
    Next i
    ssetCircle.AddItems CircleArray
End Sub
Public Sub DeleteCircles()
    Dim ssetCircle As AcadSelectionSet
'Create the selection set:
    On Error Resume Next
    Set ssetCircle = ThisDrawing.SelectionSets.Add("SET_CIRCLES")
    If (Err = -2145320851) Then 'set "SET_CIRCLES" exist
        Set ssetCircle = ThisDrawing.SelectionSets("SET_CIRCLES")
    End If
'erase a circles:
    If (ssetCircle.Count > 0) Then
        ssetCircle.Update
        If (Err = -2145386420) Then 'object was erased
            Exit Sub
        End If
        ssetCircle.Erase
       'ThisDrawing.Regen acActiveViewport
    End If
End Sub

End file:Contur.dvb
File:Start_Contur.lsp

(defun C:BP (/ PATH)
  (setq PATH "C:\\Program Files\\Contur\\Contur.dvb")
  (vl-vbaload PATH)
  (vl-vbarun "SelectLine")
  (gc)
)                    ;BP
(defun C:BPD (/ PATH)
  ;;(setq PATH "C:\\Program Files\\Contur\\Contur.dvb")
  ;;(vl-vbaload PATH)
  (vl-vbarun "DeleteCircles")
  (gc)
);BPD                
                    

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
Пишет:

Wrong file format

Это он на Contur.dvb

Re: VBA. Программа поиска и индикации разрывов в контуре

> Forma
Я ведь выложил не файлы, а тексты кода. Соответствующие файлы надо создать и заполнить этим текстом...

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
Так в чём прикол? Можно подробнее. Лисп программа Start_Contur.lsp загружается (_appload Start_Contur.lsp successfully loaded), а при запуске пишет (Command: bp
; error: Automation Error. Problem in loading DVB file).

Re: VBA. Программа поиска и индикации разрывов в контуре

> Valery Brelovsky
А в чем прикол? Загружаемый файл должен находиться вот в этом самом месте:
PATH "C:\\Program Files\\Contur\\Contur.dvb")
и смотри - > LeonidSN (2005-08-27 14:47:11)

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
Часто приходится работать со штриховками и когда нужно искать где не замыкается. Короче приходится проверять все пересечения. Программа интересует не ради любопытства. Я сделал как у вас написано в начале. Из первого сделал файл Contur.dvb, с нижней лисповский Start_Contur.lsp, сделал папку с соответствующим именем,где их и разместил. Через _appload загрузил, через BP запустил и в ответ получил - ; error: Automation Error. Problem in loading DVB file). Так где я пошёл не правильно?

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
Программа доходит до строки:

.........................
................................
Call TerminPoint(ssetObj, PointSet)
    Set ssetObj = Nothing
    Call BreakPoint(PointSet, BreakPoints)
    Call IndicationPoint(BreakCircle, BreakPoints)
    Call SelSet(BreakCircle)
End Sub
....................
.....................

И пишет:"Compile error:
    Sub or Function not defined
Что делать?

Re: VBA. Программа поиска и индикации разрывов в контуре

> Forma
Затрудняюсь сказать, почему у вас возникает сбой, программа обкатанная, работает не на одной машине...
Если хотите, скиньте адресок, пришлю исходники.
Попробуйте их запустить.

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
forma<sobaka>nightmail.ru

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
Спасибо. Получил. Все заработало с первого раза. Программа нужная. Большое спасибо. Хорошая работа!

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
Действительно программа очень удобная и оригинал работает отлично. Спасибо большое.

Re: VBA. Программа поиска и индикации разрывов в контуре

Судя по описанию, программа очень полезная, но, к сожалению, запустить ее не удалось и у меня вопрос, что я делаю не так:
1.    Создаю текстовый файл:
'File: Contur.dvb
???????
End Sub (последняя подобная строчка)
2.    Переименовываю файл в Contur.dvb
3.    Создаю текстовый файл:
(defun C:BPD (/ PATH)
??????..
);BPD
4.    Переименовываю файл в Start_Contur.lsp
5.    Помещаю оба файла в C:\Program Files\Contur\
6.    Из АКАДа запускаю Start_Contur.lsp
Идет сообщение:
Команда: _appload Start_Contur.lsp успешно загружено.
Start_Contur.lsp успешно загружено.
7.    После ввода команды BP:
Инициализация системы VBA...; ошибка: Ошибка Automation. Проблемы при загрузке
DVB-файла
Думаю, если готовые файлы программы будут выложены на: http://dwg.ru/dwl/ , то буду благодарен не только я. Заранее спасибо.

Re: VBA. Программа поиска и индикации разрывов в контуре

> LeonidSN
dvb не загружается.

Re: VBA. Программа поиска и индикации разрывов в контуре

Я так думаю, что следующие строки можно засовывать в FAQ:
"Как загрузить приведенный код, если код на VBA?"
Примечание:
По максимуму приводятся команды, а не пути через меню или еще что-то, т.к. команды гарантированно будут работать, а меню может быть модифицировано. Команды приводятся для английской версии AutoCAD, но будут работать и для локализованных версий.
Меню приводятся только в тех случаях, когда без них не обойтись.
1. Запускаем AutoCAD и в ком.строке даем команду

Command: _.vbaide

2. В левом верхнем дереве (Project, как правило, находится именно там) правый щелчок мышкой, в контекстном меню выбирается Insert -> Module. Меню на английском языке, насколько мне известно, и для официально локализованных версий.
3. Скопировать текст кода из интернет-браузера и вставить его (без изменений) в новое окно.
4. После этого выполняется сохранение файла VBA-проекта (либо меню File -> Save, либо [Ctrl]+[S]). Имя файла и его местоположение - либо по вкусу, либо по требованиям, прописанным разработчиком. Оптимальный вариант - сохранять в папку (папки), прописанные в путях поддержки AutoCAD (сугубо imho).
5. Если разработана lisp-функция, выполняющая загрузку vba-проекта, то сохранить ее как lsp-файл. Если нет lisp-загрузчика, необходимо загрузить dvb-файл через выполнение команды

Command: _.appload

, и выполнять команду vba любым из возможных способов: либо [Alt]+[F8] (стандартное сокращение для запуска VBA-макросов), либо через командную строку

Command: _.vbastmt

. Более подробную информацию можно найти в справке и в интернете.
---
P.S. Если чего забыл / упустил / неправильно написал, сволочь этакая => ничего не работает, прошу подправить.

Re: VBA. Программа поиска и индикации разрывов в контуре

Спасибо LeonidSN за программу и Kpblc за инструкцию по загрузке кода на VBA, все OK.

Re: VBA. Программа поиска и индикации разрывов в контуре

Готовая программа выложена здесь:
http://vbamaker.narod.ru/Programs.html

Re: VBA. Программа поиска и индикации разрывов в контуре

> Денис
Еще за компанию: http://vbamaker.narod.ru/List2.html

Re: VBA. Программа поиска и индикации разрывов в контуре

Очень простая по идее и полезная штука оказалась. Не сразу обратил внимание. Большое спасибо!
Я её сильно подправил её под себя. Если не обидит, то вот мои несколько комментариев и исправлений.
1) Ошибка в процедуре

Private Sub IndicationPoint(BreakCircle As Collection, BreakPoints As Collection)
...
'clear a circle set:
    For i = 1 To BreakCircle.Count
        BreakCircle.Remove (i)
    Next i
...

Этот цикл доходил бы до половины и возникала бы ошибка. В данном случае это место проходит без сбоев только потому, что Count здесь всегда равен нулю!!!
Я обычно такие штуки делаю так:

    ii= Collection.Count
    For i = 1 To ii
        Collection.Remove 1
    Next i
...

Первый элемент в коллекции внутри такого цикла всегда будет в наличии.
Я этот участок кода просто выкинул.
2)Предлагаю заменить

Private Sub MakePoint(BreakPoints() As Double, BreakCircle As Collection)
...
    ... ThisDrawing.ActiveViewport.Height ...

на

... ThisDrawing.GetVariable("VIEWSIZE") ...

Этот "АктивВьюпорт.Хай" непонятно когда обновляется, поэтому размер маркеров непредсказуем. Переменная VIEWSIZE это как раз то, что тут требуется.
3)Предлагаю заменить Вашу процедуру BreakPoint на мою

Private Sub BreakPoint(vColPnt As Collection, vColBP As Collection)
Dim pV, pT(2) As Double
Dim pAF() As Boolean
Dim pAP() As Double
Dim i As Long, j As Long, n As Long, nn As Long
Dim pFreeEnd As Boolean
    nn = vColPnt.Count
    ReDim pAF(1 To nn)
    ThisDrawing.Utility.Prompt vbCrLf & "Подготовка массива точек." & vbCrLf
    ReDim pAP(1 To nn, 0 To 1)
    For i = 1 To nn
        pV = vColPnt(i)
        pAP(i, 0) = pV(0)
        pAP(i, 1) = pV(1)
    Next i
    n = 1
    For i = 1 To nn - 1
        n = n + 1
        If Not pAF(i) Then
            pFreeEnd = True
            For j = i + 1 To nn
                If Math.Abs(pAP(i, 0) - pAP(j, 0)) <= ddd _
                    And _
                    Math.Abs(pAP(i, 1) - pAP(j, 1)) <= ddd _
                Then
                    pFreeEnd = False
                    pAF(j) = True
                    Exit For
                End If
            Next j
            If pFreeEnd Then
                pT(0) = pAP(i, 0): pT(1) = pAP(i, 1)
                vColBP.Add pT
            End If
        End If
        ThisDrawing.Utility.Prompt vbCr & "Точек " & n & " из " & nn
        DoEvents
        If GetAsyncKeyState(VK_ESCAPE) Then
            Exit For
        End If
   Next i
   Set vColPnt = Nothing
End Sub

Эта процедура на два порядка быстрее работает при большом количестве объектов.
Угадай-ка почему??? (<-шутка)
Здесь у меня

Public Const ddd = 0.0000000001

А GetAsyncKeyState на форуме хорошо обсуждалась.
Стоит иметь в виду, что Utility.Prompt будет выводиться на экран если где то ранее сделано ThisDrawing.SetVariable "CMDECHO", 1.
Остальные изменения оставлю для себя.
Еще раз БОЛЬШОЕ СПАСИБО!