Тема: Как получить длину линии?

Хочу написать макрос на VBA, суммирующий длины всех выделенных линий и полилиний. и не могу :(

Re: Как получить длину линии?

Готовыйй вариант не устроит?
http://dwg.ru/dwl/116

Re: Как получить длину линии?

Немного не то. Мне нужно свойство или функция, возращающая длину полилинии или линии. Просто одно слово, а не целая готовая программа.

Re: Как получить длину линии?

object.Length

Re: Как получить длину линии?

Ну а для полилинии?
для объекта по имени "AcDbLine" это "Length"
для объекта по имени "AcDbArc" это "ArcLength"
для объекта по имени "AcDbPolyline" подобных свойств нет!!!!
А что если полилинию скопировать, взорвать и сложить длины получившихся линий и окружностей?
а затем их удалить.
Но как, при этом, мне отследить все объекты после "взрыва"? Какой командой взорваный объект можно "загнать" в массив получившихся объектов?

Re: Как получить длину линии?

Из хелпа
object.Length
object
Line, LightweightPolyline, Polyline, 3dPolyline
The object or objects this property applies to.
Length

Re: Как получить длину линии?

object.Explode
возврашхает массив содержашхийй елементы взрываемого обььекта

Re: Как получить длину линии?

Public Function PerimeterOb(Ob As AcadEntity) As Double
Dim VarTyp As String
    VarTyp = TypeName(Ob)
    Select Case VarTyp
    Case "IAcadLWPolyline": PerimeterOb = PerimeterLWP(Ob)
    Case "IAcadCircle": PerimeterOb = PerimeterCirc(Ob)
    Case "IAcadLine": PerimeterOb = PerimeterLine(Ob)
    Case "IAcadArc": PerimeterOb = PerimeterArc(Ob)
    Case Else
        PerimeterOb = 0
    End Select
End Function
Public Function PerimeterLWP(LWP As AcadLWPolyline) As Double
Dim vrt As Variant
Dim nn As Long, n As Long
Dim vP As Double
Dim t0(1) As Double, t1(1) As Double, tc(1) As Double
Dim kr As Double, lseg As Double
    vP = 0
    On Error Resume Next
        vrt = LWP.Coordinates
    If Err Then
        Err.Clear
    End If
    nn = UBound(vrt)
    For n = 0 To nn - 2 Step 2
        t0(0) = vrt(n): t0(1) = vrt(n + 1)
        kr = LWP.GetBulge(n / 2)
        t1(0) = vrt(n + 2): t1(1) = vrt(n + 3)
        vP = vP + LenSegmPL(t0, t1, kr)
    Next n
    If LWP.Closed Then
        t0(0) = vrt(nn - 1): t0(1) = vrt(nn)
        kr = LWP.GetBulge(Int(nn / 2))
        t1(0) = vrt(0): t1(1) = vrt(1)
        vP = vP + LenSegmPL(t0, t1, kr)
    End If
    PerimeterLWP = vP
End Function
Public Function PerimeterCirc(Cir As AcadCircle) As Double
Dim vP As Double
    vP = 0
    On Error Resume Next
        vP = Cir.Circumference
    If Err Then
        Err.Clear
    End If
    PerimeterCirc = vP
End Function
Public Function PerimeterLine(Line As AcadLine) As Double
Dim vP As Double
    vP = 0
    On Error Resume Next
        vP = Line.Length
    If Err Then
        Err.Clear
    End If
    PerimeterLine = vP
End Function
Public Function PerimeterArc(Arc As AcadArc) As Double
Dim vP As Double
    vP = 0
    On Error Resume Next
        vP = Arc.ArcLength
    If Err Then
        Err.Clear
    End If
    PerimeterArc = vP
End Function

Re: Как получить длину линии?

Да забыл!!! Тут еще используются:

Public Function LenSegmPL( _
    ByVal t0 As Variant, ByVal t1 As Variant, ByVal kr As Double, _
    Optional ta As Variant, Optional vR As Variant, _
    Optional vS As Variant, Optional vTC As Variant) As Double
Dim t2(1) As Double, t3 As Variant, tc As Variant
Dim t4(1) As Double, t5 As Variant
Dim dd As Double, d As Double, d1 As Double, sign As Integer
Dim Alfa As Double, beta As Double, Gamma As Double, r As Double
    dd = Distance(t0, t1)
    d = dd / 2
    d1 = d * kr
    If Abs(d1) >= Prec Then
        t2(0) = (t0(0) + t1(0)) / 2
        t2(1) = (t0(1) + t1(1)) / 2
        Alfa = AngleFromXAxis(t0, t1)
        d1 = d * (kr * kr - 1) / 2 / kr
        tc = PolarPoint(t2, Alfa - Pi_2, d1)
        r = Distance(t0, tc)
        Gamma = 4 * Atn(kr)
        sign = Sgn(kr)
        If Not IsMissing(ta) Then ta = Gamma
        If Not IsMissing(vR) Then vR = r
        If Not IsMissing(vS) Then vS = sign
        If Not IsMissing(vTC) Then vTC = tc
        LenSegmPL = Abs(Gamma) * r
   Else
        LenSegmPL = dd
        If Not IsMissing(ta) Then ta = Null
        If Not IsMissing(vR) Then vR = Null
        If Not IsMissing(vS) Then vS = Null
        If Not IsMissing(vTC) Then vTC = Null
    End If
End Function
Public Function AngleFromXAxis(t0 As Variant, t1 As Variant)
Dim x As Double, y As Double
    x = t1(0) - t0(0): y = t1(1) - t0(1)
    If x >= Abs(y) Then
        AngleFromXAxis = Atn(y / x)
    ElseIf -x >= Abs(y) Then
        AngleFromXAxis = Pi + Atn(y / x)
    ElseIf y >= Abs(x) Then
        AngleFromXAxis = Pi_2 + Atn(-x / y)
    Else
        AngleFromXAxis = Pi_3 + Atn(-x / y)
    End If
End Function

Re: Как получить длину линии?

Еще функции Distance({}, {}) не хватает!

Re: Как получить длину линии?

т.е. я вижу, что она встроенная, но у меня почему то, в VBA Автокаде её нет. Не знаете в чём может быть проблема?

Re: Как получить длину линии?

В конечном итоге я обошелся без Distance()
и у меня получилось вот что:

Public Sub LenLine()
    Dim Obj As AcadEntity
    Dim SelSet As AcadSelectionSet
    Dim SumLen As Double
    Dim A As Long
    Dim ObjExpl() As AcadEntity
On Error GoTo Control 'Ловим ошибку на тот случай, если пользователь нажмет "Esc"
    Set SelSet = ThisDrawing.SelectionSets.Add("Set") 'Создаем новый набор выбора, например с именем "Set"
On Error GoTo 0
    SelSet.SelectOnScreen 'Запрос на выбор примитивов
    If SelSet.Count = 0 Then GoTo Control
    For Each Obj In SelSet
        SumLen = SumLen + LenObj(Obj)
    Next Obj
    PrintMessage "Сумма длин " & SelSet.Count & " элементов = " & SumLen
Control:
    SelSet.Delete 'Удаляем набор выбора
End Sub
Private Function LenObj(Obj As AcadEntity) As Double
    Dim Ln As AcadLine
    Dim Pl As AcadLWPolyline  'AcadPolyline
    Dim Ci As AcadArc
    Dim ObjExpl() As AcadEntity
    Dim A As Long
    If Obj.ObjectName = "AcDbLine" Then
        Set Ln = Obj
        LenObj = Ln.Length
        Set Ln = Nothing
    ElseIf Obj.ObjectName = "AcDbPolyline" Then
        Set Pl = Obj
        ObjExpl() = Pl.Explode
        For A = 0 To UBound(ObjExpl()) - 1
            LenObj = LenObj + LenObj(ObjExpl(A))
            ObjExpl(A).Delete
        Next A
        ObjExpl(A).Delete
        Set Pl = Nothing
    ElseIf Obj.ObjectName = "AcDbArc" Then
        Set Ci = Obj
        LenObj = LenObj + Ci.ArcLength
        Set Ci = Nothing
    Else
        MsgBox "Объект с именем " & Obj.ObjectName & " не включен в расчет", vbCritical
    End If
    'Obj.Update'Обновляем объект
End Function
Public Sub PrintMessage(MessageString As String)
Dim pEchoVal As Integer
    pEchoVal = ThisDrawing.GetVariable("CMDECHO")
    ThisDrawing.SetVariable "CMDECHO", 1
    ThisDrawing.Utility.Prompt MessageString
    ThisDrawing.SetVariable "CMDECHO", pEchoVal
End Sub

Re: Как получить длину линии?

Прошу прощения, в коде есть ошибка в блоке:
   

ElseIf Obj.ObjectName = "AcDbPolyline" Then
        Set Pl = Obj
        ObjExpl() = Pl.Explode
        For A = 0 To UBound(ObjExpl()) - 1
            LenObj = LenObj + LenObj(ObjExpl(A))
            ObjExpl(A).Delete
        Next A
        ObjExpl(A).Delete
        Set Pl = Nothing

заменить на:
   

ElseIf Obj.ObjectName = "AcDbPolyline" Then
        Set Pl = Obj
        ObjExpl() = Pl.Explode
        For A = 0 To UBound(ObjExpl())
            LenObj = LenObj + LenObj(ObjExpl(A))
            ObjExpl(A).Delete
        Next A
        Set Pl = Nothing

Пользуйтесь!

Re: Как получить длину линии?

Прошу прощения!!! У меня интернет не круглосуточный. Я не углядел отсутствие Distance.

Public Function Distance(ByVal t0 As Variant, ByVal t1 As Variant) As Double
        Distance = Sqr(((t1(0) - t0(0)) ^ 2) + ((t1(1) - t0(1)) ^ 2))
End Function

Это естественно всего лишь корень... Двумерная дистанция. Отмечу еще что использую и вычисление угла своей функцией а не AcadUtility.AngleFromXAxis, т.к. обращение к утилитам вызывает какие-то перемигивания в командной строке и замедляет прохождение больших циклов.

Re: Как получить длину линии?

Я конечно же понимаю что мой скриптик довольно простенький и делитанский, но чего огород городить?.
Да и для понимания он лучше подходит.

Sub slozhenie_dlin_liniy()
'скриптик для сложения длин выделенных линий.
    Dim ssetObj As AcadSelectionSet
    Dim Items As AcadObject
On Error GoTo end_prog
Set ssetObj = ThisDrawing.SelectionSets.Add("NABOR1")
    ssetObj.SelectOnScreen
For Each Items In ssetObj
    dlinna = dlinna + Items.Length
Next
a_11 = InputBox(" ", " ", dlinna)
end_prog:
ssetObj.Clear
ssetObj.Delete
End Sub

Re: Как получить длину линии?

> Boxa
А проверить, если ли такой набор? А очистить его перед созданием?
И теперь - у меня в набор попали тексты, многострочные тексты, блоки со скрытыми атрибутами... Кад сказал Goodbye. И все.
Может, только у меня.

Re: Как получить длину линии?

Да нет все правильно... никаких проверок нет. просто ты спрашивал как это сделать и я привел простейший вариант. Если так уж критично вставь проверку на наличие свойства ".Length"
(просто у меня линии для суммирования обычно вынесены в отдельный слой и вырубив все остальные я не глядя делаю выборку.  На счет наличия такого набора объектов... а зачем проверять? В каждом скрипте название выборки уникально и выборка удаляется после завершения скрипта.)
Просто мне все это не очень надо (проверки), я не пишу законченные программы. вот слобать маленький скриптик для выполнения какой нибудь последовательности операций, это да. ИМХО на написание и отработку всяких условий и красивостей времени уходит больше чем на написание алгоритма того что нужно.

Re: Как получить длину линии?

> Boxa
Я спрашивал? Я-то не спрашивал, а указывал на возможные трудности. А они могут быть - достаточно каду всего один раз (в момент работы VBA-макроса) сказать "ква". Или пользователю жать Esc до тех пор, пока клава на полу не окажется. А данный "скрипт" (буду пользоваться твоей терминологией) уже успел запуститься. Вот насчет хранения наборов я не знаю - может, они и хранятся в файле, может, нет. Лучше снести.
И смотри - тебе надо идти, вырубать отдельно слои (или использовать фильтры слоев - тоже песня та еще), а потом запускать на выполнение. Может, проще использовать фильтры?

Re: Как получить длину линии?

> kpblc
ну в общем то ты прав во всем...
тока я скрипты для себя пишу и под свой стиль , свою манеру черчения. А если уж приходится делать что то для других, то да приходится вставлять 1001 проверку на правильность введеных данных, работать над удобством пользования скриптом... только все это не относится к скрипту сбацаному за минуту для выполнения какой то операции.

Хочу написать макрос на VBA, суммирующий длины всех выделенных линий и полилиний. и не могу :(

просто я посмотрел что предлагают другие и запостил самый простой скрипт, он не хороший не плохой, он ПРОСТОЙ и дальше человек разобравшись как это работает дополнит его как ему надо.
На этом считаю тему исчерпаной для обсуждения.
ЗЫ. Все что выше ИМХО и я не претендую на истину в последней инстанции.

Re: Как получить длину линии?

> Boxa
Ну я надеюсь, я тебя не обидел?
Жалко, что здесь нет механизма подписей - я постоянно забываю добавлять "ИМХО" в конце.

Re: Как получить длину линии?

JS, а что за функция PolarPoint() ?

Re: Как получить длину линии?

Вот блин. Называется помог!!! Я, вообще-то, начинал на ЛИСПе, поэтому все привычные команды постарался оттуда вынести. Теперь это естественно целый модуль с всякими функциями и вынуть по быстрому со всеми увязками не так-то просто. ПоларПоинт это тоже всего лишь ПоларПоинт двумерный:

Public Function PolarPoint(ByVal t0 As Variant, ByVal ang As Double, _
            ByVal Dist As Double, Optional Result) As Variant
Dim vVal(2) As Double
    vVal(0) = t0(0) + Cos(ang) * Dist
    vVal(1) = t0(1) + Sin(ang) * Dist
    vVal(2) = 0
    PolarPoint = vVal
    If Not IsMissing(Result) Then
        Result(0) = vVal(0)
        Result(1) = vVal(1)
    End If
End Function

В качестве компенсации за такую "помощь" вот ещё функция для определения длины сегмента:

Public Function LenSegmPLWithTangents( _
            Xs As Double, Ys As Double, _
            Xe As Double, Ye As Double, _
            kr As Double, _
            sTg As Double, eTg As Double, _
            Optional ta As Variant, Optional vR As Variant, _
            Optional vS As Variant, Optional vTC As Variant) As Double
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim t0(1) As Double, t1(1) As Double
Dim t2(1) As Double, t3 As Variant, tc As Variant
Dim t4(1) As Double, t5 As Variant
Dim dd As Double, d As Double, d1 As Double, sign As Integer
Dim Alfa As Double, beta As Double, Gamma As Double, r As Double
'Если сегмент дуговой, то опциональные переменные будут заполнены:
'ta - полный угол дуги
'vR - радиус
'vS - знак кривизны выпуклости
'vTC - точка центра дуги
    t0(0) = Xs: t0(1) = Ys
    t1(0) = Xe: t1(1) = Ye
    dd = Distance(t0, t1)
    d = dd / 2
    d1 = d * kr
    If d >= Prec Then
        If Abs(d1) >= Prec Then
            t2(0) = (t0(0) + t1(0)) / 2
            t2(1) = (t0(1) + t1(1)) / 2
            Alfa = AngleFromXAxis(t0, t1)
            d1 = d * (kr * kr - 1) / 2 / kr
            tc = PolarPoint(t2, Alfa - Pi_2, d1)
            r = Distance(t0, tc)
            Gamma = 4 * Atn(kr)
            sign = Sgn(kr)
            sTg = AngleTo_0_2pi(AngleFromXAxis(tc, t0) + sign * Pi_2)
            eTg = AngleTo_0_2pi(AngleFromXAxis(tc, t1) + sign * Pi_2)
            If Not IsMissing(ta) Then ta = Gamma
            If Not IsMissing(vR) Then vR = r
            If Not IsMissing(vS) Then vS = sign
            If Not IsMissing(vTC) Then vTC = tc
            LenSegmPLWithTangents = Abs(Gamma) * r
        Else
            sTg = AngleTo_0_2pi(AngleFromXAxis(t0, t1))
            eTg = AngleTo_0_2pi(sTg)
            If Not IsMissing(ta) Then ta = Null
            If Not IsMissing(vR) Then vR = Null
            If Not IsMissing(vS) Then vS = Null
            If Not IsMissing(vTC) Then vTC = Null
            LenSegmPLWithTangents = dd
        End If
    Else
        LenSegmPLWithTangents = 0
    End If
End Function

Используется так:

Dim pD As Double, psTg As Double, peTg As Double, pTA, pR, pS, pTC
Dim vXs As Double, vYs As Double, vXe As Double, vYe As Double, vBlg As Double
    '''''''''''''''''''''''''''''''''''
    'Тело цикла
    'определяем для сегмента координаты стартовой точки vXs, vYs
    'координаты конечной точки vXe, vYe, и кривизну  vBlg
    pD = LenSegmPLWithTangents(vXs, vYs, vXe, vYe, vBlg, psTg, peTg, pTA, pR, pS, pTC)
    If Abs(pD) <= Prec Then
      'короткий сегмент - ничего не делаем
    else
        If IsNull(pTC) Then
            'Линейный сегмент
            'Действия для линейного сегмента
        Else
            'Дуговой сегмент
            'Действия для дугового сегмента
        End If
    end if
    '''''''''''''''''''''''''''''''''''

Надеюсь, что теперь ничего не забыл. Ну а если забыл, так допишу. Всего хорошего.