Есть ли возможность изменить отображение объекта в "Просмотре объектов"?

Автор Тема: Есть ли возможность изменить отображение объекта в "Просмотре объектов"?  (Прочитано 3193 раз)

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

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Всем привет!
Появилась необходимость изменить вид объекта (трубы) в утилите Civil 3D "Просмотр объектов".

Предварительные опыты показали, что загруженный в чертёж код, выполняющий перерисовку объекта (DrawableOverrule), не используется в этом просмотрщике. Подозреваю, что для труб и колодцев берутся их Solid3d и передаются в него. В API он доступен только для чтения.
В общем, пока что мне задача видится как невыполнимая. Может быть, у вас есть какие-то идеи?
« Последнее редактирование: 15-02-2018, 15:20:31 от Александр Ривилис »

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
В этом окне точно не работает DrawableOverrule. Так что это тупик. Теоретически можно пойти другим путём - создать своё окно для просмотра объектов (пример BlockView.NET: http://adndevblog.typepad.com/autocad/Downloads/BlockView.NET.zip)
Наверное в нём можно было бы дорисовывать то, что тебе нужно, но это всё равно без использования DrawableOverrule).
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
В этом окне точно не работает DrawableOverrule. Так что это тупик.
Печально. Скорее всего, у этого просмотрщика вообще нет никакого API и что-то с ним сделать невозможно.
Теоретически можно пойти другим путём - создать своё окно для просмотра объектов (пример BlockView.NET: http://adndevblog.typepad.com/autocad/Downloads/BlockView.NET.zip)
Очень интересный пример! Спасибо!
Но вряд ли у меня получится сделать адекватную замену стандартному просмотрщику. Он же позволяет просмотреть всю 3D модель чертежа в таком виде, который практически не вызывает подтормаживаний. Причём, просматривать можно не только трубы и колодцы, но и все другие трёхмерные объекты Civil 3D и AutoCAD. Реализовать это будет очень трудозатратно...
Вообще, у меня появилась одна очень интересная идея. Попробую, если получится - расскажу :)

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
В общем, судя по всему, получится.
Суть идеи очень проста: подмена объектов в предварительном выборе на временные 3D тела по событиям запуска-завершения команды "Просмотр объектов" ("AECOBJECTVIEWER"). Просмотрщику важна только геометрия объектов, поэтому, должно получиться. По крайней мере, предварительные тесты отработали как надо.

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Вроде получилось, но в работе присутствуют иногда глюки такого плана: выбираю объекты, запускаю просмотрщик, а он просит снова выбрать объекты. Где-то идёт рассинхрон между событиями. Буду разбираться, в чём причина.
Но, в целом, работает забавно :)

