Как найти исходный объект для временного клона?

Автор Тема: Как найти исходный объект для временного клона?  (Прочитано 605 раз)

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

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Здравствуйте!
Столкнулся с проблемой. Переопределяю отображение объекта (DrawableOverrule). Всё хорошо работает до тех пор, пока объект не начинают редактировать с помощью ручек. При редактировании ручками создаётся копия объекта, которая не находится в базе данных и её ObjectId нулевой. А у меня на ObjectId объекта завязана обработка, влияющая на отображение. Соответственно, отображение получается некорректным.
Вопрос: можно ли как-то найти ObjectId исходного объекта?
Либо, может быть, можно как-то однозначно отловить момент, в который создаётся эта копия, и сохранить нужные данные в буфер?
Пример кода (переделал этот, для пущей наглядности надо включить отображение толщины линии):
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Colors;
  3. using Autodesk.AutoCAD.Geometry;
  4. using Autodesk.AutoCAD.GraphicsInterface;
  5. using Autodesk.AutoCAD.Runtime;
  6. using AcDb = Autodesk.AutoCAD.DatabaseServices;
  7.  
  8. namespace MyFirstOverrule
  9. {
  10.     // This is our custom DrawableOverrule class.
  11.     // In this case we're just overruling WorldDraw
  12.     public class MyDrawOverrule : DrawableOverrule
  13.     {
  14.         public override bool WorldDraw(Drawable drawable, WorldDraw wd)
  15.         {
  16.             // Cast Drawable to Line so we can access its methods and
  17.             // properties
  18.             AcDb.Polyline pline = (AcDb.Polyline)drawable;
  19.  
  20.             if (pline.Id.IsNull)
  21.             {
  22.                 EntityColor oldCol = wd.SubEntityTraits.TrueColor;
  23.                 AcDb.LineWeight oldLw = wd.SubEntityTraits.LineWeight;
  24.                 wd.SubEntityTraits.TrueColor = new EntityColor(235, 240, 80);
  25.                 wd.SubEntityTraits.LineWeight = AcDb.LineWeight.LineWeight050;
  26.  
  27.                 for (int i = 1; i < pline.NumberOfVertices; i++)
  28.                 {
  29.                     Point3d
  30.                         ptEnd = pline.GetPoint3dAt(i),
  31.                         ptStart = pline.GetPoint3dAt(i - 1);
  32.                     // Draw some graphics primitives
  33.                     wd.Geometry.WorldLine(ptStart, ptEnd);
  34.                 }
  35.  
  36.                 wd.SubEntityTraits.TrueColor = oldCol;
  37.                 wd.SubEntityTraits.LineWeight = oldLw;
  38.                 // In this case we don't want the line to draw itself, nor do
  39.                 // we want ViewportDraw called
  40.                 return true;
  41.             }
  42.             else
  43.             {
  44.                 return base.WorldDraw(drawable, wd);
  45.             }
  46.         }
  47.     }
  48.  
  49.     public class Commands
  50.     {
  51.         //Shared member variable to store our Overrule instance
  52.         private static MyDrawOverrule _drawOverrule;
  53.  
  54.         [CommandMethod("TOG")]
  55.         public static void ToggleOverrule()
  56.         {
  57.             // Initialize Overrule if first time run
  58.             if (_drawOverrule == null)
  59.             {
  60.                 _drawOverrule = new MyDrawOverrule();
  61.                 Overrule.AddOverrule(RXObject.GetClass(typeof(AcDb.Polyline)), _drawOverrule, false);
  62.                 Overrule.Overruling = true;
  63.             }
  64.             else
  65.             {
  66.                 Overrule.RemoveOverrule(RXObject.GetClass(typeof(AcDb.Polyline)), _drawOverrule);
  67.                 _drawOverrule = null;
  68.             }
  69.  
  70.             // Regen is required to update changes on screen
  71.             Application.DocumentManager.MdiActiveDocument.Editor.Regen();
  72.         }
  73.     }
  74. }
« Последнее редактирование: 16-04-2018, 23:58:51 от Дмитрий Загорулькин »

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

  • Administrator
  • *****
  • Сообщений: 8795
  • Карма: 1089
  • Рыцарь ObjectARX
  • Skype: rivilis
Дмитрий Загорулькин
Боюсь, что единственный вариант - это хранить в Xdata свой собственный handle. В этом случае по нему в клоне можно будет найти исходный объект (это если клон правильный и Xdata тоже копируется).
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение Дмитрий Загорулькин 17-04-2018, 14:50:19

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

  • Administrator
  • *****
  • Сообщений: 8795
  • Карма: 1089
  • Рыцарь ObjectARX
  • Skype: rivilis
