CAD-страница НП | Статьи | English | Download

Пересечение примитивов
(из раздела "Геометрические вычисления" главы 5 "ObjectARX (С++)"
книги "AutoCAD: разработка приложений, настройка и адаптация")

Часто используемой функцией геометрических вычислений является intersectWith - пересечение примитивов. Метод с таким названием присутствует как у AcGe-объектов, так и у примитивов, порожденных классом AcDbEntity.
Пример расчета точек пересечения выбираемых примитивов приведен в листинге 5.15.

Листинг 5.15. Пересечение примитивов
// Н.Н.Полещук, Глава 5\Book09\acrxEntryPoint.cpp
// В книге "AutoCAD: разработка приложений, настройка и адаптация"
// (издательство "БХВ-Петербург", 2006)
// http://poleshchuk.spb.ru/cad/2006/Razr2006.htm
//
// ----- ads_book09 symbol (do not rename)
static int ads_book09(void)
{
int es1, es2;
ads_name ent1, ent2;
ads_point pt1, pt2;
AcDbObjectId entId1, entId2;
AcDbEntity* pEnt1 = NULL;
AcDbEntity* pEnt2 = NULL;
AcGePoint3dArray points;
 
// Выбор первого объекта по запросу
es1 = acedEntSel("\nУкажите объект 1: ", ent1, pt1);
if (es1 != RTNORM)
{
acutPrintf("\nОбъект не выбран.");
return (RSRSLT);
}
 
// Вычисление ID для перехода от ads_name к AcDbObjectId
es1 = acdbGetObjectId(entId1, ent1);
if (es1 != Acad::eOk)
{
acutPrintf("\nНе получен 1 ");
return (RSRSLT);
}
// Вычисление указателя на AcDbEntity
es1 = acdbOpenAcDbEntity(pEnt1, entId1, AcDb::kForRead);
if (es1 != Acad::eOk)
{
acutPrintf("\nНе открыт объект 1 ");
return (RSRSLT);
}
// Подсветка первого объекта
pEnt1->highlight();
 
// Выбор второго объекта по запросу
label_select2:
es2 = acedEntSel("\nУкажите объект 2: ", ent2, pt2);
if (es2 != RTNORM)
{
pEnt1->unhighlight(); // сброс подсветки
pEnt1->close();// закроем объект 1
acutPrintf("\nОбъект не выбран. ");
return (RSRSLT);
}
// Вычисление ID для перехода от ads_name к AcDbObjectId
es2 = acdbGetObjectId(entId2, ent2);
if (es2 != Acad::eOk)
{
pEnt1->unhighlight();
pEnt1->close(); // закроем объект 1
acutPrintf("\nНе получен Id 2 ");
return (RSRSLT);
}
// Проверка совпадения объектов
if(entId2 == entId1)
{
acutPrintf(" Повторите выбор: ");
goto label_select2;
}
 
// Вычисление указателя на AcDbEntity
es2 = acdbOpenAcDbEntity(pEnt2, entId2, AcDb::kForRead);
if (es2 != Acad::eOk)
{
pEnt1->unhighlight();
pEnt1->close(); // закроем объект 1
acutPrintf("\nНе открыт объект 2 ");
return (RSRSLT);
}
 
// Подсветка второго объекта
pEnt2->highlight();
 
// Распечатка выбранных объектов
acutPrintf("\n");// переход на новую строку
pEnt1->list();
acutPrintf("\n");
pEnt2->list();
 
// Вычисление точек пересечения для объектов
es1 = pEnt2->intersectWith(pEnt1, AcDb::kOnBothOperands, points);
int len;
if ( (es1 != Acad::eOk) || ((len = points.length() ) == 0) )
acutPrintf("\nОбъекты не пересекаются.");
else
{
acutPrintf("\nТочки пересечения:");
for (int i=0; i<len; ++i)
{
acutPrintf("\n%d: %.6f %.6f %.6f",
i+1, points[i].x, points[i].y, points[i].z);
}
}
 
// Сброс подсветки
pEnt1->unhighlight();
pEnt2->unhighlight();
 
// Закрытие объектов с освобождением памяти
pEnt1->close();
pEnt2->close();
 
// TODO: Replace the following line by your returned value if any
acedRetVoid () ;
return (RSRSLT) ;
}

