Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Дмитрий Загорулькин от 16-04-2018, 21:26:49

Название: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 16-04-2018, 21:26:49
Здравствуйте!
Столкнулся с проблемой. Переопределяю отображение объекта (DrawableOverrule). Всё хорошо работает до тех пор, пока объект не начинают редактировать с помощью ручек. При редактировании ручками создаётся копия объекта, которая не находится в базе данных и её ObjectId нулевой. А у меня на ObjectId объекта завязана обработка, влияющая на отображение. Соответственно, отображение получается некорректным.
Вопрос: можно ли как-то найти ObjectId исходного объекта?
Либо, может быть, можно как-то однозначно отловить момент, в который создаётся эта копия, и сохранить нужные данные в буфер?
Пример кода (переделал этот (http://through-the-interface.typepad.com/through_the_interface/2009/08/a-simple-overrule-to-change-the-way-autocad-lines-are-displayed-using-net.html), для пущей наглядности надо включить отображение толщины линии):
Код - 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. }
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Александр Ривилис от 16-04-2018, 23:38:59
Дмитрий Загорулькин
Боюсь, что единственный вариант - это хранить в Xdata свой собственный handle. В этом случае по нему в клоне можно будет найти исходный объект (это если клон правильный и Xdata тоже копируется).
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Александр Ривилис от 16-04-2018, 23:49:20
Нет. Всё же есть второй вариант. Подписаться на событие DBObject.Copied. Правда в нём причину копирования определить не так просто. Т.е. это может быть и результат команды _COPY и начало перетаскивания объекта... Впрочем тут можно еще проанализировать активную в данный момент команду.
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 17-04-2018, 00:08:13
Ох... Как-то это всё очень сложно... Что в первом способе, что во втором, нужно сперва пройтись по всем объектам и либо им прописать хендлы в XData, либо подписаться на событие их копирования. Отслеживать создание новых объектов, чтобы и с ними то же самое делать. Команду, опять же, проверять... Меня это, честно говоря, в тоску вгоняет - слишком громоздкий механизм получается. Хочется найти какое-то решение поизящнее :)
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Александр Ривилис от 17-04-2018, 00:17:56
Если тебя интересует клон, который получается при изменении объекта ручками, то следи за pickfirst. Прежде чем объект можно будет редактировать ручками он должен быть выделен.
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 17-04-2018, 11:40:16
Мысль дельная! Но есть большая вероятность того, что будет выделено несколько объектов, а редактироваться ручками будет только один из них.
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Александр Ривилис от 17-04-2018, 12:10:47
Мысль дельная! Но есть большая вероятность того, что будет выделено несколько объектов, а редактироваться ручками будет только один из них.
Ну тут ты можешь подписаться на DBObject.Copied :-)
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 17-04-2018, 13:49:45
Получилось! Не сказать, что простой способ, но терпимо :)
Спасибо!
Код-пример:
Код - 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. }
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Александр Ривилис от 17-04-2018, 13:59:44
Возможно я ошибаюсь, но мне кажется, что коллекция тебе не нужна. Достаточно иметь последний Obj_Copied. Думаю, что именно он и будет тот, который сейчас ручками редактируется. Впрочем возможно для сложного примитива это и не так.
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 17-04-2018, 14:36:33
Нужна-нужна :)
Маловероятно, конечно, но может быть такое, что пользователь выберет несколько объектов, через Shift выберет ручки на них и будет их перетаскивать одновременно.
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 17-04-2018, 15:53:54
А если использовать связку событий 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. }
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Вильдар от 18-04-2018, 08:58:15
А почему не подходит вариант пропускать свою прорисовку, когда пустой id у drawable?
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Александр Ривилис от 18-04-2018, 10:46:53
А почему не подходит вариант пропускать свою прорисовку, когда пустой id у drawable?
Наверное потому, что хочется видеть и в этой ситуации вместо стандартного изображения своё.
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 18-04-2018, 11:17:23
Именно так. Я немного дорисовываю стандартное отображение. При редактировании мои дорисовки пропадают, а стандартное отображение видно. Не сказать, что это прямо сильно критично, но выглядит непрезентабельно.
Название: Re: Как найти исходный объект для временного клона?
Отправлено: Дмитрий Загорулькин от 18-04-2018, 15:35:22
А почему не подходит вариант пропускать свою прорисовку, когда пустой id у drawable?
Тогда это выглядит вот так:
(https://s9.postimg.cc/8fqirldnf/skip_draw.gif) (https://postimg.cc/image/8fqirldnf/)
Согласитесь, что не очень как-то?
Когда я попытался это исправить, у меня получилось вот так:
(https://s9.postimg.cc/khlwlskbv/wrong_draw.gif) (https://postimg.cc/image/khlwlskbv/)
То есть, в таком вот сложном случае (когда несколько одинаковых меток), метка не может корректно определить своё положение. Это было как раз из-за того, что я не мог вычислить её Id.
А теперь это выглядит вот так:
(https://s9.postimg.cc/jtd22hyd7/true_draw.gif) (https://postimg.cc/image/jtd22hyd7/)