Нет. Всё же есть второй вариант. Подписаться на событие DBObject.Copied. Правда в нём причину копирования определить не так просто. Т.е. это может быть и результат команды _COPY и начало перетаскивания объекта... Впрочем тут можно еще проанализировать активную в данный момент команду.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Ох... Как-то это всё очень сложно... Что в первом способе, что во втором, нужно сперва пройтись по всем объектам и либо им прописать хендлы в XData, либо подписаться на событие их копирования. Отслеживать создание новых объектов, чтобы и с ними то же самое делать. Команду, опять же, проверять... Меня это, честно говоря, в тоску вгоняет - слишком громоздкий механизм получается. Хочется найти какое-то решение поизящнее :)

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

  • Administrator
  • *****
  • Сообщений: 8795
  • Карма: 1089
  • Рыцарь ObjectARX
  • Skype: rivilis
Если тебя интересует клон, который получается при изменении объекта ручками, то следи за pickfirst. Прежде чем объект можно будет редактировать ручками он должен быть выделен.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Мысль дельная! Но есть большая вероятность того, что будет выделено несколько объектов, а редактироваться ручками будет только один из них.

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

  • Administrator
  • *****
  • Сообщений: 8795
  • Карма: 1089
  • Рыцарь ObjectARX
  • Skype: rivilis
Мысль дельная! Но есть большая вероятность того, что будет выделено несколько объектов, а редактироваться ручками будет только один из них.
Ну тут ты можешь подписаться на DBObject.Copied :-)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Получилось! Не сказать, что простой способ, но терпимо :)
Спасибо!
Код-пример:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Colors;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.GraphicsInterface;
  7. using Autodesk.AutoCAD.Runtime;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using AcDb = Autodesk.AutoCAD.DatabaseServices;
  12.  
  13. namespace AcadTest
  14. {
  15.     public class PlineDrawOverrule : DrawableOverrule
  16.     {
  17.         public override bool WorldDraw(Drawable drawable, WorldDraw wd)
  18.         {            
  19.             AcDb.Polyline pline = (AcDb.Polyline)drawable;
  20.  
  21.             ObjectId plineId = pline.Id;
  22.  
  23.             if (plineId.IsNull)
  24.             {
  25.                 Commands.CopiesDict.TryGetValue(pline, out plineId);
  26.             }
  27.  
  28.             if (plineId.IsNull)
  29.             {
  30.                 EntityColor oldCol = wd.SubEntityTraits.TrueColor;
  31.                 AcDb.LineWeight oldLw = wd.SubEntityTraits.LineWeight;
  32.                 wd.SubEntityTraits.TrueColor = new EntityColor(235, 240, 80);
  33.                 wd.SubEntityTraits.LineWeight = AcDb.LineWeight.LineWeight050;
  34.  
  35.                 for (int i = 1; i < pline.NumberOfVertices; i++)
  36.                 {
  37.                     Point3d
  38.                         ptEnd = pline.GetPoint3dAt(i),
  39.                         ptStart = pline.GetPoint3dAt(i - 1);                  
  40.                     wd.Geometry.WorldLine(ptStart, ptEnd);
  41.                 }
  42.  
  43.                 wd.SubEntityTraits.TrueColor = oldCol;
  44.                 wd.SubEntityTraits.LineWeight = oldLw;          
  45.                 return true;
  46.             }
  47.             else
  48.             {
  49.                 return base.WorldDraw(drawable, wd);
  50.             }
  51.         }
  52.     }
  53.  
  54.     public class Commands
  55.     {
  56.         static RXClass m_plineClass = RXObject.GetClass(typeof(AcDb.Polyline));
  57.         static ObjectId[] m_prevSelIds = new ObjectId[0];
  58.         static PlineDrawOverrule m_drawOverrule;
  59.  
  60.         internal static Dictionary<DBObject, ObjectId>
  61.             CopiesDict = new Dictionary<DBObject, ObjectId>();
  62.  
  63.         [CommandMethod("TOG")]
  64.         public static void ToggleOverrule()
  65.         {
  66.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  67.             Editor ed = adoc.Editor;
  68.  
  69.             adoc.ImpliedSelectionChanged += Adoc_ImpliedSelectionChanged;
  70.  
  71.             // Initialize Overrule if first time run
  72.             if (m_drawOverrule == null)
  73.             {
  74.                 m_drawOverrule = new PlineDrawOverrule();              
  75.                 Overrule.AddOverrule(m_plineClass, m_drawOverrule, false);                
  76.                 Overrule.Overruling = true;
  77.                 ed.WriteMessage("\nOverrule on\n");
  78.             }
  79.             else
  80.             {
  81.                 Overrule.RemoveOverrule(m_plineClass, m_drawOverrule);              
  82.                 m_drawOverrule = null;              
  83.                 ed.WriteMessage("\nOverrule off\n");
  84.             }            
  85.             ed.Regen();
  86.         }
  87.  
  88.         private static void Adoc_ImpliedSelectionChanged(object sender, EventArgs e)
  89.         {
  90.             Document adoc = sender as Document;
  91.             Editor ed = adoc.Editor;          
  92.  
  93.             PromptSelectionResult selObj = ed.SelectImplied();
  94.  
  95.             if (selObj.Status != PromptStatus.OK)
  96.             {
  97.                 CopiesDict.Clear();
  98.                 return;
  99.             }
  100.  
  101.             ObjectId[] curSelIds = selObj.Value
  102.                 .GetObjectIds()
  103.                 .Where(id =>id.IsValid && id.ObjectClass.Equals(m_plineClass))                    
  104.                 .ToArray();
  105.  
  106.             foreach (ObjectId id in m_prevSelIds.Except(curSelIds))
  107.             {
  108.                 using (DBObject obj = id.Open(OpenMode.ForRead))
  109.                 {
  110.                     obj.Copied -= Obj_Copied;
  111.                 }
  112.             }
  113.            
  114.             foreach (ObjectId id in curSelIds.Except(m_prevSelIds))
  115.             {
  116.                 using (DBObject obj = id.Open(OpenMode.ForRead))
  117.                 {
  118.                     obj.Copied += Obj_Copied;
  119.                 }                
  120.             }
  121.  
  122.             m_prevSelIds = curSelIds;
  123.         }
  124.  
  125.         private static void Obj_Copied(object sender, ObjectEventArgs e)
  126.         {
  127.             CopiesDict[e.DBObject] = ((DBObject)sender).Id;
  128.         }
  129.     }
  130. }

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

  • Administrator
  • *****
  • Сообщений: 8795
  • Карма: 1089
  • Рыцарь ObjectARX
  • Skype: rivilis
