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

ADN Club => AutoCAD .NET API => Тема начата: Windcastle от 23-08-2015, 19:34:03

Название: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Windcastle от 23-08-2015, 19:34:03
Задача: на чистом NET API AutoCAD написать код для определения размеров динамического блока (файл прилагается).

1. Имеется чертеж с динамическим блоком внутри.
2. Требуется определить его габаритные размеры с помощью AutoCAD NET и вывести с помощью MessageBox на экран.

Файл прикрепляю!

1. Раньше бы я решил этот вопрос с помощью метода GetBoundingBox, предварительно создав взорванную копию данного блока и пропарсил бы все объекты, входящие в копию! Но с учетом того, что от меня требуют именно NET код, то прошу помочь разобраться с чего начать:
     - Какие библиотеки подключить?
     - Что делать дальше?
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 24-08-2015, 11:17:08
В NET можно (и проще всего, и правильнее, на мой взгляд) сделать то же самое. Берем блок, взрываем, определяем габариты осколков и по ним определяем общий габарит. По поводу остального -  материалов по этим вопросам в интернете много.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Пашин Евгений от 24-08-2015, 11:19:20
Я так уже делал через COM объект с помощью GetBoundingBox, предварительно взрывая блоки Object.Explode и перебирая их!

Но мне сказали, что это не правильно!

А нужно правильно!
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 24-08-2015, 11:24:17
Мне лично почти все равно кто там и что сказал  8)
Я к такому выводу пришел самостоятельно, путем довольно долгих поисков и натыканий на различного рода грабли. Если у кого-то есть другое обоснованное мнение на этот счет - с удовольствием выслушаю.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: German от 24-08-2015, 11:31:09
Это динамический блок. В нем есть параметры, которые управляют геометрией блока. Их можно считать. Код есть и на этом форуме.
Только нужно знать, что это за параметры. Для этого нужно "поковыряться" в блоке.

Увидел, что образец прикреплен. Однозначно, взрывать!
Off-Topic: показать
А еще лучше сделать свой динамический блок, отвечающий за рамку печати.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Пашин Евгений от 24-08-2015, 11:36:35
Дмитрий Загорулькин, спасибо за ответ! Мне Ваше решение тоже кажется простым и изящным, но мне скоро по рукам будут бить... мне нужна помощь в освоении именно NET технологии.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Doublefish от 24-08-2015, 12:11:32
Либо иметь в блоке в параметрах операций два параметра - высоту и ширину и соответственно считывать свойства этих параметров из блока (т.е. не через видимость переключение, а через параметры).
Я правда не совсем понял зачем в блоке под каждую видимость перечертивать объекты вместо того чтобы использовать параметры где и указать размеры.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Пашин Евгений от 24-08-2015, 12:26:45
Я не задаюсь таким вопросом - это стандарт предприятия.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Андрей Бушман от 24-08-2015, 12:33:14
это стандарт предприятия.
?
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 24-08-2015, 13:40:58
Но мне сказали, что это не правильно!
Это намёк на меня? Тогда ты не понял о чём я писал. В AutoCAD его AutoCAD .NET API значительно мощнее и гибче, чем его же COM/ActiveX. Поэтому раз ты уже ушел от VBA в VB.NET (хотя я бы рекомендовал C#), то есть возможность (не обязанность) использовать более мощные средства.
Пример, разницы между AutoCAD .NET API и COM/ActiveX. В COM метод Explode для примитива (в частности вставки блока) создаёт кучу новых примитивов и сразу добавляет их в чертеж. Поэтому после их обработки тебе придётся их удалить, а это кроме того что займёт лишнее время, так еще и приведёт к утечке памяти. В AutoCAD .NET API метод Explode не добавляет получающиеся примитивы в чертеж (они только в оперативной памяти) и их не нужно удалять из чертежа - только использовать метод Dispose() для очистки памяти. Это один из простейших примеров.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 24-08-2015, 13:45:20
Если у кого-то есть другое обоснованное мнение на этот счет - с удовольствием выслушаю.
Если у вставки блока различные масштабные коэффициенты по осям X, Y, Z - метод Explode в большинстве случаев не сработает.
А ведь здесь задача значительно проще. Необходимо отобрать только видимые примитивы, получить их Extents, получить крайние точки и выполнить преобразование по матрице BlockTransform. Вроде бы ничего не забыл.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Пашин Евгений от 24-08-2015, 13:52:19
Это намёк на меня?

А Вы тут ни при чем! Хотя Ваши слова и слова моего начальника похожи.

В AutoCAD его AutoCAD .NET API значительно мощнее и гибче, чем его же COM/ActiveX

Да-к покажите как это сделать.

Если у вставки блока различные масштабные коэффициенты по осям X, Y, Z

Это ж кому придет в голову еще и коэффициенты  менять?



Разница VBA от NET AutoCAD! Простыми словами сможете объяснить? Или опять меня куда-нибудь пошлете, чего-нибудь почитать ))))
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 24-08-2015, 14:49:27
Если у вставки блока различные масштабные коэффициенты по осям X, Y, Z - метод Explode в большинстве случаев не сработает.
Надо перепроверить, но вроде бы, если память не изменяет, работает хорошо и при масштабах разных и при повороте блока и пр.
Необходимо отобрать только видимые примитивы, получить их Extents, получить крайние точки и выполнить преобразование по матрице BlockTransform.
И получите не те границы, которые нужны, если блок повернут. Это мы уже проходили.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 24-08-2015, 15:26:45
Да-к покажите как это сделать.
Смотри. Только с C# на VB.NET перепишешь сам. Для этого можешь воспользоваться одним из конвертеров: http://www.developerfusion.com/tools/convert/csharp-to-vb/
Надо перепроверить, но вроде бы, если память не изменяет, работает хорошо и при масштабах разных и при повороте блока и пр.
Проверь. Очень удивишься. Особенно если в блоке присутствуют дуги и/или полилинии с дугами.

Пока простейший код без обработки ошибок. Кстати вроде и с повернутыми блоками работает:
Код - C# [Выбрать]
  1. using System;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.EditorInput;
  7.  
  8. #pragma warning disable 618
  9.  
  10. [assembly: CommandClass(typeof(Rivilis.BlockExtents))]
  11.  
  12. namespace Rivilis
  13. {
  14.   public class BlockExtents
  15.   {
  16.     [CommandMethod("BlockExt")]
  17.     public void BlockExt()
  18.     {
  19.       Document doc = Application.DocumentManager.MdiActiveDocument;
  20.       if (doc == null) return;
  21.       Editor ed = doc.Editor;
  22.       PromptEntityOptions enOpt =
  23.         new PromptEntityOptions("\nВыберите вставку блока:");
  24.       enOpt.SetRejectMessage("Это не вставка блока");
  25.       enOpt.AddAllowedClass(typeof(BlockReference), true);
  26.       PromptEntityResult enRes = ed.GetEntity(enOpt);
  27.       if (enRes.Status == PromptStatus.OK)
  28.       {
  29.         Extents3d blockExt = new Extents3d(Point3d.Origin, Point3d.Origin);
  30.         using (BlockReference bref = enRes.ObjectId.Open(OpenMode.ForRead) as BlockReference)
  31.         {
  32.           Matrix3d mat = bref.BlockTransform;
  33.           using (BlockTableRecord btr = bref.BlockTableRecord.Open(OpenMode.ForRead) as BlockTableRecord)
  34.           {
  35.             foreach (ObjectId id in btr)
  36.             {
  37.               using (DBObject obj = id.Open(OpenMode.ForRead) as DBObject)
  38.               {
  39.                 Entity en = obj as Entity;
  40.                 if (en != null && en.Visible == true)
  41.                 {
  42.                   Extents3d ext = en.GeometricExtents;
  43.                   ext.TransformBy(mat);
  44.                   if (blockExt.MinPoint == Point3d.Origin && blockExt.MaxPoint == Point3d.Origin)
  45.                     blockExt = ext;
  46.                   else
  47.                     blockExt.AddExtents(ext);
  48.                 }
  49.               }
  50.             }
  51.           }
  52.         }
  53.         string s =
  54.           "MinPoint: " + blockExt.MinPoint.ToString() + " " +
  55.           "MaxPoint: " + blockExt.MaxPoint.ToString();
  56.         ed.WriteMessage(s);
  57.       }
  58.     }
  59.   }
  60. }
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 24-08-2015, 15:49:30
Решил сам воспользоваться конвертером. Практически ничего не пришлось править:

Код - vb.net [Выбрать]
  1. Imports Autodesk.AutoCAD.Runtime
  2. Imports Autodesk.AutoCAD.ApplicationServices
  3. Imports Autodesk.AutoCAD.DatabaseServices
  4. Imports Autodesk.AutoCAD.Geometry
  5. Imports Autodesk.AutoCAD.EditorInput
  6.  
  7. <Assembly: CommandClass(GetType(Rivilis.BlockExtents))>
  8.  
  9. Namespace Rivilis
  10.     Public Class BlockExtents
  11.         <CommandMethod("BlockExt")> _
  12.         Public Sub BlockExt()
  13.             Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  14.             If doc Is Nothing Then
  15.                 Return
  16.             End If
  17.             Dim ed As Editor = doc.Editor
  18.             Dim enOpt As New PromptEntityOptions(vbLf & "Выберите вставку блока:")
  19.             enOpt.SetRejectMessage("Это не вставка блока")
  20.             enOpt.AddAllowedClass(GetType(BlockReference), True)
  21.             Dim enRes As PromptEntityResult = ed.GetEntity(enOpt)
  22.             If enRes.Status = PromptStatus.OK Then
  23.                 Dim blockExt__1 As New Extents3d(Point3d.Origin, Point3d.Origin)
  24.                 Using bref As BlockReference = _
  25.                       TryCast(enRes.ObjectId.Open(OpenMode.ForRead), BlockReference)
  26.                     Dim mat As Matrix3d = bref.BlockTransform
  27.                     Using btr As BlockTableRecord = _
  28.                           TryCast(bref.BlockTableRecord.Open(OpenMode.ForRead), BlockTableRecord)
  29.                         For Each id As ObjectId In btr
  30.                             Using obj As DBObject = TryCast(id.Open(OpenMode.ForRead), DBObject)
  31.                                 Dim en As Entity = TryCast(obj, Entity)
  32.                                 If en IsNot Nothing AndAlso en.Visible = True Then
  33.                                     Dim ext As Extents3d = en.GeometricExtents
  34.                                     ext.TransformBy(mat)
  35.                                     If blockExt__1.MinPoint = Point3d.Origin _
  36.                                           AndAlso blockExt__1.MaxPoint = Point3d.Origin Then
  37.                                         blockExt__1 = ext
  38.                                     Else
  39.                                         blockExt__1.AddExtents(ext)
  40.                                     End If
  41.                                 End If
  42.                             End Using
  43.                         Next
  44.                     End Using
  45.                 End Using
  46.                 Dim s As String = "MinPoint: " & blockExt__1.MinPoint.ToString() & " " & _
  47.                     "MaxPoint: " & blockExt__1.MaxPoint.ToString()
  48.                 ed.WriteMessage(s)
  49.             End If
  50.         End Sub
  51.     End Class
  52. End Namespace
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 24-08-2015, 16:10:16
Самый простой пример, который пришел в голову, чтобы показать, почему этот способ неточен. Нарисуйте окружность, сделайте из нее блок, поверните этот блок на 45 градусов и попробуйте определить границы.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 24-08-2015, 17:09:08
Да. В этом случае работать будет не точно. А вот с рамками должно быть нормально при любом повороте и масштабировании, так как сама рамка совпадает с габаритным контейнером и поворачивается и масштабируется одновременно с ним.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Windcastle от 24-08-2015, 18:56:30
Надо попробовать! Отпишусь как сделаю.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 24-08-2015, 22:15:52
Немного модифицировал код. Теперь если блок имеет одинаковые коэффициенты масштабирования по X, Y, Z, то используется один алгоритм (более точный), если разные - другой. Ну и добавлена рекурсия (т.е. обрабатывается вставка блока в другом блоке):
Код - C# [Выбрать]
  1. using System;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.EditorInput;
  7.  
  8. #pragma warning disable 618
  9.  
  10. [assembly: CommandClass(typeof(Rivilis.BlockExtents))]
  11.  
  12. namespace Rivilis
  13. {
  14.   public class BlockExtents
  15.   {
  16.     [CommandMethod("BlockExt")]
  17.     public void BlockExt()
  18.     {
  19.       Document doc = Application.DocumentManager.MdiActiveDocument;
  20.       if (doc == null) return;
  21.       Editor ed = doc.Editor;
  22.       PromptEntityOptions enOpt = new PromptEntityOptions("\nВыберите примитив: ");
  23.       PromptEntityResult enRes = ed.GetEntity(enOpt);
  24.       if (enRes.Status == PromptStatus.OK)
  25.       {
  26.         Extents3d blockExt = new Extents3d(Point3d.Origin, Point3d.Origin);
  27.         Matrix3d mat = Matrix3d.Identity;
  28.         using (Entity en = enRes.ObjectId.Open(OpenMode.ForRead) as Entity)
  29.         {
  30.           GetBlockExtents(en, ref blockExt, ref mat);
  31.         }
  32.         string s =
  33.           "MinPoint: " + blockExt.MinPoint.ToString() + " " +
  34.           "MaxPoint: " + blockExt.MaxPoint.ToString();
  35.         ed.WriteMessage(s);
  36.         //------------------------------------------------------------
  37.         // Только для тестирования полученного габаритного контейнера
  38.         //------------------------------------------------------------
  39.         #region TestinExts
  40.         using (BlockTableRecord curSpace =
  41.           doc.Database.CurrentSpaceId.Open(OpenMode.ForWrite) as BlockTableRecord)
  42.         {
  43.           Point3dCollection pts = new Point3dCollection();
  44.           pts.Add(blockExt.MinPoint);
  45.           pts.Add(new Point3d(blockExt.MinPoint.X,blockExt.MaxPoint.Y, blockExt.MinPoint.Z));
  46.           pts.Add(blockExt.MaxPoint);
  47.           pts.Add(new Point3d(blockExt.MaxPoint.X,blockExt.MinPoint.Y, blockExt.MinPoint.Z));
  48.           using (Polyline3d poly = new Polyline3d(Poly3dType.SimplePoly,pts,true))
  49.           {
  50.             curSpace.AppendEntity(poly);
  51.           }
  52.         }
  53.         #endregion
  54.       }
  55.     }
  56.     /// <summary>
  57.     /// Рекурсивное получение габаритного контейнера для вставки блока.
  58.     /// </summary>
  59.     /// <param name="en">Имя примитива</param>
  60.     /// <param name="ext">Габаритный контейнер</param>
  61.     /// <param name="mat">Матрица преобразования из системы координат блока в МСК.</param>
  62.     void GetBlockExtents(Entity en, ref Extents3d ext, ref Matrix3d mat)
  63.     {
  64.       if (!IsLayerOn(en.LayerId))
  65.         return;
  66.       if (en is BlockReference)
  67.       {
  68.         BlockReference bref = en as BlockReference;
  69.         Matrix3d matIns = mat * bref.BlockTransform;
  70.         using (BlockTableRecord btr =
  71.           bref.BlockTableRecord.Open(OpenMode.ForRead) as BlockTableRecord)
  72.         {
  73.           foreach (ObjectId id in btr)
  74.           {
  75.             using (DBObject obj = id.Open(OpenMode.ForRead) as DBObject)
  76.             {
  77.               Entity enCur = obj as Entity;
  78.               if (enCur == null || enCur.Visible != true)
  79.                 continue;
  80.               GetBlockExtents(enCur, ref ext, ref matIns);
  81.             }
  82.           }
  83.         }
  84.       }
  85.       else
  86.       {
  87.         if (mat.IsUniscaledOrtho())
  88.         {
  89.           using (Entity enTr = en.GetTransformedCopy(mat))
  90.           {
  91.             if (IsEmptyExt(ref ext))
  92.               ext = enTr.GeometricExtents;
  93.             else
  94.               ext.AddExtents(enTr.GeometricExtents);
  95.             return;
  96.           }
  97.         }
  98.         else
  99.         {
  100.           try
  101.           {
  102.             Extents3d curExt = en.GeometricExtents;
  103.             curExt.TransformBy(mat);
  104.             if (IsEmptyExt(ref ext))
  105.               ext = curExt;
  106.             else
  107.               ext.AddExtents(curExt);
  108.           }
  109.           catch { }
  110.           return;
  111.         }
  112.       }
  113.  
  114.       return;
  115.  
  116.     }
  117.     /// <summary>
  118.     /// Определяет видны ли объекты на слое, указанном его ObjectId.
  119.     /// </summary>
  120.     /// <param name="layerId">ObjectId слоя.</param>
  121.     /// <returns></returns>
  122.     bool IsLayerOn(ObjectId layerId)
  123.     {
  124.       using (LayerTableRecord ltr = layerId.Open(OpenMode.ForRead) as LayerTableRecord)
  125.       {
  126.         if (ltr.IsFrozen) return false;
  127.         if (ltr.IsOff) return false;
  128.       }
  129.       return true;
  130.     }
  131.     /// <summary>
  132.     /// Определят не пустой ли габаритный контейнер.
  133.     /// </summary>
  134.     /// <param name="ext">Габаритный контейнер.</param>
  135.     /// <returns></returns>
  136.     bool IsEmptyExt(ref Extents3d ext)
  137.     {
  138.       if (ext.MinPoint.DistanceTo(ext.MaxPoint) < Tolerance.Global.EqualPoint)
  139.         return true;
  140.       else
  141.         return false;
  142.     }
  143.   }
  144. }

Перевод в VB.NET сделаешь самостоятельно.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Windcastle от 25-08-2015, 05:30:27
Спасибо! Сделаю обязательно. Почему нельзя добавлять карму за каждый ответ? Ведь это же другой ответ!
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 26-08-2015, 10:22:03
Так круто, что я даже не понимаю, как это работает :) Надо будет потестировать на досуге... А с атрибутами пробовали, проблем нет? Это одни из тех граблей, что я не смог побороть аналитическими методами.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 26-08-2015, 11:40:12
А с атрибутами пробовали, проблем нет? Это одни из тех граблей, что я не смог побороть аналитическими методами.
Не пробовал. Точнее у меня было несколько блоков с атрибутами и с ними всё нормально. Но в крайнем случае и их обработаем отдельно.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 02-10-2015, 16:58:22
Дошли наконец руки до этого кода. С атрибутами, как я и предполагал, не дружит. На 94 строке выдает исключение.
Еще один момент заметил. Если на краю блока полилиния с толщиной, то в рамку попадает только половина толщины. Мелочь, но неприятно... Причем, это не недостаток Вашего кода, это в принципе методами не учитывается при определении границ полилинии. Надо будет мне подумать, насколько это критично и как это можно учитывать...
P.S. посмотрел, как я решал проблему с атрибутами. Гениально, на мой взгляд :D (строки 24-27, исходное комментирование сохранено):
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Получение границ блока
  3. /// </summary>
  4. /// <param name="blkRef">Ссылка на вставку блока</param>
  5. /// <returns>Границы блока</returns>
  6. public static Extents3d GetBlockGeometricExtents(this BlockReference blkRef)
  7. {
  8.     if (blkRef.IsDynamicBlock)
  9.     {
  10.         Extents3d resExts = new Extents3d();
  11.  
  12.         DBObjectCollection explObjs = new DBObjectCollection();
  13.         blkRef.Explode(explObjs);
  14.         foreach (DBObject dbObj in explObjs)
  15.         {
  16.             using (dbObj)
  17.             {
  18.                 try
  19.                 {
  20.                     Entity dbEnt = dbObj as Entity;
  21.  
  22.                     Extents3d entExt;
  23.                     if (
  24.                         // С атрибутами все непросто
  25.                         !(dbEnt is AttributeDefinition)                                
  26.                         // С размерами тоже
  27.                         && !(dbEnt is Dimension))
  28.                     {
  29.                         if (dbEnt is BlockReference)
  30.                             entExt = GetBlockGeometricExtents(dbEnt as BlockReference);
  31.  
  32.                         else entExt = dbEnt.GeometricExtents;
  33.  
  34.                         resExts.AddExtents(entExt);
  35.                     }
  36.                     else
  37.                         Debug.WriteLine
  38.                             ("GetBlockGeometricExtents subentity ignored: {0}",
  39.                             dbEnt.Handle.Value);
  40.                 }
  41.                 catch
  42.                 {
  43.                     Debug.WriteLine
  44.                         ("GetBlockGeometricExtents error! Block: {0}, subentity: {1}",
  45.                         blkRef.Handle.Value,
  46.                         dbObj.Handle.Value);
  47.                 }
  48.             }
  49.         }                
  50.         return resExts;
  51.     }
  52.     else return blkRef.GeometricExtents;
  53. }
  54.  
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 02-10-2015, 18:09:21
Обработку определений атрибутов и атрибуты я действительно пропустил. Исправляюсь. Определения атрибутов нужно пропускать все кроме видимых константных. Вставки атрибутов следует обрабатывать все, кроме невидимых. Поэтому как-то так:
Код - C# [Выбрать]
  1. using System;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.EditorInput;
  7.  
  8. #pragma warning disable 618
  9.  
  10. [assembly: CommandClass(typeof(Rivilis.BlockExtents))]
  11.  
  12. namespace Rivilis
  13. {
  14.   public class BlockExtents
  15.   {
  16.     [CommandMethod("BlockExt")]
  17.     public void BlockExt()
  18.     {
  19.       Document doc = Application.DocumentManager.MdiActiveDocument;
  20.       if (doc == null) return;
  21.       Editor ed = doc.Editor;
  22.       PromptEntityOptions enOpt =
  23.         new PromptEntityOptions("\nВыберите примитив: ");
  24.       PromptEntityResult enRes = ed.GetEntity(enOpt);
  25.       if (enRes.Status == PromptStatus.OK) {
  26.         Extents3d blockExt =
  27.           new Extents3d(Point3d.Origin, Point3d.Origin);
  28.         Matrix3d mat = Matrix3d.Identity;
  29.         using (Entity en =
  30.           enRes.ObjectId.Open(OpenMode.ForRead) as Entity) {
  31.           GetBlockExtents(en, ref blockExt, ref mat);
  32.         }
  33.         string s =
  34.           "MinPoint: " + blockExt.MinPoint.ToString() + " " +
  35.           "MaxPoint: " + blockExt.MaxPoint.ToString();
  36.         ed.WriteMessage(s);
  37.         //------------------------------------------------------------
  38.         // Только для тестирования полученного габаритного контейнера
  39.         //------------------------------------------------------------
  40.         #region TestinExts
  41.         using (BlockTableRecord curSpace =
  42.           doc.Database.CurrentSpaceId.Open(OpenMode.ForWrite) as BlockTableRecord) {
  43.           Point3dCollection pts = new Point3dCollection();
  44.           pts.Add(blockExt.MinPoint);
  45.           pts.Add(new Point3d(blockExt.MinPoint.X,
  46.             blockExt.MaxPoint.Y, blockExt.MinPoint.Z));
  47.           pts.Add(blockExt.MaxPoint);
  48.           pts.Add(new Point3d(blockExt.MaxPoint.X,
  49.             blockExt.MinPoint.Y, blockExt.MinPoint.Z));
  50.           using (Polyline3d poly =
  51.             new Polyline3d(Poly3dType.SimplePoly, pts, true)) {
  52.             curSpace.AppendEntity(poly);
  53.           }
  54.         }
  55.         #endregion
  56.       }
  57.     }
  58.     /// <summary>
  59.     /// Рекурсивное получение габаритного контейнера для вставки блока.
  60.     /// </summary>
  61.     /// <param name="en">Имя примитива</param>
  62.     /// <param name="ext">Габаритный контейнер</param>
  63.     /// <param name="mat">Матрица преобразования из системы координат блока в МСК.</param>
  64.     void GetBlockExtents(Entity en, ref Extents3d ext, ref Matrix3d mat)
  65.     {
  66.       if (!IsLayerOn(en.LayerId))
  67.         return;
  68.       if (en is BlockReference) {
  69.         BlockReference bref = en as BlockReference;
  70.         Matrix3d matIns = mat * bref.BlockTransform;
  71.         using (BlockTableRecord btr =
  72.           bref.BlockTableRecord.Open(OpenMode.ForRead) as BlockTableRecord) {
  73.           foreach (ObjectId id in btr) {
  74.             using (DBObject obj = id.Open(OpenMode.ForRead) as DBObject) {
  75.               Entity enCur = obj as Entity;
  76.               if (enCur == null || enCur.Visible != true)
  77.                 continue;
  78.               // Пропускаем неконстантные и невидимые определения атрибутов
  79.               AttributeDefinition attDef = enCur as AttributeDefinition;
  80.               if (attDef != null && (!attDef.Constant || attDef.Invisible))
  81.                 continue;
  82.               GetBlockExtents(enCur, ref ext, ref matIns);
  83.             }
  84.           }
  85.         }
  86.         // Отдельно обрабатываем атрибуты блока
  87.         if (bref.AttributeCollection.Count > 0) {
  88.           foreach (ObjectId idAtt in bref.AttributeCollection) {
  89.             using (AttributeReference attRef =
  90.               idAtt.Open(OpenMode.ForRead) as AttributeReference) {
  91.                 if (!attRef.Invisible && attRef.Visible)
  92.                 GetBlockExtents(attRef, ref ext, ref mat);
  93.             }
  94.           }
  95.         }
  96.       }
  97.       else {
  98.         if (mat.IsUniscaledOrtho()) {
  99.           using (Entity enTr = en.GetTransformedCopy(mat)) {
  100.             if (IsEmptyExt(ref ext)) {
  101.               try { ext = enTr.GeometricExtents; } catch { };
  102.             }
  103.             else {
  104.               try { ext.AddExtents(enTr.GeometricExtents); } catch { };
  105.             }
  106.             return;
  107.           }
  108.         }
  109.         else {
  110.           try {
  111.             Extents3d curExt = en.GeometricExtents;
  112.             curExt.TransformBy(mat);
  113.             if (IsEmptyExt(ref ext))
  114.               ext = curExt;
  115.             else
  116.               ext.AddExtents(curExt);
  117.           }
  118.           catch { }
  119.           return;
  120.         }
  121.       }
  122.  
  123.       return;
  124.  
  125.     }
  126.     /// <summary>
  127.     /// Определяет видны ли объекты на слое, указанном его ObjectId.
  128.     /// </summary>
  129.     /// <param name="layerId">ObjectId слоя.</param>
  130.     /// <returns></returns>
  131.     bool IsLayerOn(ObjectId layerId)
  132.     {
  133.       using (LayerTableRecord ltr = layerId.Open(OpenMode.ForRead) as LayerTableRecord) {
  134.         if (ltr.IsFrozen) return false;
  135.         if (ltr.IsOff) return false;
  136.       }
  137.       return true;
  138.     }
  139.     /// <summary>
  140.     /// Определят не пустой ли габаритный контейнер.
  141.     /// </summary>
  142.     /// <param name="ext">Габаритный контейнер.</param>
  143.     /// <returns></returns>
  144.     bool IsEmptyExt(ref Extents3d ext)
  145.     {
  146.       if (ext.MinPoint.DistanceTo(ext.MaxPoint) < Tolerance.Global.EqualPoint)
  147.         return true;
  148.       else
  149.         return false;
  150.     }
  151.   }
  152. }
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 05-10-2015, 14:56:33
Интересно получается, если в блоке есть размер. Его границы не пересчитываются в координаты модели и вычисляются для объекта внутри блока.
(https://adn-cis.org/forum/proxy.php?request=http%3A%2F%2Fs4.postimg.org%2Fgjby1n3i1%2Fblockext_dim.jpg&hash=0a0bc42e47c68cd5da71e8e75f2e9f3c) (http://postimg.org/image/gjby1n3i1/)
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 05-10-2015, 17:31:28
Его границы не пересчитываются в координаты модели и вычисляются для объекта внутри блока.
На досуге посмотрю что с ним не так.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 05-10-2015, 17:35:53
Я вроде откопал решение. Нужно для размеров после получения их трансформированной копии выполнять метод Dimension.RecomputeDimensionBlock(true). Если это добавить - границы вычисляются правильно. Сейчас тестирую, пока вроде все ок.
А зачем Вы матрицу с ref передаете в метод? Для увеличения скорости?
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 05-10-2015, 17:45:37
А зачем Вы матрицу с ref передаете в метод? Для увеличения скорости?
Да. Хотя была ещё одна мысль, что в каких-то ситуациях эту матрицу придётся модифицировать. Но пока не понадобилось.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 05-10-2015, 18:40:15
Дмитрий Загорулькин
По твоей наводке исправил код. Заодно на всякий случай обработал не только Dimension, но и Table:
Код - C# [Выбрать]
  1. using System;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.EditorInput;
  7.  
  8. #pragma warning disable 618
  9.  
  10. [assembly: CommandClass(typeof(Rivilis.BlockExtents))]
  11.  
  12. namespace Rivilis
  13. {
  14.   public class BlockExtents
  15.   {
  16.     [CommandMethod("BlockExt")]
  17.     public void BlockExt()
  18.     {
  19.       Document doc = Application.DocumentManager.MdiActiveDocument;
  20.       if (doc == null) return;
  21.       Editor ed = doc.Editor;
  22.       PromptEntityOptions enOpt =
  23.         new PromptEntityOptions("\nВыберите примитив: ");
  24.       PromptEntityResult enRes = ed.GetEntity(enOpt);
  25.       if (enRes.Status == PromptStatus.OK) {
  26.         Extents3d blockExt =
  27.           new Extents3d(Point3d.Origin, Point3d.Origin);
  28.         Matrix3d mat = Matrix3d.Identity;
  29.         using (Entity en =
  30.           enRes.ObjectId.Open(OpenMode.ForRead) as Entity) {
  31.           GetBlockExtents(en, ref blockExt, ref mat);
  32.         }
  33.         string s =
  34.           "MinPoint: " + blockExt.MinPoint.ToString() + " " +
  35.           "MaxPoint: " + blockExt.MaxPoint.ToString();
  36.         ed.WriteMessage(s);
  37.         //------------------------------------------------------------
  38.         // Только для тестирования полученного габаритного контейнера
  39.         //------------------------------------------------------------
  40.         #region TestinExts
  41.         using (BlockTableRecord curSpace =
  42.           doc.Database.CurrentSpaceId.Open(OpenMode.ForWrite) as BlockTableRecord) {
  43.           Point3dCollection pts = new Point3dCollection();
  44.           pts.Add(blockExt.MinPoint);
  45.           pts.Add(new Point3d(blockExt.MinPoint.X,
  46.             blockExt.MaxPoint.Y, blockExt.MinPoint.Z));
  47.           pts.Add(blockExt.MaxPoint);
  48.           pts.Add(new Point3d(blockExt.MaxPoint.X,
  49.             blockExt.MinPoint.Y, blockExt.MinPoint.Z));
  50.           using (Polyline3d poly =
  51.             new Polyline3d(Poly3dType.SimplePoly, pts, true)) {
  52.             curSpace.AppendEntity(poly);
  53.           }
  54.         }
  55.         #endregion
  56.       }
  57.     }
  58.     /// <summary>
  59.     /// Рекурсивное получение габаритного контейнера для выбранного примитива.
  60.     /// </summary>
  61.     /// <param name="en">Имя примитива</param>
  62.     /// <param name="ext">Габаритный контейнер</param>
  63.     /// <param name="mat">Матрица преобразования из системы координат блока в МСК.</param>
  64.     void GetBlockExtents(Entity en, ref Extents3d ext, ref Matrix3d mat)
  65.     {
  66.       if (!IsLayerOn(en.LayerId))
  67.         return;
  68.       if (en is BlockReference) {
  69.         BlockReference bref = en as BlockReference;
  70.         Matrix3d matIns = mat * bref.BlockTransform;
  71.         using (BlockTableRecord btr =
  72.           bref.BlockTableRecord.Open(OpenMode.ForRead) as BlockTableRecord) {
  73.           foreach (ObjectId id in btr) {
  74.             using (DBObject obj = id.Open(OpenMode.ForRead) as DBObject) {
  75.               Entity enCur = obj as Entity;
  76.               if (enCur == null || enCur.Visible != true)
  77.                 continue;
  78.               // Пропускаем неконстантные и невидимые определения атрибутов
  79.               AttributeDefinition attDef = enCur as AttributeDefinition;
  80.               if (attDef != null && (!attDef.Constant || attDef.Invisible))
  81.                 continue;
  82.               GetBlockExtents(enCur, ref ext, ref matIns);
  83.             }
  84.           }
  85.         }
  86.         // Отдельно обрабатываем атрибуты блока
  87.         if (bref.AttributeCollection.Count > 0) {
  88.           foreach (ObjectId idAtt in bref.AttributeCollection) {
  89.             using (AttributeReference attRef =
  90.               idAtt.Open(OpenMode.ForRead) as AttributeReference) {
  91.                 if (!attRef.Invisible && attRef.Visible)
  92.                 GetBlockExtents(attRef, ref ext, ref mat);
  93.             }
  94.           }
  95.         }
  96.       }
  97.       else {
  98.         if (mat.IsUniscaledOrtho()) {
  99.           using (Entity enTr = en.GetTransformedCopy(mat)) {
  100.             if (enTr is Dimension)
  101.               (enTr as Dimension).RecomputeDimensionBlock(true);
  102.             if (enTr is Table)
  103.               (enTr as Table).RecomputeTableBlock(true);
  104.             if (IsEmptyExt(ref ext)) {
  105.               try { ext = enTr.GeometricExtents; } catch { };
  106.             }
  107.             else {
  108.               try { ext.AddExtents(enTr.GeometricExtents); } catch { };
  109.             }
  110.             return;
  111.           }
  112.         }
  113.         else {
  114.           try {
  115.             Extents3d curExt = en.GeometricExtents;
  116.             curExt.TransformBy(mat);
  117.             if (IsEmptyExt(ref ext))
  118.               ext = curExt;
  119.             else
  120.               ext.AddExtents(curExt);
  121.           }
  122.           catch { }
  123.           return;
  124.         }
  125.       }
  126.  
  127.       return;
  128.  
  129.     }
  130.     /// <summary>
  131.     /// Определяет видны ли объекты на слое, указанном его ObjectId.
  132.     /// </summary>
  133.     /// <param name="layerId">ObjectId слоя.</param>
  134.     /// <returns></returns>
  135.     bool IsLayerOn(ObjectId layerId)
  136.     {
  137.       using (LayerTableRecord ltr = layerId.Open(OpenMode.ForRead) as LayerTableRecord) {
  138.         if (ltr.IsFrozen) return false;
  139.         if (ltr.IsOff) return false;
  140.       }
  141.       return true;
  142.     }
  143.     /// <summary>
  144.     /// Определят не пустой ли габаритный контейнер.
  145.     /// </summary>
  146.     /// <param name="ext">Габаритный контейнер.</param>
  147.     /// <returns></returns>
  148.     bool IsEmptyExt(ref Extents3d ext)
  149.     {
  150.       if (ext.MinPoint.DistanceTo(ext.MaxPoint) < Tolerance.Global.EqualPoint)
  151.         return true;
  152.       else
  153.         return false;
  154.     }
  155.   }
  156. }
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: JohnLennin от 13-04-2016, 10:30:02
Коллеги, посмотрел я Ваше прекрасное обсуждение, вещь очень полезная, как раз то, что я ищу, однако есть один вопрос или нюанс - рамка и габаритный контейнер очень хороши, когда блоки ортогонально ориентированы, однако если есть поворот то контейнер выходит за пределы блока.
Мне необходимо решить простую задачу - стоят ли BReferences в одном ряду (вплотную друг к другу) или нет. Точка вставки у них расположена одинаково, осталось только вычислить длину блока. Данный код помогает все разобрать, но только при ортогональном расположении, а если BReferences стоят в ряд, но этот ряд повернут под углом, то этот  принцип не совсем работает.
Собственно в чем вопрос - можно ли этот контейнер ориентировать под тем же углом, под которым повернута BReference?
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дима_ от 13-04-2016, 11:19:57
можно ли этот контейнер ориентировать под тем же углом, под которым повернута BReference
Нет, но можно, например, повернуть копии блоков вокруг "первой" точки и посмотреть.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Пашин Евгений от 13-04-2016, 11:27:16
можно ли этот контейнер ориентировать под тем же углом, под которым повернута BReference?

Можно, если предварительно его создать, а затем повернуть :)

А если создавать непосредственно для повернутого объекта, то только вычисляя диагональ области, НО, как мы видим на рисунке, размер диагонали будет отличаться от длины самого объекта.

Либо такой вариант, НО предупреждаю, что это ИМХО :)
1. Выбрать объект
2. Поворачивать его на 360 градусов с шагом, ну скажем 0,1 градуса, И, для каждого положения объекта вычислять размеры ЗАНИМАЕМОЙ ОБЛАСТИ (габаритный контейнер), в Вашем случае надо лишь узнать длину.
3. Тот из контейнеров, чья длина будет максимальной ВСТАВИТЬ по центру повернутого объекта и повернуть его на тот же угол, что и сам объект.

Решить можно и по-другому, НО буду рад, если у Вас найдётся решение лучше предложенного )))
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: JohnLennin от 13-04-2016, 11:44:27
Нет, но можно, например, повернуть копии блоков вокруг "первой" точки и посмотреть.
Но прежде чем повернуть надо понять стоят ли они в один ряд или нет? и вокруг какой точки поворачивать.
Задача примерно такая, есть файл  с кучей BReferences, которые в него вставлены, надо найти те, которые стоят в ряд соприкасаясь. с ортогональными проблем нет, с повернутыми под углом - я могу их сгруппировать по одинаковому rotation, а дальше что с ними делать? вокруг которой точки поворачивать?
Я не совсем до конца понимаю код приведенный здесь, может имеет смысл что-то с преобразованием матрицы в МСК из ПСК блока сделать?

В идеале хотелось бы получить что-то типа вот этого:
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дима_ от 13-04-2016, 12:06:59
я могу их сгруппировать по одинаковому rotation, а дальше что с ними делать? вокруг которой точки поворачивать?
Любой точки - (0,0,0) например - а потом смотреть какие "легли в ряды". 
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: JohnLennin от 13-04-2016, 12:29:28
Любой точки - (0,0,0) например - а потом смотреть какие "легли в ряды". 
А, понял, тупанул что-то. Т.е. предлагаете выбрать BRef с одинаковым rotation, который не равен 0,90,180,270 и повернуть их все вокруг любой точки на -rotation, затем посмотреть в ряду они или нет, затем снова повернуть обратно на rotation?
Блин... а если в файле сотни объектов? Это ведь очень долго будет. Нет ли поизящнее решения?
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 13-04-2016, 12:40:01
Блин... а если в файле сотни объектов? Это ведь очень долго будет. Нет ли поизящнее решения?
Не думаю, что есть более быстрое решение. И в любом случае у него будут ограничения.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 13-04-2016, 13:31:11
Сейчас времени нет проверять, но в качестве идеи:
1. Определять общий Extents3d объектов внутри блока в координатах блока, не транслируя объекты в модель.
2. Вычислить помимо полученных в Extents3d MinPoint и MaxPoint еще 2 точки рамки в координатах блока.
3. Транслировать эти 4 точки в координаты модели и получить нужную рамку.
Может быть, так получится?
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Александр Ривилис от 13-04-2016, 13:45:47
1. Определять общий Extents3d объектов внутри блока в координатах блока, не транслируя объекты в модель.
С динамическими блоками будет не так просто.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 13-04-2016, 13:53:11
Если с динамическими не будет получаться, то, как вариант, можно попробовать их в статические конвертировать временно.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дима_ от 13-04-2016, 14:20:34
Блин... а если в файле сотни объектов?
Ну дамаю где-то пол секунды.
з.ы. не надо крутить туда-обратно - выбрал вхождения, сгруппировал их по углу поворота, сделал копии каждой группы сохраняя ссылку на id исходного объекта, повернул и поделил на "однорядцев".
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: JohnLennin от 16-04-2016, 12:43:45
Спасибо! Вроде все получилось и ряды правильно определяются, единственное что иногда при определении границы у некоторых динамических блоков выходят за их пределы из-за отображения текста видимых атрибутов. Нельзя ли как-нибудь исключить видимые атрибуты из определения границ блока?
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дима_ от 16-04-2016, 12:58:53
Вы же копию проверяете - ну так удалить все атрибуты перед обмеркой.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: JohnLennin от 16-04-2016, 14:09:04
Вы же копию проверяете - ну так удалить все атрибуты перед обмеркой.
А атрибуты у блока не удалятся? Я ведь копию BlockReference делаю.
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: Дмитрий Загорулькин от 16-04-2016, 16:11:24
Нельзя ли как-нибудь исключить видимые атрибуты из определения границ блока?
Если используется код отсюда: http://adn-cis.org/forum/index.php?topic=2933.msg12071#msg12071, то нужно закомментировать строки 87-95
Название: Re: Как получить размеры динамического блока с помощью чистого NET? (без COM)
Отправлено: JohnLennin от 18-04-2016, 06:49:16
Спасибо! Теперь границы выехавшего текста не попадают в границы блока.