CustomEntity

Автор Тема: CustomEntity  (Прочитано 12387 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн GennadiyАвтор темы

  • ADN Club
  • **
  • Сообщений: 81
  • Карма: 0
CustomEntity
« : 17-12-2014, 09:42:48 »
Добрый день. Нужда заставила начать изучение ObjectARX для создания своих примитивов.
Для начала мне нужен самый простой примитив в виде обычной полилинии. В связи с этим появилось несколько вопросов, на которые я не смог найти ответы.
1. Если я наследую от AcDbPolyline, (как в примере http://adn-cis.org/forum/index.php?topic=987.0) то в палетке свойств при выборе отображается что это полилиния,
    а не то имя, которое я задаю в ACRX_DXF_DEFINE_MEMBERS.
    соответственно ее можно отредактировать средствами AutoCAD а при сглаживании она заменяется на AcDb2dPolyline, отсюда вопрос как отобразить в палетке
    свойств моё имя примитива, или запретить любое пользовательское редактирование, кроме ручек (редактировать я буду только программно)
2. Если я буду наследовать от AcDbCurve, то, если я правильно понимаю, мне нужно будет в моем классе хранить переменную AcDbPolyline m_Poly и использовать её во всех
    переопределяемых методах AcDbCurve. Как правильно это сделать и как использовать при отрисовке в subWorldDraw?

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: CustomEntity
« Ответ #1 : 17-12-2014, 11:32:59 »
Если я наследую от AcDbPolyline, (как в примере http://adn-cis.org/forum/index.php?topic=987.0) то в палетке свойств при выборе отображается что это полилиния,
    а не то имя, которое я задаю в ACRX_DXF_DEFINE_MEMBERS.
    соответственно ее можно отредактировать средствами AutoCAD а при сглаживании она заменяется на AcDb2dPolyline, отсюда вопрос как отобразить в палетке
    свойств моё имя примитива, или запретить любое пользовательское редактирование, кроме ручек (редактировать я буду только программно)
1. Чтобы в Панели свойств отображалось твоё имя примитива следуй этой инструкции: http://adndevblog.typepad.com/autocad/2013/01/changing-the-entity-type-name-that-appears-in-the-opm-window.html
2. Никакого соответствия между отображаемым именем примитива и тем позволительно или нет AutoCAD'у менять твой примитив нет. Тут нужно действовать иначе. А чтобы понять как именно это следует делать нужно понять полностью логику поведения твоего примитива. Если она не продумана в деталях сразу, то очень вероятно, что в какой-то момент придётся переписывать код с нуля. В ряде случаев удобно наследовать от AcDbPolyline, в ряде от AcDbCurve, в ряде вообще от AcDbEntity. Наследование от AcDbPolyline в ряде случаев проще, но с другой стороны накладывает ряд ограничений на поведение примитива, связанных с особенностью обработки AutoCAD таких примитивов. И в разных версиях AutoCAD эта особенность может быть разной.
А вообще могу лишь посочувствовать. Custom Entity - одна из наиболее сложных (если не самая сложная) тема в ObjectARX.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн GennadiyАвтор темы

  • ADN Club
  • **
  • Сообщений: 81
  • Карма: 0
Re: CustomEntity
« Ответ #2 : 17-12-2014, 12:57:34 »
Цитировать
1. Чтобы в Панели свойств отображалось твоё имя примитива следуй этой инструкции: http://adndevblog.typepad.com/autocad/2013/01/changing-the-entity-type-name-that-appears-in-the-opm-window.html

если я правильно понял инструкции по этой ссылке, строку STDMETHOD(GetDisplayName) (DISPID dispId, BSTR *propName); я должен вставить в заголовочный файл MyPoly.h,
а метод
Код - C++ [Выбрать]
  1. STDMETHODIMP CCustomEntProps::GetDisplayName (DISPID dispId,
  2.                                           BSTR *propName)
  3. {
  4.  switch (dispId)
  5. {
  6.   // this is the property name title string
  7.  case (0x01):
  8.   *propName  = ::SysAllocString(L"my Text String");
  9.   break;
  10.   // this changes the title of the entity
  11.  case (0x401):
  12.   *propName  = ::SysAllocString(L"My Heading");
  13.   break;
  14. }
  15.  return S_OK;
  16. }
  17.  

в файл MyPoly.cpp
тогда вместо CCustomEntProps, я подставляю MyPoly?
я так сделал, но не работает. (AutoCAD 2015)

Цитировать
А чтобы понять как именно это следует делать нужно понять полностью логику поведения твоего примитива. Если она не продумана в деталях сразу, то очень вероятно, что в какой-то момент придётся переписывать код с нуля.

Собственно мне нужно самое простое: создать свою полилинию, чтобы она имела свое имя dxf и не могла редактироваться средствами autocad, кроме как ручками. Так как я пишу на c#,
то нужен wrapper, чтобы все остальное редактирование я мог делать программно уже в среде .net. Сейчас для этой цели я использую ObjectOverrule, но он работает только при загруженном моем приложении, а если оно не загружено, то пользователь может редактировать мою полилинию средсвами autocad как угодно, чего я и хочу избежать.
Поскольку
Цитировать
Наследование от AcDbPolyline в ряде случаев проще
остается один вопрос, как запретить менять AutoCAD'у мою полилинию?
« Последнее редактирование: 17-12-2014, 19:29:06 от Александр Ривилис »

Оффлайн Николай Горлов

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
Re: CustomEntity
« Ответ #3 : 17-12-2014, 15:57:42 »
ну для начала
Цитировать
нужен wrapper
это да. враппер - это класс, который собственно и отвечает за диспетчер свойств, если так можно выразиться. и вот собственно функция GetDisplayName является функцией враппера, а не dbx. для связи враппера и dbx есть функция subGetClassID (а это функция DBX)

Цитировать
пишу на c#
это зря. DBX и C# штука взрывоопасная :):):) лучше всего делать чистый проект на С++ для DBX+wrapper. а уж если хочется модифицировать его из C#, то это никто не мешает делать уже в совсем другой ARX, dll или чем-то другом, хотя, на вкус и цвет фломастеры разные :).

Цитировать
и не могла редактироваться средствами autocad
если DBX выгружен (не загружен в автокад), то объект становится прокси, и редактированию не подлежит. во всех остальных случаях достаточно выгрузить управляющие реакторы и можно делать с объектом все что душе угодно. се ля ви :). еще момент, если DBX был загружен в автокад, и есть загруженные чертежи, которые используют DBX, то выгрузить его просто не получится :):):)ю автокад не даст