Возможно я ошибаюсь, но мне кажется, что коллекция тебе не нужна. Достаточно иметь последний Obj_Copied. Думаю, что именно он и будет тот, который сейчас ручками редактируется. Впрочем возможно для сложного примитива это и не так.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Нужна-нужна :)
Маловероятно, конечно, но может быть такое, что пользователь выберет несколько объектов, через Shift выберет ручки на них и будет их перетаскивать одновременно.

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
А если использовать связку событий Editor.SelectionAdded(добавление обработчика Copied)+DBObject.Copied(сохранение связи копия-владелец), то отлавливаются копии не только при редактировании ручками, но и при любом другом редактировании (перенос, поворот, масштабирование, зеркало и т.п.)! Для очистки буферизированных данных приспособил Application.Idle. Получилось как-то так:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Colors;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.GraphicsInterface;
  7. using Autodesk.AutoCAD.Runtime;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using AcDb = Autodesk.AutoCAD.DatabaseServices;
  12.  
  13. namespace AcadTest
  14. {
  15.     public class PlineDrawOverrule : DrawableOverrule
  16.     {
  17.         public override bool WorldDraw(Drawable drawable, WorldDraw wd)
  18.         {
  19.             AcDb.Polyline pline = (AcDb.Polyline)drawable;
  20.  
  21.             ObjectId plineId = pline.Id;
  22.  
  23.             if (plineId.IsNull)
  24.             {
  25.                 Commands.CopiesDict.TryGetValue(pline, out plineId);            
  26.                 EntityColor oldCol = wd.SubEntityTraits.TrueColor;
  27.                 AcDb.LineWeight oldLw = wd.SubEntityTraits.LineWeight;
  28.                 short index = (short)(plineId.IsNull ? 1 : 3);
  29.                 wd.SubEntityTraits.TrueColor = new EntityColor(ColorMethod.ByAci, index);
  30.                 wd.SubEntityTraits.LineWeight = AcDb.LineWeight.LineWeight050;
  31.  
  32.                 for (int i = 1; i < pline.NumberOfVertices; i++)
  33.                 {
  34.                     Point3d
  35.                         ptEnd = pline.GetPoint3dAt(i),
  36.                         ptStart = pline.GetPoint3dAt(i - 1);
  37.                     wd.Geometry.WorldLine(ptStart, ptEnd);
  38.                 }
  39.  
  40.                 wd.SubEntityTraits.TrueColor = oldCol;
  41.                 wd.SubEntityTraits.LineWeight = oldLw;
  42.                 return true;
  43.             }
  44.             else
  45.             {
  46.                 return base.WorldDraw(drawable, wd);
  47.             }
  48.         }
  49.     }
  50.  
  51.     public class Commands
  52.     {
  53.         static RXClass m_plineClass = RXObject.GetClass(typeof(AcDb.Polyline));
  54.         static List<ObjectId> m_prevSelIds = new List<ObjectId>();
  55.         static PlineDrawOverrule m_drawOverrule;
  56.  
  57.         internal static Dictionary<DBObject, ObjectId>
  58.             CopiesDict = new Dictionary<DBObject, ObjectId>();
  59.  
  60.         [CommandMethod("TOG")]
  61.         public static void ToggleOverrule()
  62.         {
  63.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  64.             Editor ed = adoc.Editor;
  65.            
  66.             if (m_drawOverrule == null)
  67.             {
  68.                 m_drawOverrule = new PlineDrawOverrule();
  69.                 Overrule.AddOverrule(m_plineClass, m_drawOverrule, false);
  70.                 Overrule.Overruling = true;
  71.                 ed.SelectionAdded += Ed_SelectionAdded;
  72.                 Application.Idle += Application_Idle;
  73.                 ed.WriteMessage("\nOverrule on\n");
  74.             }
  75.             else
  76.             {
  77.                 Overrule.RemoveOverrule(m_plineClass, m_drawOverrule);
  78.                 m_drawOverrule = null;
  79.                 ed.SelectionAdded -= Ed_SelectionAdded;
  80.                 Application.Idle -= Application_Idle;
  81.                 ed.WriteMessage("\nOverrule off\n");
  82.             }
  83.  
  84.             ed.Regen();
  85.         }
  86.  
  87.         private static void Application_Idle(object sender, EventArgs e)
  88.         {
  89.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  90.             if (adoc is null
  91.                 || (string.IsNullOrEmpty(adoc.CommandInProgress)
  92.                 && adoc.Editor.SelectImplied().Status != PromptStatus.OK))
  93.             {
  94.                 ClearBuffer();
  95.             }
  96.         }
  97.  
  98.         private static void Ed_SelectionAdded(object sender, SelectionAddedEventArgs e)
  99.         {
  100.             ObjectId[] curSelIds = e.AddedObjects
  101.                 .GetObjectIds()
  102.                 .Where(id => id.IsValid && id.ObjectClass.Equals(m_plineClass))
  103.                 .Except(m_prevSelIds)
  104.                 .ToArray();
  105.  
  106.             foreach (ObjectId id in curSelIds)
  107.             {
  108.                 using (DBObject obj = id.Open(OpenMode.ForRead))
  109.                 {
  110.                     obj.Copied += Obj_Copied;
  111.                 }
  112.             }
  113.  
  114.             m_prevSelIds.AddRange(curSelIds);
  115.         }
  116.  
  117.         private static void ClearBuffer()
  118.         {
  119.             foreach (ObjectId id in m_prevSelIds)
  120.             {
  121.                 using (DBObject obj = id.Open(OpenMode.ForRead))
  122.                 {
  123.                     obj.Copied -= Obj_Copied;
  124.                 }
  125.             }
  126.             m_prevSelIds.Clear();
  127.             CopiesDict.Clear();
  128.         }
  129.  
  130.         private static void Obj_Copied(object sender, ObjectEventArgs e)
  131.         {
  132.             CopiesDict[e.DBObject] = ((DBObject)sender).Id;
  133.         }
  134.     }
  135. }

