Тема: Напраление полилинии

Существует замкнутая полилиниия, произвольной формы. Необходимо узнать,в каком направлении она построена (по часовой или против часовой стрелки идут по порядку вершины).

Re: Напраление полилинии

Я не разбирался, как это делается на VBA, но могу подсказать как это сделать на лиспе.
Во-первых необходимость в такой задаче может возникнуть, только если полилиния включает дуги. Если полилиния состоит из отрезков такая необходимость мне кажется - отсутствует и думаю, что отличить нельзя. Разве что програмно анализировать контур.
Дуги в формате полилинии описываются начальной и конечной точками + направлением касательной к дуге в первой точке.
Направление касательной задается как тангенс угла наклона касательной в ассоциативном коде 42.
Так вот: если тангенс угла наклона - положительный, то полилиния нарисована против часовой стрелки, а если отрицательный - то по часовой. Естественно, что у всех дуг полилинии знак тангенса - одинаковый.

Re: Напраление полилинии

У объектов 3DPoly, Leader, LightweightPolyline, Point, PolyfaceMesh, PolygonMesh, Polyline, Solid, Trace есть свойство Coordinate(index). Оно возвращает 2-х или 3-х элементный массив типа Double с координатами вершины, определяемой index. Сравнивая вершины, допустим, 0-ю и 1-ю и можно определить "направление".

Re: Напраление полилинии

Небольшое дополнение.
Корректнее сравнивать 1-ю (индекс - 0) и последнюю (индекс - n) вершины. Индекс последней вершины n можно определить сформировав с помощью свойства Coordinates массив retCoord (это свойство возвращает массив Double с координатами всех вершин примитива, подробнее в справке). Тогда

n = (UBound(retCoord) + 1) / i - 1

где i = 2 или 3 в зависимости от типа примитива.
Подразумевается, что нумерация элементов массива начинается с 0 (Option Base 0).

Re: Напраление полилинии

Можно попробовать такой способ:
Определить "центральную" точку (приблизительно), от нее определить углы на каждую или некоторые вершины и на основании разницы углов определить направление.

Re: Напраление полилинии

> по
2-м точкам и жаде по трем нельзя определить направление. Т.к. контур может быть сначала вогнутым, а потом выпуклым и здесь можно ошибиться, что я однажды и сделал.
Я тоже пробовал решить такую проблему.
Алгоритм немного объемный, но найди проще?!
Идея:
Если сумма левых углов в многоугольнике равна 180*(n-2), то он направлен против часовой стрелки. А если 180*(n+2), то по часовой.
Таким образом находишь сумму левых углов полилинии(не важно при этом дуговые у него элементы или прямые!).
Сравниваешь её с 180*n
если сумма> 180*n
       по часовой
иначе
      против часовой
Левые и правые углы это понятие из геодезии.
b=Aпередн.-Aзадн. +-180 (в градусах)
b-левый угол при вершине
Aпередн. - дирекционный угол переднего направления
Aзадн. - дирекционный угол заднего направления

Re: Напраление полилинии

Забыл сказать.
n - количество вершин полилинии

Re: Напраление полилинии

Павел пишет:

по 2-м точкам и жаде по трем нельзя определить направление. Т.к. контур может быть сначала вогнутым, а потом выпуклым и здесь можно ошибиться, что я однажды и сделал.

Именно поэтому надо анализировать первую и последнюю вершины. Они как раз и определяют "вектор". При этом контур должен быть, так сказать, "простым", т. е. не иметь внутри себя "пересечений".

Re: Напраление полилинии

Интимный вопрос: Зачем нужно знать направление отрисовки контура если он состоит из отрезков?
Если в контуре есть дуги, то это понятно, - возникает неопределенность положения дугового участка, в случае неизвестности направления обхода. Но как быть с дугами - я написал ранее.

Re: Напраление полилинии

> bender
А если полилиния "закручена" по спирали? Способ работает если нет пересечений и если нет "закрученности".

Re: Напраление полилинии

Я о чем хочу сказать. Любые задачи, которые встают перед человеком програмирующем на VBA требуют знания объектов, их свойст и действий, которые с ним можно производить. Поэтому всем маленкий совет - изучайте объектную модель AutoCAD. И намного станет легче жить. Изучайте папку Sample, пользуйтесь клавишей F1 и почаще посещайте ресурс на куличках посвященный акаду. И bender, зная свойства и методы полилиний сразу предложил решение. Изучайте свойства и методы объектов!!! Этот совет практически универсален smile)) Правда есть некоторые вещи, которые в VBA мне непонятны. Мне кровь из носа надо было взорвать солид программно. Не получилось. Не поддерживает вбашная модель для солидов таких вещей - обидно. Вот так.

Re: Напраление полилинии

Сергей пишет:

> И bender, зная свойства и методы полилиний сразу предложил решение.

Bender не предложил накакого решения. Извлечь список вершин полилинии - не проблема.

Re: Напраление полилинии

Я о чем хочу сказать.
Площадь замкнутой фигуры из прямолинейных сегментов можно вычислить по формуле
S=Summa i=1...n { 1/2*(Y[i]+Y[i+1])*(X[i+1]-X[i]) },
где при i=n, конечно, i+1=1.
При этом площадь "правозавернутой" фигуры по этой формуле - положительное число, а "левозавернутой" - отрицательное. Дуговые сегменты в данном случае можно заменить линейными - мы ж не площадь в самом деле вычисляем.

Re: Напраление полилинии

> VH
Абсолютно верно. Класс. Супер. Учим аналитическую геометрию на плоскости!!! Даздравствуют Корны. Плохо прочитал топик, если речь идет о замкнутой фигуре то и метод предложенный бендером подойдет, а если не замкнута? То наверное сложнее будет (это к вопросу о "спиральности" полилинии скажем)

Re: Напраление полилинии

> Сергей

> Олег
На самом деле я действительно ничего конкретного не предложил. Все что я говорил достаточно банально и может служить лишь отправной точкой. Самое интересное начинается тогда, когда получены координаты 0-ой, первой и последней вершин. Ну получили, и что? Что с ними делать, чтобы определить"направление" полилинии?
Как всегда решений, наверное, не одно. Если угодно могу предложить такое (наверное на самое правильное и уж  несомненно не самое красивое, т. к. требует дополнительных построений).
1. Строим линию из "первой" вершины в "последнюю" и точку с координатами 0-й вершины.
2. Определяем угол, образуемый линией (angle).
3. Поворачиваем линию и точку относительно "первой" вершины на минус angle, ориентируя тем самым линию паралельно оси X.
4. Если точка окажется "выше" линии - то исходная полилиния нарисована против часовой стрелки, если "ниже", то по часовой.
5. Убираем мусор.
Перед всем этим нужно определить не находится ли 0-я вершина "внутри" контура. Если находится то п.4 - все наоборот.
А вообще-то решение VH (2004-01-09 17:08:37) кажется более простое и правильное.

Сергей пишет:

Не поддерживает вбашная модель для солидов таких вещей - обидно.

Объектная модель одна и таже для всех программ (VBA, Delfi).

Re: Напраление полилинии

> VH
Да. Век живи - век учись.

Re: Напраление полилинии

> bender
Я знаю что одна и та-же. Просто не поддерживает. А было-бы здорово, если-бы солиды моджно было взрывать и получать массив из регионов скажем.

Re: Напраление полилинии

> Антон
Вот мой алгоритм в действии.
Нарисуй в AutoCAD несколько полилиний по часовой и против часовой и посмотри, как работает моя функция(Запусти процедуру primer1 и повыбирай полилинии). Помоему превосходно?

Sub primer1()
    Dim SeldObj As AcadObject
    Dim BasePnt As Variant
    On Error GoTo labelexit
label:
    ThisDrawing.Utility.GetEntity SeldObj, BasePnt, "Укажите  участок"
    If DetectNapravlKontura(SeldObj.Coordinates) Then
        MsgBox "Контур направлен по часовой"
    Else
        MsgBox "Контур направлен против часовой"
    End If
    GoTo label
labelexit:
End Sub
Function DetectNapravlKontura(ByRef Coords As Variant) As Boolean
'Функция определяет направление контура и возвращает
'True - если контур направлен по часовой
'False - если против часовой
'контур должен быть "правильным", т.е. последняя точка не должна совпадать с первой
    Dim i As Integer
    Dim upper As Integer
    Dim B As Double 'левый угол поворота
    Dim A12 As Double
    Dim A23 As Double
    Dim SummB As Double
    Dim pt1(2) As Double
    Dim pt2(2) As Double
    SummB = 0
    upper = (UBound(Coords) + 1) / 2
    'определяется B для первой точки
    pt1(0) = Coords((upper - 1) * 2): pt1(1) = Coords((upper - 1) * 2 + 1): pt1(2) = 0
    pt2(0) = Coords(0): pt2(1) = Coords(1): pt2(2) = 0
    A12 = (Pi() / 2) - ThisDrawing.Utility.AngleFromXAxis(pt1, pt2)
    If A12 < 0 Then A12 = 2 * Pi() + A12
    pt1(0) = Coords(0): pt1(1) = Coords(1): pt1(2) = 0
    pt2(0) = Coords(2): pt2(1) = Coords(3): pt2(2) = 0
    A23 = (Pi() / 2) - ThisDrawing.Utility.AngleFromXAxis(pt1, pt2)
    If A23 < 0 Then A23 = 2 * Pi() + A23
    B = A23 - A12
    If B < 0 Then B = 2 * Pi() + B
    If B < Pi() Then
        B = B + Pi()
    Else
        B = B - Pi()
    End If
    SummB = B
    'определяется B для промежуточных точек
    For i = 1 To upper - 2
        pt1(0) = Coords((i - 1) * 2): pt1(1) = Coords((i - 1) * 2 + 1): pt1(2) = 0
        pt2(0) = Coords(i * 2): pt2(1) = Coords(i * 2 + 1): pt2(2) = 0
        A12 = (Pi() / 2) - ThisDrawing.Utility.AngleFromXAxis(pt1, pt2)
        If A12 < 0 Then A12 = 2 * Pi() + A12
        pt1(0) = Coords(i * 2): pt1(1) = Coords(i * 2 + 1): pt1(2) = 0
        pt2(0) = Coords((i + 1) * 2): pt2(1) = Coords((i + 1) * 2 + 1): pt2(2) = 0
        A23 = (Pi() / 2) - ThisDrawing.Utility.AngleFromXAxis(pt1, pt2)
        If A23 < 0 Then A23 = 2 * Pi() + A23
        B = A23 - A12
        If B < 0 Then B = 2 * Pi() + B
    If B < Pi() Then
        B = B + Pi()
    Else
        B = B - Pi()
    End If
        SummB = SummB + B
    Next
    'определяется B для последней точки
    pt1(0) = Coords((upper - 2) * 2): pt1(1) = Coords((upper - 2) * 2 + 1): pt1(2) = 0
    pt2(0) = Coords((upper - 1) * 2): pt2(1) = Coords((upper - 1) * 2 + 1): pt2(2) = 0
    A12 = (Pi() / 2) - ThisDrawing.Utility.AngleFromXAxis(pt1, pt2)
    If A12 < 0 Then A12 = 2 * Pi() + A12
    pt1(0) = Coords((upper - 1) * 2): pt1(1) = Coords((upper - 1) * 2 + 1): pt1(2) = 0
    pt2(0) = Coords(0): pt2(1) = Coords(1): pt2(2) = 0
    A23 = (Pi() / 2) - ThisDrawing.Utility.AngleFromXAxis(pt1, pt2)
    If A23 < 0 Then A23 = 2 * Pi() + A23
    B = A23 - A12
    If B < 0 Then B = 2 * Pi() + B
    If B < Pi() Then
        B = B + Pi()
    Else
        B = B - Pi()
    End If
    SummB = SummB + B
    If SummB < Pi() * upper Then
        DetectNapravlKontura = False
    Else
        DetectNapravlKontura = True
    End If
End Function

Re: Напраление полилинии

контур должен быть "правильным", т.е. последняя точка не должна совпадать с первой

А если нет? А если вершины n и n+1 совпадают? Алгоритм будет правильно работать?
Ниже приблизительная реализация алгоритма VH (2004-01-09 17:08:37)

Public Sub testGetEntVector1()
Dim retObj As AcadEntity
Dim x, y As Variant
Dim control As Boolean
Dim summa As Double
Dim i As Long, upperBound As Long
On Error GoTo Error_Control
Do While control = False
    ThisDrawing.Utility.GetEntity retObj, x, "Выберите объект: "
    Select Case retObj.ObjectName
        Case "AcDb2dPolyline"
            upperBound = 3
        Case "AcDbPolyline"
            upperBound = 2
        Case Else
            upperBound = 0
    End Select
    If upperBound = 0 Then
        control = True
        Exit Do
    End If
    summa = 0
    For i = 0 To (UBound(retObj.Coordinates) + 1) / upperBound - 1
        If i + 1 <= (UBound(retObj.Coordinates) + 1) / upperBound - 1 Then
            x = retObj.Coordinate(i)
            y = retObj.Coordinate(i + 1)
            summa = summa + ((x(1) + y(1)) * (y(0) - x(0))) / 2
        Else
            x = retObj.Coordinate(i)
            y = retObj.Coordinate(0)
            summa = summa + ((x(1) + y(1)) * (y(0) - x(0))) / 2
        End If
    Next
    Select Case summa
        Case Is < 0
            MsgBox "Против часовой стрелки."
        Case Is > 0
            MsgBox "По часовой стрелке."
        Case Else
            MsgBox summa
    End Select
    Select Case MsgBox("Продолжить программу?", vbYesNo)
        Case 6
            control = False
        Case Else
            control = True
    End Select
Loop
GoTo Exit_Here
Error_Control:
Select Case Err.Number
    Case -2147352567
        x = ThisDrawing.GetVariable("LASTPROMPT")
        If InStr(1, x, "*Cancel*") <> 0 Then
            Err.Clear
            control = True
        Else
            Err.Clear
            Resume
        End If
    Case -2147467259
        Err.Clear
        Resume
    Case Else
        MsgBox Err.Description & " " & Err.Number
        Err.Clear
        Resume Exit_Here
End Select
Exit_Here:
End Sub

Re: Напраление полилинии

Да, красиво.
На данный момент я воспользовался методом предложенным VH:

Function get_dir(ByVal pl As AcadLWPolyline) As Boolean
        Dim s As Double = 0
        Dim i As Integer
                For i = 0 To (UBound(pl.Coordinates) - 1) - 3 Step 2
            s = s + (1 / 2) * (pl.Coordinates(i + 1) + pl.Coordinates(i + 3)) * (pl.Coordinates(i + 2) - pl.Coordinates(i))
        Next
        If s > 0 Then
            Return False
        End If
    End Function

Соответственно, в случае возвращения функцией False- линия нарисована по часовой стрелке.