Тема: Напраление полилинии
Существует замкнутая полилиниия, произвольной формы. Необходимо узнать,в каком направлении она построена (по часовой или против часовой стрелки идут по порядку вершины).
Информационный портал для профессионалов в области САПР
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Форумы CADUser → Программирование → VBA → Напраление полилинии
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Существует замкнутая полилиниия, произвольной формы. Необходимо узнать,в каком направлении она построена (по часовой или против часовой стрелки идут по порядку вершины).
Я не разбирался, как это делается на VBA, но могу подсказать как это сделать на лиспе.
Во-первых необходимость в такой задаче может возникнуть, только если полилиния включает дуги. Если полилиния состоит из отрезков такая необходимость мне кажется - отсутствует и думаю, что отличить нельзя. Разве что програмно анализировать контур.
Дуги в формате полилинии описываются начальной и конечной точками + направлением касательной к дуге в первой точке.
Направление касательной задается как тангенс угла наклона касательной в ассоциативном коде 42.
Так вот: если тангенс угла наклона - положительный, то полилиния нарисована против часовой стрелки, а если отрицательный - то по часовой. Естественно, что у всех дуг полилинии знак тангенса - одинаковый.
У объектов 3DPoly, Leader, LightweightPolyline, Point, PolyfaceMesh, PolygonMesh, Polyline, Solid, Trace есть свойство Coordinate(index). Оно возвращает 2-х или 3-х элементный массив типа Double с координатами вершины, определяемой index. Сравнивая вершины, допустим, 0-ю и 1-ю и можно определить "направление".
Небольшое дополнение.
Корректнее сравнивать 1-ю (индекс - 0) и последнюю (индекс - n) вершины. Индекс последней вершины n можно определить сформировав с помощью свойства Coordinates массив retCoord (это свойство возвращает массив Double с координатами всех вершин примитива, подробнее в справке). Тогда
n = (UBound(retCoord) + 1) / i - 1
где i = 2 или 3 в зависимости от типа примитива.
Подразумевается, что нумерация элементов массива начинается с 0 (Option Base 0).
Можно попробовать такой способ:
Определить "центральную" точку (приблизительно), от нее определить углы на каждую или некоторые вершины и на основании разницы углов определить направление.
> по
2-м точкам и жаде по трем нельзя определить направление. Т.к. контур может быть сначала вогнутым, а потом выпуклым и здесь можно ошибиться, что я однажды и сделал.
Я тоже пробовал решить такую проблему.
Алгоритм немного объемный, но найди проще?!
Идея:
Если сумма левых углов в многоугольнике равна 180*(n-2), то он направлен против часовой стрелки. А если 180*(n+2), то по часовой.
Таким образом находишь сумму левых углов полилинии(не важно при этом дуговые у него элементы или прямые!).
Сравниваешь её с 180*n
если сумма> 180*n
по часовой
иначе
против часовой
Левые и правые углы это понятие из геодезии.
b=Aпередн.-Aзадн. +-180 (в градусах)
b-левый угол при вершине
Aпередн. - дирекционный угол переднего направления
Aзадн. - дирекционный угол заднего направления
по 2-м точкам и жаде по трем нельзя определить направление. Т.к. контур может быть сначала вогнутым, а потом выпуклым и здесь можно ошибиться, что я однажды и сделал.
Именно поэтому надо анализировать первую и последнюю вершины. Они как раз и определяют "вектор". При этом контур должен быть, так сказать, "простым", т. е. не иметь внутри себя "пересечений".
Интимный вопрос: Зачем нужно знать направление отрисовки контура если он состоит из отрезков?
Если в контуре есть дуги, то это понятно, - возникает неопределенность положения дугового участка, в случае неизвестности направления обхода. Но как быть с дугами - я написал ранее.
> bender
А если полилиния "закручена" по спирали? Способ работает если нет пересечений и если нет "закрученности".
Я о чем хочу сказать. Любые задачи, которые встают перед человеком програмирующем на VBA требуют знания объектов, их свойст и действий, которые с ним можно производить. Поэтому всем маленкий совет - изучайте объектную модель AutoCAD. И намного станет легче жить. Изучайте папку Sample, пользуйтесь клавишей F1 и почаще посещайте ресурс на куличках посвященный акаду. И bender, зная свойства и методы полилиний сразу предложил решение. Изучайте свойства и методы объектов!!! Этот совет практически универсален )) Правда есть некоторые вещи, которые в VBA мне непонятны. Мне кровь из носа надо было взорвать солид программно. Не получилось. Не поддерживает вбашная модель для солидов таких вещей - обидно. Вот так.
> И bender, зная свойства и методы полилиний сразу предложил решение.
Bender не предложил накакого решения. Извлечь список вершин полилинии - не проблема.
Я о чем хочу сказать.
Площадь замкнутой фигуры из прямолинейных сегментов можно вычислить по формуле
S=Summa i=1...n { 1/2*(Y[i]+Y[i+1])*(X[i+1]-X[i]) },
где при i=n, конечно, i+1=1.
При этом площадь "правозавернутой" фигуры по этой формуле - положительное число, а "левозавернутой" - отрицательное. Дуговые сегменты в данном случае можно заменить линейными - мы ж не площадь в самом деле вычисляем.
> VH
Абсолютно верно. Класс. Супер. Учим аналитическую геометрию на плоскости!!! Даздравствуют Корны. Плохо прочитал топик, если речь идет о замкнутой фигуре то и метод предложенный бендером подойдет, а если не замкнута? То наверное сложнее будет (это к вопросу о "спиральности" полилинии скажем)
> Сергей
> Олег
На самом деле я действительно ничего конкретного не предложил. Все что я говорил достаточно банально и может служить лишь отправной точкой. Самое интересное начинается тогда, когда получены координаты 0-ой, первой и последней вершин. Ну получили, и что? Что с ними делать, чтобы определить"направление" полилинии?
Как всегда решений, наверное, не одно. Если угодно могу предложить такое (наверное на самое правильное и уж несомненно не самое красивое, т. к. требует дополнительных построений).
1. Строим линию из "первой" вершины в "последнюю" и точку с координатами 0-й вершины.
2. Определяем угол, образуемый линией (angle).
3. Поворачиваем линию и точку относительно "первой" вершины на минус angle, ориентируя тем самым линию паралельно оси X.
4. Если точка окажется "выше" линии - то исходная полилиния нарисована против часовой стрелки, если "ниже", то по часовой.
5. Убираем мусор.
Перед всем этим нужно определить не находится ли 0-я вершина "внутри" контура. Если находится то п.4 - все наоборот.
А вообще-то решение VH (2004-01-09 17:08:37) кажется более простое и правильное.
Не поддерживает вбашная модель для солидов таких вещей - обидно.
Объектная модель одна и таже для всех программ (VBA, Delfi).
> bender
Я знаю что одна и та-же. Просто не поддерживает. А было-бы здорово, если-бы солиды моджно было взрывать и получать массив из регионов скажем.
> Антон
Вот мой алгоритм в действии.
Нарисуй в 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
контур должен быть "правильным", т.е. последняя точка не должна совпадать с первой
А если нет? А если вершины 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
Да, красиво.
На данный момент я воспользовался методом предложенным 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- линия нарисована по часовой стрелке.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Форумы CADUser → Программирование → VBA → Напраление полилинии
Форум работает на PunBB, при поддержке Informer Technologies, Inc