Оффлайн Вильдар

  • ADN Club
  • ****
  • Сообщений: 291
  • Карма: 42
  • Skype: vildar82
А почему не подходит вариант пропускать свою прорисовку, когда пустой id у drawable?

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

  • Administrator
  • *****
  • Сообщений: 8795
  • Карма: 1089
  • Рыцарь ObjectARX
  • Skype: rivilis
А почему не подходит вариант пропускать свою прорисовку, когда пустой id у drawable?
Наверное потому, что хочется видеть и в этой ситуации вместо стандартного изображения своё.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
Именно так. Я немного дорисовываю стандартное отображение. При редактировании мои дорисовки пропадают, а стандартное отображение видно. Не сказать, что это прямо сильно критично, но выглядит непрезентабельно.

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 1727
  • Карма: 426
  • LISP/C#, AutoCAD/Civil 3D
  • Skype: zagor_dmtr
А почему не подходит вариант пропускать свою прорисовку, когда пустой id у drawable?
Тогда это выглядит вот так:

Согласитесь, что не очень как-то?
Когда я попытался это исправить, у меня получилось вот так:

То есть, в таком вот сложном случае (когда несколько одинаковых меток), метка не может корректно определить своё положение. Это было как раз из-за того, что я не мог вычислить её Id.
А теперь это выглядит вот так: