Здравствуйте Уважаемые программисты. Я действующий инженер-проектировщик линейных объектов. В своей практике работы я самостоятельно и при помощи хороших людей, научился немного программировать в среде LISP, для реализации инженерных задач и задач оформительского характера в AutoCAD. С некоторого момента мне стало не хватать дополнительных инструментов для реализации своих идей, потому что они стали заходит далеко за рамки AutoCAD и LISP. У меня также был небольшой опыт работы в среде VBA. Взвесив все за и против, я решил попробовать себя в связке C++ objectARX. Для этого я изучил вводные лекции по С++ и купил книгу уважаемого Полищука Николая Николаевича "Программирование для AutoCAD 2013-2015". Идея была простая, LISP я начал изучать с подобной книги, думал и тут можно будет найти ключи к знаниям.
Первое что меня удивило- это отсутствие функции main. Во всех лекциях по С++ это функция приводилась как основное тело программы в котором без которой ничего не работает. Поняв что я ее в примерах не найду, решил принять это так как есть. Я отлично понимаю что это Ваш хлеб и вы им дорожите, но если все таки найдете возможным помочь, прошу дать направление в следующих шагах.
Как правило, множество операция в Autocad носит следующий характер:
- Выделить объект;
- Получить информацию об объекте;
- Совершить какие то изменения над объектом;
или
- Создать объект;
- Разместить в базе данных Autocad;
- Задать параметры объекту.
Конечно существуют более сложные формы программ с подключением баз данных с подготовкой к информационной модели BIM, но это ведь уже совсем другой уровень для меня.
Вот пример простого кода в LISP
(vl-load-com) ;загрузили фунции AutoLISP для работы со свойствами ActiveX
(setq acad_aplication (vlax-get-acad-object));получили указатель на объект Aplication
(setq active_document (vla-get-activedocument acad_aplication)) ;получили указатель на объект активный документ;
(setq model_space (vla-get-modelspace active_document)) ;получили указатель на пространство модели;
(setq paper_space (vla-get-paperspace active_document)) ;получили указатель на пространство листа;
(if (= (vla-get-ActiveSpace active_document) 0); если условие выполнено значит находимся в пространстве листа
(setq model_space paper_space); делаем такое хитрое присвоение, чтобы везде в коде использовать model_space без лишних заморочек
;***************************************************************
(setq lpol_koor_t nil) ; задали lpol_koor_t значение nil
(setq nabpol_t (ssget '((0 . "LWPOLYLINE")))); создали набор из полилиний и присвоили его значению nabpol_t.
(setq lpol_t (ssname nabpol_t 0));получили 0-й элемент набора, а то мало ли там еще чего лишнего выбрал пользователь))), и присвоили значение lpol_t
(setq lpol_vla_t (vlax-ename->vla-object lpol_t )); преобразовали в vla объект и присвоили значение lpol_vla_t
(setq lpol_koor_t (vlax-safearray->list (vlax-variant-value (vla-get-Coordinates lpol_vla_t)))); получили координаты в виде данных Varian, приобразовали в безоспастный массив и преобразовали в список с присвоением переменной lpol_koor_t.
(setq n_t (length lpol_koor_t)); получили длину списка;
Наверное тем кто не знаком с LISP это покажется слишком сложным, а тем кто с ним знаком скажет что зачем было столько переменных плодить можно было все через скобки сделать, я знаю. Но для понимания так проще. Я когда заканчивал университет и имел базовые понятия в turbo pascal именно в таком ключе все преподавалось.
Тут ключевым является момент (setq lpol_vla_t (vlax-ename->vla-object lpol_t ));
после этого в инспекторе объектов можно посмотреть все возможные данные для lpol_vla_t и в дальнейшем обращаться уже непосредственно к доступным свойствам или применять какие то методы к объекту. Если каких то особых специфических задач нет кроме описанных выше, то этой информации вполне достаточно, чтобы дальше конструировать нужный для задачи код.
1. Первый вопрос
Как вышеизложенный код возможно было бы реализовать в objectARX придерживаясь логики изложенной в LISP или же это принципиально не правильный подход??
Далее
В objectARX оказалось все совсем даже не так, ну вот например код из книги Полищука Н.Н. проекта UserSimple файл acrxEntryPoint.cpp
IMPLEMENT_ARX_ENTRYPOINT(CUserSimpleApp) //Данный код создает связь с AutoCAD где весь код по сути является описанием класса CUserSimpleApp. Лично для меня уже на этой стадии уже есть определенные трудности понимания. Получается исполнение макроса IMPLEMENT_ARX_ENTRYPOINT с параметром CUserSimpleApp возвращает компилятору указатель в пространство модели. При этом не совсем понятно если разница для листа или модели или там все предусмотрено уже. Тогда возникает вопрос как получить указатель конкретно для какого то листа.
Самый сложный момент для меня на данный момент это следующий:
В Visual Studio подключаемся к процессу acad
В AutoCAD загружаем UserSimple.arx
Рисуем круг и пересекающую его полилинию
В Visual Studio создаем точку остановки в середине модуля для отслеживания значений объектов
Пишем командной строке (entsinters)
Выделяем первый объект
Открывается Visual Studio и смотрим окно локальных переменных
В окне исследований значений локальных переменных появляется такая такая запись для переменной pEnt1
- pEnt1 0x0000000032031810 <Информация недоступна, символы для acdb19.dll не загружены> AcDbEntity *
Интуитивно конечно понятно что значения объекта, сохраненного в переменной pEnt1 не могут быть корректно отображены потому что чего то не хватает. Но вот чего не хватает, правильно ли я вообще в целом размышляю и что надо сделать, чтобы начать правильно размышлять, эти вопросы на данный момент, не имея ответа, стали гирей на моей ноге, причем такой, которую нет сил сдвинуть.
Ниже приведет участок кода из примера UserSimple
ads_entsinters, код приведен ниже
static int ads_entsinters(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(L"\nУкажите объект 1: ", ent1, pt1);
if (es1 != RTNORM)
{
acutPrintf(L"\nОбъект не выбран. ");
return (RTNORM);
}
// Вычисление ID для перехода от ads_name к AcDbObjectId
es1 = acdbGetObjectId(entId1, ent1);
if (es1 != Acad::eOk)
{
acutPrintf(L"\nНе получен Id 1 ");
return (RTNORM);
}
// Вычисление указателя на AcDbEntity
es1 = acdbOpenAcDbEntity(pEnt1, entId1, AcDb::kForRead);
if (es1 != Acad::eOk)
{
acutPrintf(L"\nНе открыт объект 1 ");
return (RTNORM);
}
// Подсветка первого объекта
pEnt1->highlight();
//<<<<Тут ставим точку, чтобы в локальных переменных увидеть значение pEnt1
// Выбор второго объекта по запросу
label_select2:
es2 = acedEntSel(L"\nУкажите объект 2: ", ent2, pt2);
if (es2 != RTNORM)
{
pEnt1->unhighlight(); // сброс подсветки
pEnt1->close(); // закроем объект 1
acutPrintf(L"\nОбъект не выбран. ");
return (RTNORM);
}
// Вычисление ID для перехода от ads_name к AcDbObjectId
es2 = acdbGetObjectId(entId2, ent2);
if (es2 != Acad::eOk)
{
pEnt1->unhighlight();
pEnt1->close(); // закроем объект 1
acutPrintf(L"\nНе получен Id 2 ");
return (RTNORM);
}
// Проверка совпадения объектов
if (entId2 == entId1)
{
acutPrintf(L" Повторите выбор: ");
goto label_select2;
}
// Вычисление указателя на AcDbEntity
es2 = acdbOpenAcDbEntity(pEnt2, entId2, AcDb::kForRead);
if (es2 != Acad::eOk)
{
pEnt1->unhighlight();
pEnt1->close(); // закроем объект 1
acutPrintf(L"\nНе открыт объект 2 ");
return (RTNORM);
}
// Подсветка второго объекта
pEnt2->highlight();
// Распечатка выбранных объектов
acutPrintf(L"\n"); // переход на новую строку
pEnt1->list();
acutPrintf(L"\n");
pEnt2->list();
// Вычисление точек пересечения для объектов
es1 = pEnt2->intersectWith(pEnt1, AcDb::kOnBothOperands, points);
int len;
if ( (es1 != Acad::eOk) || ((len = points.length() ) == 0) )
acutPrintf(L"\nОбъекты не пересекаются. ");
else
{
acutPrintf(L"\nТочки пересечения:");
for (int i=0; i<len; ++i)
{
acutPrintf(L"\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 (RTNORM) ;
}
Заранее извиняюсь за столько долгое изложение текста и свою невежественность в этих вопросах. Я самоучка, но все это мне ужасно интересно и есть огромное желание изучать и познавать.
Для работы были использованы Visual Studio 12, AutoCAD 2014, Windows 7 64 бит.