Код:
Код - 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.Runtime;
  7. using Autodesk.Civil.DatabaseServices;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11.  
  12. [assembly:ExtensionApplication(typeof(C3dTest.TestCustomObjectViewer))]
  13.  
  14. namespace C3dTest
  15. {
  16.     public class TestCustomObjectViewer : IExtensionApplication
  17.     {
  18.         static Dictionary<Document, bool> _runDict = new Dictionary<Document, bool>();
  19.  
  20.         static ObjectId[] _pipeTmpObjIds;
  21.         static List<ObjectId> _allSelObjIds;        
  22.  
  23.         static RXClass _pipeClass = RXClass.GetClass(typeof(Pipe));
  24.  
  25.         public void Initialize()
  26.         {
  27.             Application.DocumentManager.DocumentLockModeChanged
  28.                 += DocumentManager_DocumentLockModeChanged;
  29.             Application.DocumentManager.DocumentToBeDestroyed
  30.                 += DocumentManager_DocumentToBeDestroyed;
  31.         }      
  32.  
  33.         public void Terminate()
  34.         {
  35.         }
  36.  
  37.         [CommandMethod("TestCustomObjectViewerRun")]
  38.         public static void RunTest()
  39.         {
  40.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  41.  
  42.             if (_runDict.TryGetValue(adoc, out bool started) && started)
  43.             {
  44.                 return;
  45.             }
  46.  
  47.             Editor ed = adoc.Editor;
  48.  
  49.             adoc.CommandWillStart += Adoc_CommandWillStart;
  50.             adoc.CommandCancelled += Adoc_CommandEnded;
  51.             adoc.CommandEnded += Adoc_CommandEnded;
  52.             adoc.CommandFailed += Adoc_CommandEnded;
  53.  
  54.             _runDict[adoc] = true;
  55.         }
  56.  
  57.         [CommandMethod("TestCustomObjectViewerStop")]
  58.         public static void StopTest()
  59.         {
  60.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  61.  
  62.             if (_runDict.TryGetValue(adoc, out bool started) && !started)
  63.             {
  64.                 return;
  65.             }
  66.  
  67.             Editor ed = adoc.Editor;
  68.             adoc.CommandWillStart -= Adoc_CommandWillStart;
  69.             adoc.CommandCancelled -= Adoc_CommandEnded;
  70.             adoc.CommandEnded -= Adoc_CommandEnded;
  71.             adoc.CommandFailed -= Adoc_CommandEnded;
  72.  
  73.             _runDict[adoc] = false;
  74.         }
  75.  
  76.         static void DocumentManager_DocumentLockModeChanged
  77.             (object sender, DocumentLockModeChangedEventArgs e)
  78.         {
  79.             if ((e.CurrentMode & DocumentLockMode.Write) == DocumentLockMode.Write
  80.                 && e.GlobalCommandName.ToUpper() == "AECOBJECTVIEWER"
  81.                 && (sender as DocumentCollection).MdiActiveDocument is Document adoc
  82.                 && _runDict.TryGetValue(adoc, out bool started)
  83.                 && started)
  84.             {                                
  85.                 PromptSelectionResult implSelRes = adoc.Editor.SelectImplied();
  86.                 if (implSelRes.Status == PromptStatus.OK)
  87.                 {
  88.                     _allSelObjIds = implSelRes.Value.GetObjectIds().ToList();
  89.                 }
  90.                 else
  91.                 {
  92.                     // Если Просмотр объектов запущен без предварительного выбора
  93.                     PromptSelectionOptions selOpts = new PromptSelectionOptions
  94.                     {
  95.                         AllowDuplicates = false,
  96.                         AllowSubSelections = false,
  97.                         RejectObjectsFromNonCurrentSpace = true,
  98.                         RejectPaperspaceViewport = true
  99.                     };
  100.                     // Предлагаем выбрать объекты
  101.                     PromptSelectionResult selRes = adoc.Editor.GetSelection(selOpts);
  102.                     // Если объекты выбраны
  103.                     if (selRes.Status == PromptStatus.OK)
  104.                     {
  105.                         // Запоминаем их для обработки
  106.                         _allSelObjIds = selRes.Value.GetObjectIds().ToList();
  107.                     }
  108.                     else
  109.                     {
  110.                         // Если не выбраны - прерываем запуск команды
  111.                         e.Veto();
  112.                         return;
  113.                     }
  114.                 }
  115.  
  116.                 // Ищем все трубы
  117.                 ObjectId[] selPipesIds = _allSelObjIds.Where
  118.                         (id => id.ObjectClass.Equals(_pipeClass)).ToArray();
  119.                 // Если нашли
  120.                 if (selPipesIds.Count() > 0)
  121.                 {
  122.                     // Удаляем трубы из списка
  123.                     _allSelObjIds.RemoveAll
  124.                         (item => selPipesIds.Contains(item));
  125.                     // Создаём объекты для подмены
  126.                     _pipeTmpObjIds = selPipesIds.Select
  127.                         (pipeId => GetPipeTmpObject(pipeId)).ToArray();
  128.                     // Добавляем объекты подмены в список
  129.                     _allSelObjIds.AddRange(_pipeTmpObjIds);
  130.                 }                
  131.             }
  132.         }
  133.  
  134.         static void DocumentManager_DocumentToBeDestroyed
  135.             (object sender, DocumentCollectionEventArgs e)
  136.         {
  137.             _runDict.Remove(e.Document);
  138.         }
  139.  
  140.         static void Adoc_CommandWillStart(object sender, CommandEventArgs e)
  141.         {
  142.             // Если стартует команда Просмотр объектов
  143.             if (e.GlobalCommandName.ToUpper() == "AECOBJECTVIEWER"
  144.                 // и есть обработанные объекты для предвыбора
  145.                 && _allSelObjIds != null
  146.                 && _allSelObjIds.Count > 0)
  147.             {
  148.                 // Назначаем предвыбор
  149.                 (sender as Document).Editor.SetImpliedSelection(_allSelObjIds.ToArray());
  150.                 _allSelObjIds = null;
  151.             }
  152.         }
  153.  
  154.         static void Adoc_CommandEnded(object sender, CommandEventArgs e)
  155.         {
  156.             // Если завершена команда "Просмотр объектов"
  157.             if (e.GlobalCommandName.ToUpper() == "AECOBJECTVIEWER"
  158.                 // и есть временные объекты
  159.                 && _pipeTmpObjIds != null)
  160.             {
  161.                 // Удаляем их
  162.                 foreach (ObjectId id in _pipeTmpObjIds)
  163.                 {
  164. #pragma warning disable CS0618 // Type or member is obsolete
  165.                     using (var ent = id.Open(OpenMode.ForWrite))
  166. #pragma warning restore CS0618 // Type or member is obsolete
  167.                     {
  168.                         ent.Erase(true);
  169.                     }
  170.                 }
  171.                 _pipeTmpObjIds = null;
  172.             }
  173.         }      
  174.        
  175.         static ObjectId GetPipeTmpObject(ObjectId pipeId)
  176.         {
  177.             Point3d start, end;
  178.             double innerRad;
  179. #pragma warning disable CS0618 // Type or member is obsolete
  180.             using (Pipe pipe = pipeId.Open(OpenMode.ForRead) as Pipe)
  181. #pragma warning restore CS0618 // Type or member is obsolete
  182.             {
  183.                 start = pipe.StartPoint;
  184.                 end = pipe.EndPoint;
  185.  
  186.                 innerRad = pipe.InnerDiameterOrWidth * 4;                
  187.             }
  188.  
  189.             Vector3d normal = start.GetVectorTo(end).GetNormal();
  190.             SweepOptionsBuilder optBuild = new SweepOptionsBuilder();
  191.             optBuild.TwistAngle = Math.PI;
  192.             SweepOptions opts = optBuild.ToSweepOptions();
  193.             using (SweptSurface surf = new SweptSurface())
  194.             {              
  195.                 using (Polyline pline = new Polyline(4))
  196.                 using (Line line = new Line(start, end))
  197.                 {
  198.                     pline.AddVertexAt(0, new Point2d(-innerRad, -innerRad), 0, 0, 0);
  199.                     pline.AddVertexAt(1, new Point2d(innerRad, -innerRad), 0, 0, 0);
  200.                     pline.AddVertexAt(2, new Point2d(innerRad, innerRad), 0, 0, 0);
  201.                     pline.AddVertexAt(3, new Point2d(-innerRad, innerRad), 0, 0, 0);
  202.                     pline.Closed = true;
  203.                     pline.Normal = normal;
  204.                     pline.TransformBy(Matrix3d.Displacement(start.GetAsVector()));                    
  205.                     surf.CreateSweptSurface(pline, line, opts);
  206.                 }
  207.                 // Сделаем трубы фиолетовыми
  208.                 surf.Color = Color.FromColorIndex(ColorMethod.ByAci, 6);
  209. #pragma warning disable CS0618 // Type or member is obsolete
  210.                 using (BlockTableRecord bRec
  211.                     = pipeId.Database.CurrentSpaceId.Open(OpenMode.ForWrite) as BlockTableRecord)
  212. #pragma warning restore CS0618 // Type or member is obsolete
  213.                 {
  214.                     return bRec.AppendEntity(surf);
  215.                 }
  216.             }
  217.         }        
  218.     }
  219. }
  220.  

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
В общем, получилось сделать даже двумя способами! Александр Наумович, спасибо за помощь!
Оба работают как надо. Есть ощутимые подтормаживания если выбрано много труб, но оно и понятно - надо много создать новых 3D объектов в чертеже.
Пока сам не знаю, на каком в итоге остановлюсь - оба имеют свои недостатки.
Первый способ - удалось настроить нормальную работу различных командных событий. Суть такова, что используется цепочка следующих событий:
1. Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.DocumentLockModeChanged - для проверки предварительного выбора перед запуском команды "Просмотр объектов". Если предварительного выбора нет - предлагается выбрать объекты. Если выбор объектов неудачен - запуск команды прерывается (это проще всего сделать именно в этом событии, поэтому, оно и используется). Если же получены выбранные объекты - заменяем трубы на временные объекты-заменители и подписываемся на следующее событие.
2. Autodesk.AutoCAD.ApplicationServices.Application.Document.CommandWillStart - для назначения предварительного выбора перед началом работы стандартной команды. Если это сделать в предыдущем событии, то будут сбои в работе AutoCAD. Тут же подписываемся на события завершения команды.
3. Autodesk.AutoCAD.ApplicationServices.Application.Document.CommandEnded - для удаления временных объектов-заменителей.
Обработка события 1 включена всегда, пока работает приложение. События 2 и 3 при срабатывании сами отключают свои обработчики.
Код - 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.Runtime;
  7. using Autodesk.Civil.DatabaseServices;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11.  
  12. namespace C3dTest
  13. {
  14.     public class TestCustomObjectViewerEvents
  15.     {
  16.         static RXClass _pipeClass = RXClass.GetClass(typeof(Pipe));
  17.         static bool _eventAdded = false;
  18.  
  19.         bool _processStarted = false;
  20.         ObjectId[] _pipeTmpObjIds;        
  21.         List<ObjectId> _allSelObjIds;        
  22.  
  23.         [CommandMethod("TestCustomObjectViewerRun")]
  24.         public void Run()
  25.         {
  26.             if (_eventAdded) return;
  27.             Application.DocumentManager.DocumentLockModeChanged
  28.                 += DocumentManager_DocumentLockModeChanged;
  29.             _eventAdded = true;
  30.         }
  31.  
  32.         [CommandMethod("TestCustomObjectViewerStop")]
  33.         public void Stop()
  34.         {
  35.             if (!_eventAdded) return;
  36.             Application.DocumentManager.DocumentLockModeChanged
  37.                 -= DocumentManager_DocumentLockModeChanged;
  38.             _eventAdded = false;
  39.         }
  40.  
  41.         void DocumentManager_DocumentLockModeChanged
  42.             (object sender, DocumentLockModeChangedEventArgs e)
  43.         {
  44.             if ((e.CurrentMode & DocumentLockMode.Write) == DocumentLockMode.Write
  45.                 && e.GlobalCommandName.ToUpper() == "AECOBJECTVIEWER"
  46.                 && (sender as DocumentCollection).MdiActiveDocument is Document adoc
  47.                 && _allSelObjIds == null
  48.                 && !_processStarted)
  49.             {
  50.                 PromptSelectionResult implSelRes = adoc.Editor.SelectImplied();
  51.                 if (implSelRes.Status == PromptStatus.OK)
  52.                 {
  53.                     _allSelObjIds = implSelRes.Value.GetObjectIds().ToList();
  54.                 }
  55.                 else
  56.                 {
  57.                     // Если Просмотр объектов запущен без предварительного выбора
  58.                     PromptSelectionOptions selOpts = new PromptSelectionOptions
  59.                     {
  60.                         AllowDuplicates = false,
  61.                         AllowSubSelections = false,
  62.                         RejectObjectsFromNonCurrentSpace = true,
  63.                         RejectPaperspaceViewport = true
  64.                     };
  65.                     // Предлагаем выбрать объекты
  66.                     PromptSelectionResult selRes = adoc.Editor.GetSelection(selOpts);
  67.                     // Если объекты выбраны
  68.                     if (selRes.Status == PromptStatus.OK)
  69.                     {
  70.                         // Запоминаем их для обработки
  71.                         _allSelObjIds = selRes.Value.GetObjectIds().ToList();
  72.                     }
  73.                     else
  74.                     {
  75.                         // Если не выбраны - прерываем запуск команды
  76.                         e.Veto();
  77.                         return;
  78.                     }
  79.                 }
  80.  
  81.                 // Ищем все трубы
  82.                 ObjectId[] selPipesIds = _allSelObjIds.Where
  83.                         (id => id.ObjectClass.Equals(_pipeClass)).ToArray();
  84.                 // Если нашли
  85.                 if (selPipesIds.Count() > 0)
  86.                 {
  87.                     // Удаляем трубы из списка
  88.                     _allSelObjIds.RemoveAll
  89.                         (item => selPipesIds.Contains(item));
  90.                     // Создаём объекты для подмены
  91.                     _pipeTmpObjIds = selPipesIds.Select
  92.                         (pipeId => GetPipeTmpObject(pipeId)).ToArray();
  93.                     // Добавляем объекты подмены в список
  94.                     _allSelObjIds.AddRange(_pipeTmpObjIds);
  95.                 }
  96.  
  97.                 adoc.CommandWillStart += Adoc_CommandWillStart;
  98.                 _processStarted = true;
  99.             }
  100.         }        
  101.  
  102.         void Adoc_CommandWillStart(object sender, CommandEventArgs e)
  103.         {
  104.             Document adoc = sender as Document;            
  105.  
  106.             if (e.GlobalCommandName.ToUpper() == "AECOBJECTVIEWER"
  107.                 && _allSelObjIds != null)
  108.             {
  109.                 adoc.CommandCancelled += Adoc_CommandEnded;
  110.                 adoc.CommandEnded += Adoc_CommandEnded;
  111.                 adoc.CommandFailed += Adoc_CommandEnded;
  112.  
  113.                 // Назначаем предвыбор
  114.                 adoc.Editor.SetImpliedSelection(_allSelObjIds.ToArray());
  115.                 _allSelObjIds = null;
  116.  
  117.                 adoc.CommandWillStart -= Adoc_CommandWillStart;
  118.             }
  119.         }        
  120.  
  121.         void Adoc_CommandEnded(object sender, CommandEventArgs e)
  122.         {
  123.             // Если есть временные объекты (завершена команда "Просмотр объектов")
  124.             if (_pipeTmpObjIds != null)
  125.             {
  126.                 // Удаляем их
  127.                 foreach (ObjectId id in _pipeTmpObjIds)
  128.                 {
  129. #pragma warning disable CS0618 // Type or member is obsolete
  130.                     using (var ent = id.Open(OpenMode.ForWrite))
  131. #pragma warning restore CS0618 // Type or member is obsolete
  132.                     {
  133.                         ent.Erase(true);
  134.                     }
  135.                 }
  136.                 _pipeTmpObjIds = null;              
  137.  
  138.                 Document adoc = sender as Document;
  139.  
  140.                 adoc.CommandCancelled -= Adoc_CommandEnded;
  141.                 adoc.CommandEnded -= Adoc_CommandEnded;
  142.                 adoc.CommandFailed -= Adoc_CommandEnded;
  143.  
  144.                 _processStarted = false;
  145.             }            
  146.         }
  147.  
  148.         static ObjectId GetPipeTmpObject(ObjectId pipeId)
  149.         {
  150.             Point3d start, end;
  151.             double innerRad, length;
  152. #pragma warning disable CS0618 // Type or member is obsolete
  153.             using (Pipe pipe = pipeId.Open(OpenMode.ForRead) as Pipe)
  154. #pragma warning restore CS0618 // Type or member is obsolete
  155.             {
  156.                 start = pipe.StartPoint;
  157.                 end = pipe.EndPoint;
  158.  
  159.                 innerRad = pipe.InnerDiameterOrWidth;
  160.                 length = pipe.Length3DCenterToCenter;
  161.             }
  162.  
  163.             Vector3d normal = start.GetVectorTo(end).GetNormal();
  164.             SweepOptionsBuilder optBuild = new SweepOptionsBuilder();
  165.             // Зададим угол скручивания
  166.             optBuild.TwistAngle = Math.PI * 0.5;
  167.             SweepOptions opts = optBuild.ToSweepOptions();
  168.             using (SweptSurface surf = new SweptSurface())
  169.             {
  170.                 using (Polyline pline = new Polyline(4))
  171.                 using (Line line = new Line(start, end))
  172.                 {
  173.                     pline.AddVertexAt(0, new Point2d(-innerRad, -innerRad), 1, 0, 0);
  174.                     pline.AddVertexAt(1, new Point2d(innerRad, -innerRad), 1, 0, 0);
  175.                     pline.AddVertexAt(2, new Point2d(innerRad, innerRad), 1, 0, 0);
  176.                     pline.AddVertexAt(3, new Point2d(-innerRad, innerRad), 1, 0, 0);
  177.                     pline.Closed = true;
  178.                     pline.Normal = normal;
  179.                     pline.TransformBy(Matrix3d.Displacement(start.GetAsVector()));
  180.                     surf.CreateSweptSurface(pline, line, opts);
  181.                 }
  182.                 // Сделаем трубы фиолетовыми
  183.                 surf.Color = Color.FromColorIndex(ColorMethod.ByAci, 6);
  184. #pragma warning disable CS0618 // Type or member is obsolete
  185.                 using (BlockTableRecord bRec = pipeId.Database
  186.                     .CurrentSpaceId.Open(OpenMode.ForWrite) as BlockTableRecord)
  187. #pragma warning restore CS0618 // Type or member is obsolete
  188.                 {
  189.                     return bRec.AppendEntity(surf);
  190.                 }
  191.             }
  192.         }
  193.     }
  194. }
  195.  
Второй вариант - использовать "подмену" стандартной команды. Обсуждалось тут: http://adn-cis.org/forum/index.php?topic=8311.0 и тут: http://adn-cis.org/forum/index.php?topic=8319.0
Основная идея та же - осуществить выбор объектов вне стандартной команды, заменить трубы на временные объекты и передать полученный набор в стандартную команду.
Здесь дополнительно понадобилось использование события Autodesk.AutoCAD.ApplicationServices.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.Internal;
  7. using Autodesk.AutoCAD.Runtime;
  8. using Autodesk.Civil.DatabaseServices;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.IO;
  12. using System.Linq;
  13. using Win32 = Microsoft.Win32;
  14.  
  15. namespace C3dTest
  16. {
  17.     public class TestCustomObjectViewerCmdSwitch : IExtensionApplication
  18.     {
  19.         static RXClass _pipeClass = RXClass.GetClass(typeof(Pipe));
  20.         List<ObjectId> _pipeTmpObjIds = new List<ObjectId>();
  21.  
  22.         public void Initialize()
  23.         {
  24.             // Preloading AecUtilities.arx
  25.             if (!Utils.IsCommandDefined("AecObjectViewer"))
  26.                 SystemObjects.DynamicLinker.LoadModule("AecUtilities.arx", false, false);
  27.             Utils.AddCommand("ACAD_MAIN", "AecObjectViewer",
  28.                 "ObjectViewer", CommandFlags.UsePickSet | CommandFlags.Redraw,
  29.                 MyObjectViewerHandler);
  30.         }
  31.  
  32.         public void Terminate()
  33.         {
  34.  
  35.         }
  36.        
  37.         public void MyObjectViewerHandler()
  38.         {
  39.             List<ObjectId> _allSelObjIds;
  40.  
  41.             if (!(Application.DocumentManager.MdiActiveDocument is Document adoc)) return;
  42.  
  43.             PromptSelectionResult implSelRes = adoc.Editor.SelectImplied();
  44.             if (implSelRes.Status == PromptStatus.OK)
  45.             {
  46.                 _allSelObjIds = implSelRes.Value.GetObjectIds().ToList();
  47.             }
  48.             else
  49.             {
  50.                 // Если Просмотр объектов запущен без предварительного выбора
  51.                 PromptSelectionOptions selOpts = new PromptSelectionOptions
  52.                 {
  53.                     AllowDuplicates = false,
  54.                     AllowSubSelections = false,
  55.                     RejectObjectsFromNonCurrentSpace = true,
  56.                     RejectPaperspaceViewport = true
  57.                 };
  58.                 // Предлагаем выбрать объекты
  59.                 PromptSelectionResult selRes = adoc.Editor.GetSelection(selOpts);
  60.                 // Если объекты выбраны
  61.                 if (selRes.Status == PromptStatus.OK)
  62.                 {
  63.                     // Запоминаем их для обработки
  64.                     _allSelObjIds = selRes.Value.GetObjectIds().ToList();
  65.                 }
  66.                 else
  67.                 {
  68.                     // Если не выбраны - прерываем запуск команды                  
  69.                     return;
  70.                 }
  71.             }
  72.  
  73.             // Ищем все трубы
  74.             ObjectId[] selPipesIds = _allSelObjIds.Where
  75.                     (id => id.ObjectClass.Equals(_pipeClass)).ToArray();
  76.             // Если нашли
  77.             if (selPipesIds.Count() > 0)
  78.             {
  79.                 // Удаляем трубы из списка
  80.                 _allSelObjIds.RemoveAll
  81.                     (item => selPipesIds.Contains(item));
  82.  
  83.                 // Создаём объекты для подмены
  84.                 ObjectId[] curTmpObjs = selPipesIds
  85.                     .Select(pipeId => GetPipeTmpObject(pipeId))
  86.                     .ToArray();                
  87.                 _pipeTmpObjIds.AddRange(curTmpObjs);
  88.  
  89.                 // Добавляем объекты подмены в список
  90.                 _allSelObjIds.AddRange(curTmpObjs);
  91.             }
  92.  
  93.             // Назначаем предвыбор
  94.             adoc.Editor.SetImpliedSelection(_allSelObjIds.ToArray());
  95.  
  96.             Application.Idle += Application_Idle;
  97.  
  98.             string groupName = GetCmdGroupName();
  99.  
  100.             if (groupName != null)
  101.             {              
  102.                 adoc.SendStringToExecute
  103.                         ($"_{groupName}.AecObjectViewer ", true, false, false);
  104.             }
  105.         }
  106.  
  107.         void Application_Idle(object sender, EventArgs e)
  108.         {
  109.             Application.Idle -= Application_Idle;
  110.  
  111.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  112.             if (adoc == null) return;
  113.  
  114.             using (DocumentLock docLock = adoc.LockDocument())
  115.             {
  116.                 // Если есть временные объекты (завершена команда "Просмотр объектов")
  117.                 if (_pipeTmpObjIds.Count > 0)
  118.                 {
  119.                     // Удаляем их
  120.                     foreach (ObjectId id in _pipeTmpObjIds)
  121.                     {
  122. #pragma warning disable CS0618 // Type or member is obsolete
  123.                         using (var ent = id.Open(OpenMode.ForWrite))
  124. #pragma warning restore CS0618 // Type or member is obsolete
  125.                         {
  126.                             ent.Erase(true);
  127.                         }
  128.                     }
  129.                     _pipeTmpObjIds.Clear();
  130.                 }
  131.             }
  132.         }
  133.  
  134.         /// <summary>
  135.         /// Получение названия группы команд для AecUtilites
  136.         /// </summary>
  137.         /// <example>
  138.         /// Например, для Civil 3D 2016. Раздел реестра:
  139.         /// HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\R20.1\ACAD-F000:419
  140.         /// \AEC\7.8\General\Tools\{E04B3676-CBB7-454c-9CDB-A55ACA81C41F}
  141.         /// Название параметра: AppName
  142.         /// Значение параметра: AecUtilities70
  143.         /// Метод вернёт строку: "AecUtilities70"
  144.         /// </example>  
  145.         /// <returns></returns>
  146.         static string GetCmdGroupName()
  147.         {
  148.             // Открываем раздел
  149.             // HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\RXX.X\ACAD-XXXX:419\AEC
  150.             Win32.RegistryKey aecRoot = Win32.Registry.CurrentUser.OpenSubKey
  151.                 (Path.Combine(HostApplicationServices.Current.UserRegistryProductRootKey, "AEC"));
  152.  
  153.             if (aecRoot != null)
  154.             {
  155.                 // В этом разделе внутри разделы с названиями в виде номера версии,
  156.                 // в одном из них есть вложенный раздел General. Ищем его.
  157.                 string[] aecSubKeyNames = aecRoot.GetSubKeyNames();
  158.                 foreach (string aecSubKeyName in aecSubKeyNames)
  159.                 {
  160.                     Win32.RegistryKey aecSubKey = aecRoot.OpenSubKey(aecSubKeyName);
  161.                     // Если нашли
  162.                     if (aecSubKey.GetSubKeyNames().Contains("General"))
  163.                     {
  164.                         // Получаем раздел
  165.                         // HKEY_CURRENT_USER\Software\Autodesk\AutoCAD\
  166.                         // RXX.X\ACAD-XXXX:419\AEC\X.X\General\Tools
  167.                         Win32.RegistryKey tools
  168.                             = aecSubKey.OpenSubKey(Path.Combine("General", "Tools"));
  169.                         // В нём - только один раздел с названием в виде GUID.
  170.                         // Получаем его
  171.                         string guidKeyName = tools.GetSubKeyNames()[0];
  172.                         Win32.RegistryKey guidKey = tools.OpenSubKey(guidKeyName);
  173.  
  174.                         // Читаем значение AppName
  175.                         return guidKey.GetValue("AppName").ToString();
  176.                     }
  177.                 }
  178.             }
  179.             return null;
  180.         }
  181.  
  182.         static ObjectId GetPipeTmpObject(ObjectId pipeId)
  183.         {
  184.             Point3d start, end;
  185.             double innerRad;
  186. #pragma warning disable CS0618 // Type or member is obsolete
  187.             using (Pipe pipe = pipeId.Open(OpenMode.ForRead) as Pipe)
  188. #pragma warning restore CS0618 // Type or member is obsolete
  189.             {
  190.                 start = pipe.StartPoint;
  191.                 end = pipe.EndPoint;
  192.  
  193.                 innerRad = pipe.InnerDiameterOrWidth * 4;
  194.             }
  195.  
  196.             Vector3d normal = start.GetVectorTo(end).GetNormal();
  197.             SweepOptionsBuilder optBuild = new SweepOptionsBuilder();
  198.             optBuild.TwistAngle = Math.PI;
  199.             SweepOptions opts = optBuild.ToSweepOptions();
  200.             using (SweptSurface surf = new SweptSurface())
  201.             {
  202.                 using (Polyline pline = new Polyline(4))
  203.                 using (Line line = new Line(start, end))
  204.                 {
  205.                     pline.AddVertexAt(0, new Point2d(-innerRad, -innerRad), 0, 0, 0);
  206.                     pline.AddVertexAt(1, new Point2d(innerRad, -innerRad), 0, 0, 0);
  207.                     pline.AddVertexAt(2, new Point2d(innerRad, innerRad), 0, 0, 0);
  208.                     pline.AddVertexAt(3, new Point2d(-innerRad, innerRad), 0, 0, 0);
  209.                     pline.Closed = true;
  210.                     pline.Normal = normal;
  211.                     pline.TransformBy(Matrix3d.Displacement(start.GetAsVector()));
  212.                     surf.CreateSweptSurface(pline, line, opts);
  213.                 }
  214.                 // Сделаем трубы фиолетовыми
  215.                 surf.Color = Color.FromColorIndex(ColorMethod.ByAci, 6);
  216. #pragma warning disable CS0618 // Type or member is obsolete
  217.                 using (BlockTableRecord bRec
  218.                     = pipeId.Database.CurrentSpaceId.Open(OpenMode.ForWrite) as BlockTableRecord)
  219. #pragma warning restore CS0618 // Type or member is obsolete
  220.                 {
  221.                     return bRec.AppendEntity(surf);
  222.                 }
  223.             }
  224.         }
  225.     }
  226. }
  227.