Пояснения к тексту программы. Для выбора объекта в интерактивном режиме используется функция acedEntSel, которая описана в файле acedads.h следующим образом:

int acedEntSel(const char * str, ads_name entres, ads_point ptres);

Первый параметр - это текст подсказки, выводимой на экран, а через второй и третий по ссылке возвращается результат выбора (имя примитива и точка, в которой он был указан). В отличие от аналогичной LISP-функции entsel возвращается целочисленный код, а не список. Нормальным возвращаемым значением является RTNORM.
Имя первого из указываемых примитивов сохраняется в переменной ent1, которая имеет тип ads_name  - это один из типов данных, в котором представляется примитив в системе программирования ObjectARX. Для преобразования данных примитива из одного представления в другое используются соответствующие функции.
Для дальнейшей работы с первым указанным на экране объектом необходимо получить его идентификатор в базе рисунка, или ID (Object ID). Идентификаторы, как и имена примитивов (ads_name), не являются постоянными и в каждом новом сеансе редактирования рисунка имеют свои значения.
Имя примитива ent1 преобразуется в идентификатор entId1 с помощью функции acdbGetObjectId, описанной в файле dbmain.h так:

Acad::ErrorStatus acdbGetObjectId (AcDbObjectId& objId, const ads_name objName);

Через первый аргумент по ссылке передается вычисленный ID примитива.
Далее по entId1 с помощью функции acdbOpenAcDbEntity можно по ссылке получить значение указателя на примитив, как объект класса AcDbEntity, и, соответственно, доступ к свойствам и методам примитива. Эта функция описана в заголовочном файле dbmain.h следующим образом:

Acad::ErrorStatus acdbOpenAcDbEntity (AcDbEntity*& pEnt, AcDbObjectId id, AcDb::OpenMode mode, bool openErasedEntity = false);

Первый параметр функции acdbOpenAcDbEntity - это вычисляемый указатель, второй - ID объекта, третий - режим доступа (чтение, запись и т. п.), а четвертый - признак доступа к удаленным примитивам (по умолчанию удаленные примитивы недоступны). Таким образом мы для первого указанного на экране объекта получили указатель pEnt1, который указывает уже на объект типа AcDbEntity.
С помощью указателя pEnt1 можно вызвать метод подсветки выбранного примитива. Для этого используется функция highlight.
Все, что мы перечислили выше для первого примитива, применяется затем в программе ко второму объекту. Вычисляется ID второго объекта (entId2), а затем - указатель pEnt2 и объект тоже подсвечивается (хотя это не заметно, поскольку программа выполняется быстро, а в конце ее работы подсветка сбрасывается).
В процессе вычислений проверяется, не указан ли один и тот же объект два раза (тогда entId2 == entId1), и если да, то управление передается снова на метку label_select2, которой помечена операция выбора второго примитива.
Далее подробные сведения об обоих указанных примитивах с помощью функции list выводятся на экран точно так же, как это делает команда LIST (СПИСОК).
После этого переходим к непосредственному вычислению точек пересечения, для чего необходимо применить метод (функцию) intersectWith, о котором речь шла выше. Функция является виртуальной в классе AcDbEntity и описана в файле dbmain.h так:

virtual═ Acad::ErrorStatus intersectWith (const AcDbEntity* pEnt, AcDb::Intersect intType, AcGePoint3dArray& points, int thisGsMarker = 0, int otherGsMarker = 0) const;

В рассматриваемом варианте (а есть и перегруженный вариант, с другими аргументами) функция в качестве первого аргумента принимает указатель на вспомогательный объект типа AcDbEntity, в качестве второго ≈ признак продления одного или обоих примитивов при расчете пересечения, а в качестве третьего - ссылку на массив из трехмерных точек типа AcGePoint3d. Четвертый и пятый параметры нами в явном виде не используются (они принимают значения по умолчанию).
В нашем примере основным является первый из указанных на экране объектов, а pEnt2 - это указатель на вспомогательный примитив, который необходимо передать функции. Признак продления intType может принимать следующие значения:

AcDb::kOnBothOperands - не продлевается ни один из объектов;
AcDb::kExtendThis - продлевается только основной примитив;
AcDb::kExtendArg - продлевается только вспомогательный примитив;
AcDb::kExtendBoth - продлеваются оба объекта.

В нашем примере выбран признак отсутствия продления для обоих примитивов.
Результат операции пересечения возвращается по ссылке через массив points. Как и все массивы, использующие шаблоны библиотеки STL, он "умеет" динамически расширяться, а количество реально записанных в него точек можно получить с помощью стандартной функции length.
Затем с помощью цикла в текстовый экран выводятся координаты всех рассчитанных точек пересечения (как вещественные числа с шестью знаками после десятичной точки). Для извлечения из точек трех координат используются члены класса массива (x, y и z).
В конце программы с помощью функции unhighlight сбрасывается подсветка объектов, и затем они закрываются для доступа. Пример результатов работы LISP-функции book09 для случая пересечения сплайна и отрезка в русской версии системы AutoCAD приведены в листинге 5.16.

Листинг 5.16. Пример результатов работы функции book09

SPLINE Слой: "0"
Пространство: Пространство модели
Метка = 2f
Длина: 1244.8908
Порядок: 4
Свойства: Плоский, Нерациональный, Непериодический
Диапазон параметров Начало 0.0000
Конец 1186.5631
Количество управляющих точек: 14
Управляющие точки: X = 34.8204 , Y = 60.1364, Z = 0.0000
X = 8.7752 , Y = 66.6973, Z = 0.0000
X = 49.5011 , Y = 148.8445 , Z = 0.0000
X = 110.4608 , Y = 220.3712 , Z = 0.0000
X = 204.9088 , Y = 126.1070 , Z = 0.0000
X = 270.0202 , Y = 14.8550 , Z = 0.0000
X = 446.3496 , Y = 29.8802 , Z = 0.0000
X = 505.9071 , Y = 185.9470 , Z = 0.0000
X = 377.7646 , Y = 260.1995 , Z = 0.0000
X = 253.5653 , Y = 233.3190 , Z = 0.0000
X = 232.3561 , Y = 128.3835 , Z = 0.0000
X = 327.4772 , Y = 60.9859< , Z = 0.0000
X = 392.1199 , Y = 123.8904 , Z = 0.0000
X = 416.6153 , Y = 158.3182 , Z = 0.0000
Количество определяющих точек 12
Данные пользователя: Определяющие точки
X = 34.8204 , Y = 60.1364 , Z = 0.0000
X = 48.7485 , Y = 139.5000 , Z = 0.0000
X = 106.9190 , Y = 193.5000 , Z = 0.0000
X = 193.7650 , Y = 131.3182 , Z = 0.0000
X = 275.6953 , Y = 44.5909 , Z = 0.0000
X = 442.8330 , Y = 68.3182 , Z = 0.0000
X = 478.0630 , Y = 170.5909 , Z = 0.0000
X = 376.4695 , Y = 241.7727 , Z = 0.0000
X = 256.0320 , Y = 203.3182 , Z = 0.0000
X = 246.2004 , Y = 137.8636 , Z = 0.0000
X = 310.1060 , Y = 89.5909 , Z = 0.0000
X = 416.6153 , Y = 158.3182 , Z = 0.0000
Допуск для определяющих точек: 1.0000E-10
 
Касательная в начале
X = -0.9697 , Y = 0.2443 , Z = 0.0000
Касательная в конце
X = 0.5797 , Y = 0.8148 , Z = 0.0000
 
ОТРЕЗОК Слой: "0"
Пространство: Пространство модели
Метка = 30
от точки, X= 26.6273 Y= 144.4091 Z= 0.0000
до точки, X= 547.7037 Y= 123.1364 Z= 0.0000
Длина = 521.5104, Угол в плоскости XY = 358
Дельта X = 521.0764, Дельта Y = -21.2727, Дельта Z = 0.0000
 
Точки пересечения:
1: 51.195222 143.406117 0.000000
2: 188.388814 137.805246 0.000000
3: 477.399623 126.006499 0.000000
4: 247.140662 135.406726 0.000000
5: 392.756432 129.462022 0.000000


CAD-страница НП | Статьи | English | Download