Оффлайн GennadiyАвтор темы

  • ADN Club
  • **
  • Сообщений: 81
  • Карма: 0
Re: CustomEntity
« Ответ #4 : 17-12-2014, 17:26:19 »
Цитировать
лучше всего делать чистый проект на С++ для DBX+wrapper. а уж если хочется модифицировать его из C#, то это никто не мешает делать уже в совсем другой ARX, dll или чем-то другом

Я так и хочу в итоге сделать.

Цитировать
если DBX выгружен (не загружен в автокад), то объект становится прокси, и редактированию не подлежит. во всех остальных случаях достаточно выгрузить управляющие реакторы и можно делать с объектом все что душе угодно.

Мне нужно только чтобы пользователь не мог редактировать.

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

Оффлайн Николай Горлов

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
Re: CustomEntity
« Ответ #5 : 17-12-2014, 18:10:14 »
Цитировать
Мне нужно только чтобы пользователь не мог редактировать.
не совсем понятно, что значит редактировать. поменять слой, цвет, обрезать кусок объекта, скопировать, увеличит в 50 раз, вставить парочку вершин внутрь, взорвать, просто перетянуть целиком или несколько узловых точек? это ж всё редактирование. и не всё можно просто отключить программно. так что исходим из того, что нужен реактора на запрет модификации.

если упрощенно. есть dbx, есть управляющие реакторы на запрет редактирования, твои собственные.
 a) пока всё это загружено в автокаде, пользователь ограничен в действиях (реакторы мешают)
выгружаем самостоятельно реакторы, ну или закончился период триальной версии (если проект коммерческий, такое может быть). всё. arx, dll, vlx и другое "подзащитное" выгрузилось из акада или перестало работать. а dbx НЕ выгрузился. автокад не даст его выгрузить, пока хоть один из загруженных чертежей его использует. вот и всё. теперь пользователь может редактировать объект в обход всех написанных ограничений. так понятней?

б) если чертеж открыли на компьютере, где НЕ установлено приложение. при грамотном программировании dbx, пользователь увидит объект, внешне ничем не отличающийся от оригинала (при неграмотном ничего не увидит, или увидит рамку с текстом :) как в примерах objectarx), НО, это будет прокси объект, который не поддается НИКАКОЙ модификации вообще. (разве что удалить)

вариант Б подходит на 100% исходя из условия. отредактировать нельзя вообще.
вариант A нуждается в дополнительной проверке загруженности управляющих реакторов изнутри DBX. есть реакторы - ведем себя определенным образом, нет реакторов - изображаем из себя недотрогу :). но тут есть маааааленькая проблема. из DBX очень сложно  достучаться до большинства arx-овых функций. чисто для примера, ну никак не выйдет попользоваться acedGet... и т.п. изнутрей dbx. так что первоначально нужно всё продумать, как что проверять. гараздо легче из dbx достучаться до файловой системы, чем до системных переменных автокада :)


PS: а если говорить в общем, то не совсем понятно, зачем нужен объект, который нельзя модифицировать :) но очень нужно отображать его свойства в диспетчере свойств. как минимум прийдется ставить затычки на родные команды автокада, типа удлинить, обрезать, взорвать, скопировать,.... охохохохохохо. может растр вставить? он то точно не редактируется :)

Цитировать
Про остальное вышеизложенное, честно говоря, мне без хотя бы мало мальского примера в виде куска кода ничего не понятно.
и я так понимаю, что этот пример должен в себя включать именно нередактируемый объект? :) увы. помочь разобраться в чужом коде могу. но вот писать что-то уж сильно лениво. а примеры просто custom объектов есть и в интернете и даже в arx2008 samples\smiley. ток начинающему пример из samples не сильно поможет, т.к. работает только в 2005 студии. портировать его хотя б в VS2008 настолько геморройно, что проще написать с нуля.

нашел у себя мануальчик по врапперам. подходит для создания dbx начиная с 2010 автокад и по сей день.
https://yadi.sk/d/vDq1tr4NdSeXR

/* Переложил на наш ресурс (Александр Ривилис) */

Создание COM-wrapper (COM-оберток) для собственных примитивов (Custom Entity)
« Последнее редактирование: 17-12-2014, 19:39:13 от Александр Ривилис »

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: CustomEntity
« Ответ #6 : 17-12-2014, 19:45:54 »
Gennadiy, Николай абсолютно прав в том, что в твоём понимании "не редактируемый примитив" и в понимании любого другого могут быть совершенно разными вещами.
Еще одна мысль. Видимо в силу того, что ты писал на C# ты под wrapper понимаешь .NET wrapper над native C++ кодом. В данном же случае (чтобы имя примитива было не "Polyline", а "MyPolyline")  нужно сделать COM-wrapper. В предыдущем сообщении руководство как его делать при помощи ObjectARX Wizard.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн GennadiyАвтор темы

  • ADN Club
  • **
  • Сообщений: 81
  • Карма: 0
Re: CustomEntity
« Ответ #7 : 18-12-2014, 09:28:23 »
Цитировать
PS: а если говорить в общем, то не совсем понятно, зачем нужен объект, который нельзя модифицировать :) но очень нужно отображать его свойства в диспетчере свойств.

Поясню зачем.
В некоторых вертикальных приложениях Autodesk есть такой объект, как трасса или труба. С каждой узловой точкой связана информация и производятся какие то расчеты.
Почему программисты не сделали этот объект обычной полилинией и не пишут туда данные в словари? Наверное потому, что если пользователь вызовет команду _pedit и добавит или удалит вершины, замкнет, сгладит, реверс и т.д. или разорвет в точке, копирует ... Нарушится вся работа и логика такого примитива. Такое редактирование должно проводится только через предусмотренные программистом команды, а не командами AutoCAD.

Анализируя советы коллег, я пришел к выводу, что наследовать от AcDbPolyline мне не стоит, за кажущейся простотой скрывается слишком большой объем работы по разработке управляющих реакторов на всевозможные запреты редактирования Autocad'ом, написании COM-wrapper только для того чтобы отображалось имя в свойствах не Polyline а MyPolyline.

Я решил наследовать от DbCurve. Есть мысль переделать пример http://adn-cis.org/forum/index.php?topic=987.0 в первоначальном его виде, заменив в нем наследование
AuPolyline от AcDbPolyline на AcDbCurve, а MyHatch на MyPoly. Таким образом у меня получится класс MyPoly наследуемый от AcDbPolyline вложенный в класс AuPoluline наследуемый от
AcDbCurve. Решаются сразу две задачи: отображаемое имя в окне свойств будет "MyPolyline" без использования COM-wrapper; редактировать такой объект командами autocad будет невозможно. А все переопределяемые методы от AcDbCurve мне проще переписать самому.

Цитировать
Еще одна мысль. Видимо в силу того, что ты писал на C# ты под wrapper понимаешь с над native C++ кодом.

Совершенно верно. Затем мне нужно будет написать .NET wrapper для использования моего примитива в программах на c#.

Но пока это только мои мысли. Как что то реализую выложу код на ваш суд.

Оффлайн Николай Горлов

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
Re: CustomEntity
« Ответ #8 : 18-12-2014, 10:39:26 »
конечно, со своим уставом в чужой монастырь не ходят, но все же вставлю пять копеек.

Цитировать
...если пользователь вызовет команду _pedit и добавит или удалит вершины, замкнет, сгладит, реверс и т.д. или разорвет в точке, копирует ... Нарушится вся работа и логика такого примитива. Такое редактирование должно проводится только через предусмотренные программистом команды, а не командами AutoCAD.
то, что нарушится работа и логика такого примитива - вина разработчика, который НЕ МОЖЕТ описать последствие таких действий.
то, что редактирование должно проводится только собственноручно написанными командами - это ж надо, разработать эти команды и быть полностью уверенным, что они отработают так, как и должны в любой ситуации (ой, жаль нельзя обсуждать еще не вышедшие в продажу версии продуктов автодеск :):):) а то б я подрасказал)

итог, один грамотно написанный реактор позволяет заменить пару месяцев кропотливой работы, и переложить основной груз на плечи самого автокада. да и человеку, привыкшему работать в автокаде будет не слишком то удобно редактировать ВСЁ обычными средствами, а конкретно "трубу" - её собственными командами редактирования. так что на пользователя мы ... переучим, и станет он послушным и благодарным.

Цитировать
Таким образом у меня получится класс MyPoly наследуемый от AcDbPolyline вложенный в класс AuPoluline наследуемый от AcDbCurve.
а двигатель то у машинки будет в багажнике однако :)

Цитировать
А все переопределяемые методы от AcDbCurve мне проще переписать самому.
можно подумать, что методы acdbpolyline написались бы сами. только у acdbcurve нужно еще предусматривать, что на вход может прийти, скажем, эллиптическая дуга или сплайн, а не только полилиния.

кстати, если разобраться, как работают автокадовские команды редактирования, то можно прийти к заключению, что всё завязано на такие функции из BDX как subGetTransformedCopy, subGetGeomExtents, subIntersectWith, subTransformBy, subExplode, getSplitCurves, extend и т.п. так вот, если их оставить пустыми, то не будет ни копирования, ни обрезания ни чего либо другого. ХОРОШО, этого мы и хотим.
НО, возникает закономерный вопрос, а КАК МЫ ПОТОМ САМИ БУДЕМ РЕДАКТИРОВАТЬ ЭТОТ ОБЪЕКТ, если пользоваться будем теми же методами, которые так успешно заблокировали?

PS: с нетерпением жду исходники :)

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: CustomEntity
« Ответ #9 : 18-12-2014, 12:34:42 »
НО, возникает закономерный вопрос, а КАК МЫ ПОТОМ САМИ БУДЕМ РЕДАКТИРОВАТЬ ЭТОТ ОБЪЕКТ, если пользоваться будем теми же методами, которые так успешно заблокировали?
Ну это в принципе не сложно реализовать. Например, при помощи собственного метода MyEnt::setEditible(bool isEditible). Конечно методы отвечающие за редактирование не пустые, но если не включена возможность редактирования, то немедленный возврат. В своих командах после открытия примитива вызывается pEnt->setEditible(true); ну а потом уже остальные методы. Всё достаточно не сложно.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 624
  • Карма: 158
    • ПГСу Бложик
Re: CustomEntity
« Ответ #10 : 07-04-2015, 12:40:02 »
Я конечно понимаю, что влезаю не в свое дело, но может автору посмотреть на MultiCAD.net api и с его помощью создать себе нужный приметив? Это не сложнее , чем динамический блок сделать...

На всякий случай: http://adn-cis.org/forum/index.php?topic=1890.msg7468#msg7468


PS.
Вот некоторые мои экзерсисы на эту тему:


Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. using Multicad;
  8. using Multicad.AplicationServices;
  9. using Multicad.Runtime;
  10. using Multicad.DatabaseServices;
  11. using Multicad.DataServices;
  12. using Multicad.Geometry;
  13. using Multicad.CustomObjectBase;
  14. using System.ComponentModel;
  15.  
  16.  
  17. namespace CustomObject_Rebar
  18. {
  19.     [CustomEntity(typeof(Rebar),
  20.         "6B5A3569-E4BF-485A-ABFE-0C90E4ACD9D1",
  21.         "Rebar",
  22.         "RebarEntity")]
  23.     [Serializable]
  24.     class Rebar : McCustomBase
  25.     {
  26.         private List<Point3d> _pntList = new List<Point3d> {};
  27.         private int _d = 10; // диаметр
  28.         private double _d_op = 10; // диаметр оправки
  29.  
  30.         //Создание
  31.         public Rebar()
  32.         {
  33.         }
  34.  
  35.       //Параметры в таблице свойств
  36.         [DisplayName("Диаметр")]
  37.         [Description("Диаметр арматуры")]
  38.         [Category("Rebar")]
  39.         public int D
  40.         {
  41.             get
  42.             {
  43.                 return _d;
  44.             }
  45.             set
  46.             {
  47.                 if (!TryModify()) return;
  48.                 _d = value;
  49.             }
  50.         }
  51.  
  52.  
  53.         [DisplayName("Длина")]
  54.         [Description("Длина арматуры")]
  55.         [Category("Rebar")]
  56.         public Double L
  57.         {
  58.             get
  59.             {
  60.                 Polyline3d pl = new Polyline3d(_pntList);
  61.                 return pl.Length;
  62.             }
  63.             set
  64.             {
  65.             }
  66.         }
  67.  
  68.         [DisplayName("Оправка")]
  69.         [Description("Диаметр оправки, указывается в единицах диаметра. т.е ")]
  70.         [Category("Rebar")]
  71.         public double OP
  72.         {
  73.             get
  74.             {
  75.                 return _d_op;
  76.             }
  77.             set
  78.             {
  79.                 if (!TryModify()) return;
  80.                 _d_op = value;
  81.             }
  82.         }
  83.  
  84.         //Перерисовка
  85.         public override void OnDraw(GeometryBuilder dc)
  86.         {
  87.             dc.Clear();
  88.             Polyline3d pol = new Polyline3d();
  89.             foreach (Point3d i in _pntList)
  90.             {
  91.                 pol.Vertices.AddVertex(i);
  92.             }
  93.             for (int i = 0; i < pol.Vertices.Count;i++ )
  94.             {
  95.                 pol.Vertices.MakeFilletAtVertex(i,_d_op);
  96.             }
  97.             dc.Color = McDbEntity.ByObject;
  98.             List<Polyline3d> polOffPlus = pol.GetTrimmedOffset(_d / 2);
  99.             Polyline3d pol_p = new Polyline3d();
  100.             foreach(Polyline3d p in polOffPlus)
  101.             {
  102.                 pol_p.AddGeometry(p);
  103.             }
  104.             dc.DrawPolyline(pol_p);
  105.             List<Polyline3d> polOffNega = pol.GetTrimmedOffset(_d / -2);
  106.             Polyline3d pol_n = new Polyline3d();
  107.             foreach (Polyline3d p in polOffNega)
  108.             {
  109.                 pol_n.AddGeometry(p);
  110.             }
  111.             dc.DrawPolyline(pol_n);          
  112.         }
  113.  
  114.         //Трансформация
  115.         public override void OnTransform(Matrix3d tfm)
  116.         {
  117.             if (!TryModify()) return;
  118.             for (int i = 0; i < _pntList.Count;i++ )
  119.             {
  120.                 _pntList[i] = _pntList[i].TransformBy(tfm);
  121.             }
  122.         }
  123.  
  124.         //ручки
  125.         public override List<Point3d> OnGetGripPoints()
  126.         {
  127.             return _pntList;
  128.         }
  129.  
  130.         //Движение ручек
  131.         public override void OnMoveGripPoints(List<int> indexes, Vector3d offset, bool isStretch)
  132.         {
  133.             if (!TryModify()) return;
  134.  
  135.             foreach (int i in indexes)
  136.             {
  137.                 _pntList[i] += offset;
  138.             }
  139.         }
  140.      
  141.       //Добавление в чертеж
  142.         public override hresult PlaceObject(PlaceFlags lInsertType)
  143.         {
  144.             InputJig jig = new InputJig();
  145.             InputResult res;
  146.             while (true)
  147.             {
  148.                 res = jig.GetPoint("Select point:");
  149.                 if (res.Result != InputResult.ResultCode.Normal)
  150.                 {
  151.                     _pntList.RemoveAt(_pntList.Count - 1);
  152.                     return hresult.e_Fail;
  153.                 }
  154.                 if (!_pntList.Contains(res.Point))
  155.                 {
  156.                     _pntList.Add(res.Point);
  157.                 }
  158.                 DbEntity.AddToCurrentDocument();
  159.                 jig.ExcludeObject(ID);
  160.                 _pntList.Add(res.Point);
  161.                 jig.MouseMove = (s, a) =>
  162.                 {
  163.                     TryModify();
  164.                     _pntList[_pntList.Count - 1] = a.Point;
  165.                     DbEntity.Update();
  166.                 };
  167.             }
  168.             return hresult.s_Ok;
  169.         }
  170.     }
  171. }
  172.