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

ADN Club => AutoCAD .NET API => Тема начата: Андрей Бушман от 30-01-2014, 20:23:22

Название: Нарезка 3D тел
Отправлено: Андрей Бушман от 30-01-2014, 20:23:22
AutoCAD 2009 SP3

Ниже приводится код, выдавливающий регионы в 3D тела, которые затем нарезаются "мелкими ломтиками" (пример упрощённый). Во вложении находится файл slice_test.dwg, на котором код тестировался.

Код - C# [Выбрать]
  1. // SliceOperations.cs
  2. // © Andrey Bushman, 2014
  3. // Команда test запрашивает у пользователя объекты ОБЛАСТЬ (REGION), которые
  4. // необходимо выдавить не некоторую высоту, создав тем самым объекты 3D ТЕЛ (3D SOLID),
  5. // после чего ОБЛАСТИ удаляются, а 3D-ТЕЛА разрезаются по осям X и Y на куски
  6. // определённого размера.
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Text;
  11.  
  12. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  13. using App = Autodesk.AutoCAD.ApplicationServices;
  14. using Db = Autodesk.AutoCAD.DatabaseServices;
  15. using Ed = Autodesk.AutoCAD.EditorInput;
  16. using Gem = Autodesk.AutoCAD.Geometry;
  17. using Rtm = Autodesk.AutoCAD.Runtime;
  18.  
  19. [assembly: Rtm.CommandClass(typeof(Bushman.CAD.Commands.SliceOperations))]
  20.  
  21. namespace Bushman.CAD.Commands {
  22.  
  23.         public class SliceOperations {
  24.  
  25.                 const String ns = "Bushman";
  26.  
  27.                 [Rtm.CommandMethod(ns, "test", Rtm.CommandFlags.Modal | Rtm.CommandFlags.NoPaperSpace)]
  28.                 public void Test() {
  29.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  30.                         Db.Database db = doc.Database;
  31.                         Ed.Editor ed = doc.Editor;
  32.                         Double dx = 50.0;
  33.                         Double dy = 50.0;
  34.                         Double height = 1000.0;
  35.  
  36.                         Db.TypedValue[] tv = new Db.TypedValue[1];
  37.                         tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
  38.                         Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
  39.                         Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
  40.                         pso.MessageForAdding = "Выберите регионы для добавления их в набор";
  41.                         pso.MessageForRemoval = "Выберите регионы, подлежащие удалению из набора";
  42.                         pso.SingleOnly = false;
  43.                         pso.RejectObjectsFromNonCurrentSpace = true;
  44.                         pso.AllowDuplicates = false;
  45.                         Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);
  46.                         if (psr.Status != Ed.PromptStatus.OK) {
  47.                                 ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  48.                                 return;
  49.                         }
  50.                         else {
  51.                                 ed.WriteMessage("\nВсего выбрано регионов: {0}\n", psr.Value.Count);
  52.                         }
  53.                         Db.ObjectId[] ids = psr.Value.GetObjectIds();
  54.                         using (doc.LockDocument()) {
  55.                                 using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  56.                                         Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead);
  57.                                         Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[Db.BlockTableRecord.ModelSpace],
  58.                                                 Db.OpenMode.ForWrite);
  59.                                         foreach (Db.ObjectId id in ids) {
  60.                                                 if (!id.IsValid || id.IsNull || id.IsErased) {
  61.                                                         ed.WriteMessage("Неверный идентификатор объекта\n");
  62.                                                         continue;
  63.                                                 }
  64.                                                 Db.Entity ent = tr.GetObject(id, Db.OpenMode.ForWrite) as Db.Entity;
  65.                                                 if (ent == null) {
  66.                                                         ed.WriteMessage("Не удалось привести объект к типу Entity.\n");
  67.                                                         continue;
  68.                                                 }
  69.  
  70.                                                 // Выдавливаю область в 3D-тело
  71.                                                 Db.Solid3d solid3d = new Db.Solid3d();
  72.                                                 solid3d.SetDatabaseDefaults();
  73.                                                 solid3d.Extrude((Db.Region)ent, height, 0);
  74.                                                 solid3d.LayerId = ent.LayerId;
  75.                                                 solid3d.ColorIndex = ent.ColorIndex;
  76.                                                 solid3d.Color = ent.Color;
  77.                                                 btr.AppendEntity(solid3d);
  78.                                                 tr.AddNewlyCreatedDBObject(solid3d, true);
  79.                                                 ent.Erase(); // исходную область удаляю
  80.  
  81.                                                 Db.Extents3d ext = solid3d.GeometricExtents;
  82.                                                 Int32 rowsCount = (Int32)((ext.MaxPoint.Y - ext.MinPoint.Y) / dy);
  83.                                                 Int32 columnsCount = (Int32)((ext.MaxPoint.X - ext.MinPoint.X) / dx);
  84.                                                 Gem.Plane plane = null;
  85.                                                 Db.Solid3d rowSolid = null;
  86.                                                 Db.Solid3d columnSolid = null;
  87.                                                 // Режу твёрдотельный 3D-объект на "строки"
  88.                                                 for (Int32 row = 1; row <= rowsCount; row++) {
  89.                                                         plane = new Gem.Plane(
  90.                                                                 // точка, указывающая направление оси X
  91.                                                                 new Gem.Point3d(ext.MaxPoint.X, ext.MinPoint.Y + row * dy, ext.MinPoint.Z),
  92.                                                                 // Точка пересечения осей X и Y
  93.                                                                 new Gem.Point3d(ext.MinPoint.X, ext.MinPoint.Y + row * dy, ext.MinPoint.Z),
  94.                                                                 // Точка, указывающая направление оси Y
  95.                                                                 new Gem.Point3d(ext.MinPoint.X, ext.MinPoint.Y + row * dy, ext.MinPoint.Z + 100));
  96.  
  97.                                                         rowSolid = solid3d.Slice(plane, true);
  98.                                                         rowSolid.LayerId = solid3d.LayerId;
  99.                                                         rowSolid.ColorIndex = solid3d.ColorIndex;
  100.                                                         rowSolid.Color = solid3d.Color;
  101.                                                         btr.AppendEntity(rowSolid);
  102.                                                         tr.AddNewlyCreatedDBObject(rowSolid, true);
  103.  
  104.                                                         // Каждую "строку" режу на "столбцы" (ячейки)
  105.                                                         for (Int32 column = 1; column <= columnsCount; column++) {
  106.                                                                 plane = new Gem.Plane(
  107.                                                                         // точка, указывающая направление оси X
  108.                                                                 new Gem.Point3d(ext.MinPoint.X + column * dx, -ext.MaxPoint.Y, ext.MinPoint.Z),
  109.                                                                         // Точка пересечения осей X и Y
  110.                                                                 new Gem.Point3d(ext.MinPoint.X + column * dx, ext.MinPoint.Y, ext.MinPoint.Z),
  111.                                                                         // Точка, указывающая направление оси Y
  112.                                                                 new Gem.Point3d(ext.MinPoint.X + column * dx, ext.MinPoint.Y, ext.MinPoint.Z + 100));
  113.                                                                 try {
  114.                                                                         columnSolid = solid3d.Slice(plane, true);
  115.                                                                 }
  116.                                                                 catch {
  117.                                                                         ed.WriteMessage("\nНе удалось выполнить Slice для [строка, колонка]: ({0},{1}).\n",
  118.                                                                                 row, column);
  119.                                                                         continue;
  120.                                                                 }
  121.                                                                 columnSolid.LayerId = rowSolid.LayerId;
  122.                                                                 columnSolid.ColorIndex = rowSolid.ColorIndex;
  123.                                                                 columnSolid.Color = rowSolid.Color;
  124.  
  125.                                                                 btr.AppendEntity(columnSolid);
  126.                                                                 tr.AddNewlyCreatedDBObject(columnSolid, true);
  127.  
  128.                                                                 // Нарезка "ячеек" в последней "строке"
  129.                                                                 if (row == rowsCount && column == columnsCount) {
  130.                                                                         solid3d = rowSolid;
  131.                                                                         row++;
  132.                                                                         column = 0;
  133.                                                                 }
  134.                                                                 else
  135.                                                                         solid3d = columnSolid;
  136.                                                         }
  137.                                                         solid3d = rowSolid;
  138.                                                 }
  139.                                         }
  140.                                         tr.Commit();
  141.                                 }
  142.                         }
  143.                 }
  144.         }
  145. }

В чертеже, зелёным цветом подсвечены регионы (см. 0.png), при обработке которых не возникает исключение в строке 114 обозначенного выше кода. Красным подсвечены те, где исключение возникает. В консоль AutoCAD отправляю сообщение о том, в какой именно "ячейке" возникло очередное исключение... Насколько я понимаю, исключение возникает в том случае, если в очередное сечение не попадает ничего.

Результат нарезки показан в 1.png.

Вопрос 1: Как проверить, попадает ли часть нужного 3D солида в сечение (очень уж хочется избавиться от Exception...)? Или может есть какой-то иной способ избавиться от генерации Exception?

Вопрос 2: Пока не понял, почему у окружности верхний край остался не разрезанным по вертикали (см. 2.png)...   Ещё немного подумав, понял почему: генерация исключения, которая происходит при обработке предпоследней "строки" окружности, благодаря continue проскакивает мимо фрагмента кода:

Код - C# [Выбрать]
  1. // Нарезка "ячеек" в последней "строке"
  2. if (row == rowsCount && column == columnsCount) {
  3.         solid3d = rowSolid;
  4.         row++;
  5.         column = 0;
  6. }
  7. else
  8.         solid3d = columnSolid;

Исправлю этот момент завтра (сегодня уже пора домой топать). Конструктор Plane тоже перепишу - будет два параметра: точка и вектор (так будет проще и "читабельней").
Название: Re: Нарезка 3D тел
Отправлено: Дима_ от 31-01-2014, 10:20:45
Цитировать
Как проверить, попадает ли часть нужного 3D солида в сечение (очень уж хочется избавиться от Exception...)?
При нарезке строк на столбцы проверять лежит ли координата X сечения между габаритными координатами X разрезаемого тела (то есть взять габариты тела перед нарезкой - т.к. они меняются на каждой итерации) т.е.
ext = rowsolid.GeometricExtents;
ext.MinPoint.X<X<ext.MaxPoint.X // X - координата через которую проходит сечение (от которой оно строится)
и если условие не совпадает, то резать там уже не надо (все уже украдено до Вас  :) ).
Аналогично, только для Y координаты, проверять при нарезке строк (беря обновленные габариты после каждого "обрезания") т.к. исходный Region может представлять из себя 2 непересекающихся прямоугольника (один над другим), но при этом быть одним целым (как материал летающей тарелки у братьев Стругацких - две пластины закрепленные на пустоте между ними).
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 31-01-2014, 10:35:26
При нарезке строк на столбцы проверять лежит ли координата X сечения между габаритными координатами X разрезаемого тела
Да, спасибо, пока ехал с утра на работу, что называется "на свежую голову" прокручивал в голове код и тоже обнаружил эту ошибку, но вы меня уже опередили с ответом :).
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 31-01-2014, 17:00:43
Внёс необходимые исправления в код. Во вложении к данному сообщению находится обновлённая версия файла slice_test.dwg: в чертёж добавлены дополнительные регионы для тестирования, в т. ч. и регионы, созданные с помощью сплайнов (выделены красным цветом). Регионы, обозначенные зелёным цветом, обрабатываются корректно (все эти регионы созданы не сплайнами).  Исходный набор регионов показан ниже на скрине regions.png, а результат обработки - на скрине result.png.

С обработкой регионов, созданных не из сплайнов - проблем нет, работает как часы. Но вот с обработкой регионов, созданных из сплайнов возникает проблема... Дело в том, что у таких регионов свойство Entity.GeometricExtents отображает некорректную информацию. Насколько я понимаю, это свойство должно отображать противоположные углы наименьшей "коробки", в рамках которой вписано визуальное представление объекта. Так вот у регионов, созданных на основе сплайнов, это не так: см. ниже скрин GeometricExtents.png - белым контуром обозначен этот самый GeometricExtents (команда GeomExt в моём коде, предоставленном ниже).

Уж очень не хочется добавлять блок try\catch (по вполне понятным причинам)...

Код - C# [Выбрать]
  1. // SliceOperations.cs
  2. // © Andrey Bushman, 2014
  3. // Команда SliceGrid запрашивает у пользователя объекты ОБЛАСТЬ (REGION), которые
  4. // необходимо выдавить не некоторую высоту, создав тем самым объекты 3D ТЕЛ
  5. // (3D SOLID), после чего ОБЛАСТИ удаляются, а 3D-ТЕЛА разрезаются по осям X
  6. // и Y на куски определённого размера.
  7. // Команда GeomExt - для указанных примитивов показывает границы GeometricExtents
  8. // (по контуру этой границы создаётся полилиния).
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using System.Text;
  13.  
  14. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  15. using App = Autodesk.AutoCAD.ApplicationServices;
  16. using Db = Autodesk.AutoCAD.DatabaseServices;
  17. using Ed = Autodesk.AutoCAD.EditorInput;
  18. using Gem = Autodesk.AutoCAD.Geometry;
  19. using Rtm = Autodesk.AutoCAD.Runtime;
  20. using System.IO;
  21.  
  22. [assembly: Rtm.CommandClass(typeof(Bushman.CAD.Commands.SliceOperations))]
  23.  
  24. namespace Bushman.CAD.Commands {
  25.  
  26.         public class SliceOperations : Rtm.IExtensionApplication {
  27.  
  28.                 const String ns = "Bushman";
  29.  
  30.                 /// <summary>
  31.                 /// Тестовая команда преобразования регионов в 3D-тела с последующей их нарезкой
  32.                 /// ломтиками по горизонтали и вертикали
  33.                 /// </summary>
  34.                 [Rtm.CommandMethod(ns, "SliceGrid", Rtm.CommandFlags.Modal |
  35.                         Rtm.CommandFlags.NoPaperSpace)]
  36.                 public void SliceGrid() {
  37.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  38.                         Db.Database db = doc.Database;
  39.                         Ed.Editor ed = doc.Editor;
  40.                         Double dx = 50.0;
  41.                         Double dy = 50.0;
  42.                         Double height = 1000.0;
  43.  
  44.                         Db.TypedValue[] tv = new Db.TypedValue[1];
  45.                         tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
  46.                         Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
  47.                         Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
  48.                         pso.MessageForAdding = "Выберите регионы для добавления их в набор";
  49.                         pso.MessageForRemoval = "Выберите регионы, подлежащие удалению из набора";
  50.                         pso.SingleOnly = false;
  51.                         pso.RejectObjectsFromNonCurrentSpace = true;
  52.                         pso.AllowDuplicates = false;
  53.                         Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);
  54.                         if (psr.Status != Ed.PromptStatus.OK) {
  55.                                 ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  56.                                 return;
  57.                         }
  58.                         else {
  59.                                 ed.WriteMessage("\nВсего выбрано регионов: {0}\n", psr.Value.Count);
  60.                         }
  61.                         Db.ObjectId[] ids = psr.Value.GetObjectIds();
  62.                         Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions("dx");
  63.                         pdo.AllowNegative = false;
  64.                         pdo.AllowZero = false;
  65.                         pdo.AllowNone = false;
  66.                         pdo.DefaultValue = 50.0;
  67.                         pdo.UseDefaultValue = true;
  68.  
  69.                         Ed.PromptDoubleResult pdr = ed.GetDouble(pdo);
  70.                         if (pdr.Status != Ed.PromptStatus.OK) {
  71.                                 ed.WriteMessage("Выполнение команды прервано\n");
  72.                                 return;
  73.                         }
  74.                         dx = pdr.Value;
  75.  
  76.                         pdo.Message = "dy";
  77.                         pdr = ed.GetDouble(pdo);
  78.                         if (pdr.Status != Ed.PromptStatus.OK) {
  79.                                 ed.WriteMessage("Выполнение команды прервано\n");
  80.                                 return;
  81.                         }
  82.                         dy = pdr.Value;
  83.  
  84.                         pdo.Message = "Высота выдавливания";
  85.                         pdo.AllowNegative = true;
  86.                         pdo.DefaultValue = 1000.0;
  87.                         pdr = ed.GetDouble(pdo);
  88.                         if (pdr.Status != Ed.PromptStatus.OK) {
  89.                                 ed.WriteMessage("Выполнение команды прервано\n");
  90.                                 return;
  91.                         }
  92.                         height = pdr.Value;
  93.  
  94.                         DateTime start = DateTime.Now;
  95.                         Db.ObjectId[] result = SliceGrid(dx, dy, height, ids, true);
  96.                         DateTime end = DateTime.Now;
  97.                         ed.WriteMessage("\nСоздано тел: {0}\nЗатраченное время: {1}\n",
  98.                                 result.Length, end - start);
  99.                 }
  100.  
  101.                 /// <summary>
  102.                 /// Для указанных областей, метод выдавливает 3D-тела и каждое из них нарезает
  103.                 /// сеткой.
  104.                 /// </summary>
  105.                 /// <param name="dx">Ширина ячейки сетки</param>
  106.                 /// <param name="dy">Высота ячейки сетки</param>
  107.                 /// <param name="height">Высота выдавлениваемых объектов.</param>
  108.                 /// <param name="ids">Идентификаторы областей, подлежащих выдавливанию.
  109.                 /// </param>
  110.                 /// <param name="eraseRegions">true - удалять исходные регионы,
  111.                 /// false - не удалять.</param>
  112.                 /// <returns>Возвращается массив идентификаторов созданных 3D-тел.</returns>
  113.                 public static Db.ObjectId[] SliceGrid(Double dx, Double dy, Double height,
  114.                         Db.ObjectId[] ids, Boolean eraseRegions) {
  115.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  116.                         Db.Database db = doc.Database;
  117.                         Ed.Editor ed = doc.Editor;
  118.                         List<Db.ObjectId> result = new List<Db.ObjectId>();
  119.                         using (doc.LockDocument()) {
  120.                                 using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  121.                                         Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
  122.                                                 Db.OpenMode.ForRead);
  123.                                         Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
  124.                                                 Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);
  125.                                         foreach (Db.ObjectId id in ids) {
  126.                                                 if (!id.IsValid || id.IsNull || id.IsErased) {
  127.                                                         ed.WriteMessage("Неверный идентификатор объекта\n");
  128.                                                         continue;
  129.                                                 }
  130.                                                 Db.Entity ent = tr.GetObject(id, Db.OpenMode.ForWrite) as Db.Entity;
  131.                                                 if (ent == null) {
  132.                                                         ed.WriteMessage("Не удалось привести объект к типу Entity.\n");
  133.                                                         continue;
  134.                                                 }
  135.  
  136.                                                 // Для начала нужно выдавить исходную область в 3D-тело, подлежащее
  137.                                                 // дальнейшей обработке
  138.                                                 Db.Solid3d solid_base = new Db.Solid3d(); // Исходное тело
  139.                                                 solid_base.SetDatabaseDefaults();
  140.                                                 solid_base.Extrude((Db.Region)ent, height, 0); // Выдавил тело                                              
  141.                                                 solid_base.LayerId = ent.LayerId;
  142.                                                 solid_base.ColorIndex = ent.ColorIndex;
  143.                                                 solid_base.Color = ent.Color;
  144.                                                 btr.AppendEntity(solid_base);
  145.                                                 tr.AddNewlyCreatedDBObject(solid_base, true);
  146.                                                 result.Add(solid_base.ObjectId);
  147.                                                 if (eraseRegions) ent.Erase();
  148.                                                 Db.Extents3d ext_base = solid_base.GeometricExtents;
  149.  
  150.                                                 Int32 rowsCount = (Int32)((ext_base.MaxPoint.Y - ext_base.MinPoint.Y)
  151.                                                         / dy);
  152.                                                 Int32 columnsCount = (Int32)((ext_base.MaxPoint.X - ext_base.MinPoint.X)
  153.                                                         / dx);
  154.  
  155.                                                 // Часть тела, отрезанная в виде горизонтального куска, высотой dy
  156.                                                 // (если смотреть сверху).
  157.                                                 Db.Solid3d solid_added_row = null;
  158.  
  159.                                                 // Режу твёрдотельный 3D-объект на "строки"
  160.                                                 Int32 row_index = 1;
  161.                                                 Double hor_section = 0.0; // Координата Y очередного гориз. сечения
  162.  
  163.                                                 Db.Solid3d solid_current_row = solid_base;
  164.  
  165.                                                 // Режу по горизонтали до тех пор, пока секущая плоскость находится в
  166.                                                 // пределах габаритов исходного тела
  167.                                                 while ((hor_section = ext_base.MinPoint.Y + row_index * dy) <
  168.                                                         solid_current_row.GeometricExtents.MaxPoint.Y) {
  169.                                                         // Получаю границы текущего тела, т.к. зачастую, в очередном цикле
  170.                                                         // итерации, это уже будет др. тело, со своими границами
  171.                                                         Db.Extents3d ext_current = solid_current_row.GeometricExtents;
  172.                                                         // Если тело не попадает в данное сечение - перехожу к следующей
  173.                                                         // итерации
  174.                                                         if (hor_section <= ext_current.MinPoint.Y) {
  175.                                                                 ++row_index;
  176.                                                                 continue;
  177.                                                         }
  178.                                                         // Очередная горизонтальная секущая плоскость
  179.                                                         Gem.Plane hor_plane = new Gem.Plane(
  180.                                                                 // Точка пересечения осей X и Y
  181.                                                                 new Gem.Point3d(ext_base.MinPoint.X, ext_base.MinPoint.Y + row_index *
  182.                                                                         dy, ext_base.MinPoint.Z),
  183.                                                                 // Направление вектора плоскости. Это направление противоположно тому,
  184.                                                                 // в какую стороны будет производиться нарезка 3D тела
  185.                                                                 new Gem.Vector3d(0, -1, 0));
  186.  
  187.                                                         solid_added_row = solid_current_row.Slice(hor_plane, true);
  188.                                                         solid_added_row.LayerId = solid_current_row.LayerId;
  189.                                                         solid_added_row.ColorIndex = solid_current_row.ColorIndex;
  190.                                                         solid_added_row.Color = solid_current_row.Color;
  191.                                                         btr.AppendEntity(solid_added_row);
  192.                                                         tr.AddNewlyCreatedDBObject(solid_added_row, true);
  193.                                                         result.Add(solid_added_row.ObjectId);
  194.                                                         Db.Solid3d solid_current_column = solid_current_row;
  195.                                                         SliceRow(dx, ref ext_base, ref solid_current_column, ref result);
  196.                                                         ++row_index;
  197.                                                         solid_current_row = solid_added_row;
  198.                                                 }
  199.                                                 SliceRow(dx, ref ext_base, ref solid_current_row, ref result);
  200.                                         }
  201.                                         tr.Commit();
  202.                                 }
  203.                         }
  204.                         return result.ToArray();
  205.                 }
  206.  
  207.                 /// <summary>
  208.                 /// Разрезать 3D-тело по вертикали с определённым шагом
  209.                 /// </summary>
  210.                 /// <param name="dx">Горизонтальный шаг резки</param>    
  211.                 /// <param name="ext_base">Границы исходного объекта</param>
  212.                 /// <param name="solid">Объект, подлежащий резке параллельно оси Y.
  213.                 /// </param>
  214.                 /// <param name="result">Ссылка на список, в который следует добавлять
  215.                 /// идентификаторы новых 3D тел.
  216.                 /// </param>
  217.                 private static void SliceRow(Double dx, ref Db.Extents3d ext_base,
  218.                         ref Db.Solid3d solid, ref List<Db.ObjectId> result) {
  219.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  220.                         Db.Database db = doc.Database;
  221.                         Ed.Editor ed = doc.Editor;
  222.                         using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  223.                                 Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
  224.                                         Db.OpenMode.ForRead);
  225.                                 Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
  226.                                         Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);
  227.                                 // Каждую "строку" режу на "столбцы" (ячейки)
  228.                                 Int32 column_index = 1;
  229.                                 Double vert_section = 0.0; // Координата X очередного верт. сечения
  230.                                 // Часть тела, отрезанная от горизонтального куска (rowSolid) в виде
  231.                                 // вертикального куска, шириной dx (если смотреть сверху).
  232.                                 Db.Solid3d columnSolid = null;
  233.                                 // Режу по вертикали до тех пор, пока секущая плоскость находится в
  234.                                 // пределах габаритов исходного тела
  235.                                 while ((vert_section = ext_base.MinPoint.X + column_index * dx) <
  236.                                         solid.GeometricExtents.MaxPoint.X) {
  237.                                         // Получаю границы текущего тела, т.к. зачастую, в очередном цикле
  238.                                         // итерации, это уже будет др. тело, со своими границами:
  239.                                         Db.Extents3d ext_current2 = solid.GeometricExtents;
  240.                                         // Если тело не попадает в данное сечение - перехожу к следующей
  241.                                         // итерации
  242.                                         if (vert_section <= ext_current2.MinPoint.X) {
  243.                                                 ++column_index;
  244.                                                 continue;
  245.                                         }
  246.                                         // Очередная вертикальная секущая плоскость
  247.                                         Gem.Plane vert_plane = new Gem.Plane(
  248.                                                 // Точка пересечения осей X и Y
  249.                                                 new Gem.Point3d(ext_base.MinPoint.X + column_index * dx,
  250.                                                         ext_base.MinPoint.Y, ext_base.MinPoint.Z),
  251.                                                 // Направление вектора плоскости. Это направление противоположно тому,
  252.                                                 // в какую стороны будет производиться нарезка 3D тела
  253.                                                 new Gem.Vector3d(-1, 0, 0));
  254.  
  255.                                         columnSolid = solid.Slice(vert_plane, true);
  256.  
  257.                                         columnSolid.LayerId = solid.LayerId;
  258.                                         columnSolid.ColorIndex = solid.ColorIndex;
  259.                                         columnSolid.Color = solid.Color;
  260.  
  261.                                         btr.AppendEntity(columnSolid);
  262.                                         tr.AddNewlyCreatedDBObject(columnSolid, true);
  263.                                         result.Add(columnSolid.ObjectId);
  264.                                         ++column_index;
  265.                                         solid = columnSolid;
  266.                                 }
  267.                                 tr.Commit();
  268.                         }
  269.                 }
  270.  
  271.                 /// <summary>
  272.                 /// Создать прямоугольники (замкнутые плосские полилинии), демонстрирующие границы
  273.                 /// выбранных объектов. Построение выполняется на основании значений точек
  274.                 /// Entity.GeometricExtents.MinPoint и Entity.GeometricExtents.MaxPoint.
  275.                 /// </summary>
  276.                 [Rtm.CommandMethod(ns, "GeomExt", Rtm.CommandFlags.Modal |
  277.                         Rtm.CommandFlags.NoPaperSpace)]
  278.                 public void GeomExt() {
  279.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  280.                         Db.Database db = doc.Database;
  281.                         Ed.Editor ed = doc.Editor;
  282.                        
  283.                         Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
  284.                         pso.MessageForAdding = "Выберите примитивы для добавления их в набор";
  285.                         pso.MessageForRemoval = "Выберите примитивы, подлежащие удалению из набора";
  286.                         pso.SingleOnly = false;
  287.                         pso.RejectObjectsFromNonCurrentSpace = true;
  288.                         pso.AllowDuplicates = false;
  289.                         Ed.PromptSelectionResult psr = ed.GetSelection(pso);
  290.                         if (psr.Status != Ed.PromptStatus.OK) {
  291.                                 ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  292.                                 return;
  293.                         }
  294.                         else {
  295.                                 ed.WriteMessage("\nВсего выбрано примитивов: {0}\n", psr.Value.Count);
  296.                         }
  297.                         Db.ObjectId[] ids = psr.Value.GetObjectIds();
  298.                         using (doc.LockDocument()) {
  299.                                 using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  300.                                         Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
  301.                                                 Db.OpenMode.ForRead);
  302.                                         Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
  303.                                                 Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);
  304.                                         foreach (Db.ObjectId id in ids) {
  305.                                                 if (!id.IsValid || id.IsNull || id.IsErased) {
  306.                                                         ed.WriteMessage("Неверный идентификатор объекта\n");
  307.                                                         continue;
  308.                                                 }
  309.                                                 Db.Entity ent = tr.GetObject(id, Db.OpenMode.ForWrite) as Db.Entity;
  310.                                                 if (ent == null) {
  311.                                                         ed.WriteMessage("Не удалось привести объект к типу Entity.\n");
  312.                                                         continue;
  313.                                                 }
  314.                                                 Db.Polyline pline = new Db.Polyline();
  315.                                                 pline.AddVertexAt(0,
  316.                                                         new Gem.Point2d(ent.GeometricExtents.MinPoint.X,
  317.                                                         ent.GeometricExtents.MinPoint.Y), 0, 0, 0);
  318.                                                 pline.AddVertexAt(1,
  319.                                                         new Gem.Point2d(ent.GeometricExtents.MinPoint.X,
  320.                                                         ent.GeometricExtents.MaxPoint.Y), 0, 0, 0);
  321.                                                 pline.AddVertexAt(1,
  322.                                                         new Gem.Point2d(ent.GeometricExtents.MaxPoint.X,
  323.                                                         ent.GeometricExtents.MaxPoint.Y), 0, 0, 0);
  324.                                                 pline.AddVertexAt(1,
  325.                                                         new Gem.Point2d(ent.GeometricExtents.MaxPoint.X,
  326.                                                         ent.GeometricExtents.MinPoint.Y), 0, 0, 0);
  327.                                                 pline.Closed = true;
  328.                                                 pline.SetDatabaseDefaults();
  329.                                                 btr.AppendEntity(pline);
  330.                                                 tr.AddNewlyCreatedDBObject(pline, true);
  331.                                         }
  332.                                         tr.Commit();
  333.                                 }
  334.                         }
  335.                 }
  336.  
  337.                 #region IExtensionApplication Members
  338.  
  339.                 public void Initialize() {
  340.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  341.                         Db.Database db = doc.Database;
  342.                         Ed.Editor ed = doc.Editor;
  343.                         ed.WriteMessage("\n{0}. © Andrey Bushman, 2014\n",
  344.                                 Path.GetFileName(this.GetType().Assembly.Location));
  345.                 }
  346.  
  347.                 public void Terminate() {
  348.                 }
  349.                 #endregion
  350.         }
  351. }
Название: Re: Нарезка 3D тел
Отправлено: Дима_ от 31-01-2014, 17:29:58
Да со сплайнами была "беда" c габаритами (помню по vla-getboundingbox), по моему они корректно работали с IntersectWith, но ИХМО это метод еще более извратный чем проверка ошибки  :(
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 31-01-2014, 17:34:19
Да со сплайнами была "беда" c габаритами (помню по vla-getboundingbox), по моему они корректно работали с IntersectWith, но ИХМО это метод еще более извратный чем проверка ошибки 
Тут ещё нюанс ведь в том, что в коде я работаю не непосредственно со сплайнами, а с регионами созданными на их основе. Если  будет чертёж передан со стороны, то х.з. на основе чего они (регионы) там были созданы (на основе сплайна или же на основе чего-то другого). Получается, что регионы наследуют значение этого свойства от сплайнов, а затем 3D тела, в свою очередь, наследуют значение этого свойства от регионов, на основе которых эти тела создавались.

P.S. Проектировщики говорят, что у них вряд ли будут регионы, созданные посредством сплайнов. Тем не менее всё же хочу разобраться с проблемой, т.к. если есть даже минимальный шанс, наступления в гуано (т.е. в качестве исходных данных получить регионы, созданные с помощью сплайнов), то это, к сожалению, обязательно произойдёт (закон жизни).
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 31-01-2014, 18:31:00
Тут ещё нюанс ведь в том, что в коде я работаю не непосредственно со сплайнами, а с регионами созданными на их основе. Если  будет чертёж передан со стороны, то х.з. на основе чего они (регионы) там были созданы (на основе сплайна или же на основе чего-то другого).
Габариты REGION ты можешь получить еще и при помощи BREP .NET API, хотя возможно проще будет расчленить регион (Entity.Explode) - в данном случае ты получишь SPLINE. Точные его габариты методом GeometricExtents ты не получишь - причина в том, что точное определение габаритов для сплайна операция достаточно затратная по времени. Поэтому они пошли простым путем - вычисляют габариты по опорным точкам. Если тебе нужны точные координаты, то альтернативный вариант - с мелким шагом (например, с шагом (Curve.EndParam - Curve.StartParam) * 1e-6) получай координаты точки на сплайне (Curve.GetPointAtParameter) и получай габариты.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 31-01-2014, 19:13:25
Сделаю, в понедельник (сегодня уже домой пора).
Название: Re: Нарезка 3D тел
Отправлено: Дмитрий Загорулькин от 31-01-2014, 19:18:12
Функцию для довольно быстрого и точного определения границ сплайна с использованием метода Ньютона я делал на лиспе, лисп-код в этом сообщении: http://forum.dwg.ru/showpost.php?p=466002&postcount=51. Там же есть функция для его тестирования (рисования прямоугольничков вокруг сплайнов :)). В этой же теме есть еще варианты поиска границ сплайна.
Если результат устроит, могу попробовать вспомнить алгоритм поиска.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 31-01-2014, 19:24:25
Функцию для довольно быстрого и точного определения границ сплайна с использованием метода Ньютона я делал на лиспе, лисп-код в этом сообщении: http://forum.dwg.ru/showpost.php?p=466002&postcount=51. Там же есть функция для его тестирования (рисования прямоугольничков вокруг сплайнов . В этой же теме есть еще варианты поиска границ сплайна.
Если результат устроит, могу попробовать вспомнить алгоритм поиска.
В понедельник можно будет сравнить этот способ с тем, который я нацарапаю. Добавь (если тебе не сложно) в обозначенный LISP пару строк кода, сообщающих о времени выполнения, чтобы можно было более точно сравнивать в т.ч. и по временным затратам.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 31-01-2014, 19:52:15
Или может есть какой-то иной способ избавиться от генерации Exception?
К сожалению метода TrySlice (по аналогии с Database.TryGetObjectId, который тебя когда-то спасал) нет. Так что я вижу три варианта:
1) try/catch
2) BREP .NET API и проверка есть пересечение или нет
3) P/Invoke native метод из ObjectARX <---- если всё остальное не поможет, то скажи. Постараюсь помочь с этим
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 27-05-2014, 22:43:55
Проблему с областями на основе сплайнов решил путём её разрезки на более мелкие области (http://adn-cis.org/forum/index.php?topic=750.0), вместо манипуляций с солидами, описанных выше. Полученные области, при желании, в дальнейшем можно выдавливать в солиды.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 03-06-2014, 14:28:42
Да со сплайнами была "беда" c габаритами (помню по vla-getboundingbox), по моему они корректно работали с IntersectWith, но ИХМО это метод еще более извратный чем проверка ошибки 

Как вариант, в следующем коде показано два способа получения границ (для визуального сравнения результатов).

На скрине жёлтым цветом помечены границы, возвращаемые Автокадом, а зелёным - границы, вычисленныемною при помощи BrepEntity.GetLineContainment (см. ниже код метода GetVisualBoundary).

Красные регионы созданы сплайнами. На них хорошо видна разница в точности вычисления границ.

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

Код - C# [Выбрать]
  1. /* RegionCommands.cs
  2.  * © Андрей Бушман, 2014
  3.  * Дополнительные команды CAD для работы с объектами областей (region).
  4.  */
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Text;
  9.  
  10. using BUc = Bushman.CAD;
  11. using BUx = Bushman.CAD.Extensions;
  12. // для возможности использования методов расширений
  13. using Bushman.CAD.Extensions;
  14.  
  15. #if AUTOCAD
  16. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  17. using Ap = Autodesk.AutoCAD.ApplicationServices;
  18. using Db = Autodesk.AutoCAD.DatabaseServices;
  19. using Ed = Autodesk.AutoCAD.EditorInput;
  20. using Rt = Autodesk.AutoCAD.Runtime;
  21. using Gm = Autodesk.AutoCAD.Geometry;
  22. using Wn = Autodesk.AutoCAD.Windows;
  23. using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
  24. using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
  25. using Br = Autodesk.AutoCAD.BoundaryRepresentation;
  26. #endif
  27.  
  28. [assembly: Rt.CommandClass(typeof(Bushman.CAD.Commands.RegionCommands))]
  29.  
  30. namespace Bushman.CAD.Commands {
  31.  
  32.   public sealed class RegionCommands {
  33.  
  34.     // шаг горизонтальных сечений при определении визуальных границ
  35.     // "GeometricExtents" региона
  36.     static Double horSectionsStep = 1;
  37.     // шаг вертикальных сечений при определении визуальных границ
  38.     // "GeometricExtents" региона
  39.     static Double verSectionsStep = 1;
  40.  
  41.     /// <summary>
  42.     /// Получаем значения границ из "GeometricExtents" для регионов (реализация
  43.     /// AutoCAD по умолчанию).
  44.     /// </summary>
  45.     [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup,
  46.       "AcadBoundaryRegion", Rt.CommandFlags.Modal)]
  47.     public void AcadBoundaryRegion() {
  48.       Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  49.       if(doc == null)
  50.         return;
  51.  
  52.       Db.Database db = doc.Database;
  53.       BUx.RegionTools.SetDatabaseDefaultSettings(db);
  54.       Ed.Editor ed = doc.Editor;
  55.  
  56.       Db.TypedValue[] tv = new Db.TypedValue[1];
  57.       tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
  58.       Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
  59.       Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
  60.       pso.MessageForAdding = "Выберите области (Regions) для добавления " +
  61.         "их в набор";
  62.       pso.MessageForRemoval =
  63.         "Выберите области (Regions), подлежащие удалению из набора";
  64.       pso.SingleOnly = false;
  65.       pso.RejectObjectsFromNonCurrentSpace = true;
  66.       pso.AllowDuplicates = false;
  67.  
  68.       Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);
  69.  
  70.       if(psr.Status != Ed.PromptStatus.OK) {
  71.         ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  72.         return;
  73.       }
  74.       else {
  75.         ed.WriteMessage("\nВсего выбрано областей (Regions): {0}\n",
  76.           psr.Value.Count);
  77.       }
  78.       Db.ObjectId[] ids = psr.Value.GetObjectIds();
  79.  
  80.       ed.WriteMessage(Environment.NewLine);
  81.  
  82.       using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  83.  
  84.         Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
  85.          Db.OpenMode.ForRead);
  86.         Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
  87.          Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);
  88.  
  89.         foreach(Db.ObjectId id in ids) {
  90.           Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead)
  91.             as Db.Region;
  92.           using(Br.Brep brep = new Br.Brep(region)) {
  93.             Gm.Point3d origin = new Gm.Point3d(0, 0, 0);
  94.             Gm.Vector3d normal = new Gm.Vector3d(0, 0, 1);
  95.             Gm.Plane plane = new Gm.Plane(origin, normal);
  96.             Gm.Point2d minPoint = brep.BoundBlock.GetMinimumPoint().Convert2d(
  97.               plane);
  98.             Gm.Point2d maxPoint = brep.BoundBlock.GetMaximumPoint().Convert2d(
  99.               plane);
  100.             Db.Polyline pline = new Db.Polyline(4);
  101.             pline.SetDatabaseDefaults();
  102.  
  103.             Gm.Point2d[] points = new Gm.Point2d[]{
  104.               new Gm.Point2d(minPoint.X,maxPoint.Y),
  105.               new Gm.Point2d(maxPoint.X,maxPoint.Y),
  106.               new Gm.Point2d(maxPoint.X,minPoint.Y),
  107.               new Gm.Point2d(minPoint.X, minPoint.Y)};
  108.  
  109.             for(Int32 i = 0; i < points.Length; i++)
  110.               pline.AddVertexAt(i, points[i], 0, 0, 0);
  111.  
  112.             pline.Closed = true;
  113.             pline.ColorIndex = 50;
  114.  
  115.             btr.AppendEntity(pline);
  116.             tr.AddNewlyCreatedDBObject(pline, true);
  117.           }
  118.         }
  119.         tr.Commit();
  120.       }
  121.     }
  122.  
  123.     /// <summary>
  124.     /// Нахождение наиболее точных границ визуального "GeometricExtents" для
  125.     /// регионов.
  126.     /// </summary>
  127.     [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup,
  128.   "BoundaryRegion", Rt.CommandFlags.Modal)]
  129.     public void BoundaryRegion() {
  130.       Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  131.       if(doc == null)
  132.         return;
  133.  
  134.       Db.Database db = doc.Database;
  135.       BUx.RegionTools.SetDatabaseDefaultSettings(db);
  136.       Ed.Editor ed = doc.Editor;
  137.  
  138.       Db.TypedValue[] tv = new Db.TypedValue[1];
  139.       tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
  140.       Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
  141.       Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
  142.       pso.MessageForAdding = "Выберите области (Regions) для добавления " +
  143.         "их в набор";
  144.       pso.MessageForRemoval =
  145.         "Выберите области (Regions), подлежащие удалению из набора";
  146.       pso.SingleOnly = false;
  147.       pso.RejectObjectsFromNonCurrentSpace = true;
  148.       pso.AllowDuplicates = false;
  149.  
  150.       Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);
  151.  
  152.       if(psr.Status != Ed.PromptStatus.OK) {
  153.         ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  154.         return;
  155.       }
  156.       else {
  157.         ed.WriteMessage("\nВсего выбрано областей (Regions): {0}\n",
  158.           psr.Value.Count);
  159.       }
  160.       Db.ObjectId[] ids = psr.Value.GetObjectIds();
  161.  
  162.       ed.WriteMessage(Environment.NewLine);
  163.  
  164.       Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions(
  165.         "Шаг вертикальных сечений");
  166.       pdo.AllowNegative = false;
  167.       pdo.AllowZero = false;
  168.       pdo.AllowNone = false;
  169.       pdo.DefaultValue = horSectionsStep;
  170.       pdo.UseDefaultValue = true;
  171.  
  172.       Ed.PromptDoubleResult pdr = ed.GetDouble(pdo);
  173.       if(pdr.Status != Ed.PromptStatus.OK) {
  174.         ed.WriteMessage("Выполнение команды прервано\n");
  175.         return;
  176.       }
  177.       horSectionsStep = pdr.Value;
  178.  
  179.       pdo.Message = "Шаг горизонтальных сечений";
  180.       pdo.DefaultValue = verSectionsStep;
  181.  
  182.       pdr = ed.GetDouble(pdo);
  183.       if(pdr.Status != Ed.PromptStatus.OK) {
  184.         ed.WriteMessage("Выполнение команды прервано\n");
  185.         return;
  186.       }
  187.       verSectionsStep = pdr.Value;
  188.  
  189.       using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  190.  
  191.         Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
  192.          Db.OpenMode.ForRead);
  193.         Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
  194.          Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);
  195.  
  196.         foreach(Db.ObjectId id in ids) {
  197.           Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead)
  198.             as Db.Region;
  199.           using(Br.Brep brep = new Br.Brep(region)) {
  200.             Gm.Point3d origin = new Gm.Point3d(0, 0, 0);
  201.             Gm.Vector3d normal = new Gm.Vector3d(0, 0, 1);
  202.             Gm.Plane plane = new Gm.Plane(origin, normal);
  203.  
  204.             Gm.Point2d minPoint = Gm.Point2d.Origin;
  205.             Gm.Point2d maxPoint = Gm.Point2d.Origin;
  206.             Double dx = verSectionsStep;
  207.             Double dy = horSectionsStep;
  208.  
  209.             GetVisualBoundary(region, dx, dy, ref minPoint, ref maxPoint);
  210.  
  211.             Db.Polyline pline = new Db.Polyline(4);
  212.             pline.SetDatabaseDefaults();
  213.  
  214.             Gm.Point2d[] points = new Gm.Point2d[]{
  215.               new Gm.Point2d(minPoint.X,maxPoint.Y),
  216.               new Gm.Point2d(maxPoint.X,maxPoint.Y),
  217.               new Gm.Point2d(maxPoint.X,minPoint.Y),
  218.               new Gm.Point2d(minPoint.X, minPoint.Y)};
  219.  
  220.             for(Int32 i = 0; i < points.Length; i++)
  221.               pline.AddVertexAt(i, points[i], 0, 0, 0);
  222.  
  223.             pline.Closed = true;
  224.             pline.ColorIndex = 80;
  225.  
  226.             btr.AppendEntity(pline);
  227.             tr.AddNewlyCreatedDBObject(pline, true);
  228.           }
  229.         }
  230.         tr.Commit();
  231.       }
  232.     }
  233.  
  234.     /// <summary>
  235.     /// Получить координаты границ левого нижнего и правого верхнего углов для
  236.     /// визуального "GeometricExtents" региона.
  237.     /// </summary>
  238.     /// <param name="region">Регион, для которого следует получить координаты
  239.     /// границ визуального "GeometricExtents".</param>
  240.     /// <param name="dx">Шаг горизонтальных сечений.</param>
  241.     /// <param name="dy">Шаг вертикальных сечений.</param>
  242.     /// <param name="minPoint">Ссылка на переменную Point2d, в которой следует
  243.     /// сохранить координаты левого нижнего угла визуального
  244.     /// "GeometricExtents".</param>
  245.     /// <param name="maxPoint">Ссылка на переменную Point2d, в которой следует
  246.     /// сохранить координаты правого верхнего угла визуального
  247.     /// "GeometricExtents".</param>
  248.     private void GetVisualBoundary(Db.Region region, double dx, double dy,
  249.       ref Gm.Point2d minPoint, ref Gm.Point2d maxPoint) {
  250.  
  251.       using(Br.Brep brep = new Br.Brep(region)) {
  252.  
  253.         // Находим min X
  254.         Double minX = Double.MinValue;
  255.         Gm.Point3d startPoint = region.GeometricExtents.MinPoint;
  256.         Gm.Point3d endPoint = new Gm.Point3d(
  257.           region.GeometricExtents.MinPoint.X,
  258.           region.GeometricExtents.MaxPoint.Y,
  259.           region.GeometricExtents.MinPoint.Z);
  260.  
  261.         while(startPoint.X < region.GeometricExtents.MaxPoint.X) {
  262.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  263.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  264.             if(hits != null) {
  265.               foreach(Br.Hit hit in hits) {
  266.                 if(!hit.IsDisposed)
  267.                   hit.Dispose();
  268.               }
  269.               // чтобы регион полностью находился в границах
  270.               minX = startPoint.X == region.GeometricExtents.MinPoint.X ?
  271.                 startPoint.X : startPoint.X - dx;
  272.               break;
  273.             }
  274.             else {
  275.               startPoint = new Gm.Point3d(startPoint.X + dx, startPoint.Y,
  276.                 startPoint.Z);
  277.               endPoint = new Gm.Point3d(endPoint.X + dx, endPoint.Y,
  278.                 endPoint.Z);
  279.             }
  280.           }
  281.         }
  282.  
  283.         // Находим min Y
  284.         Double minY = Double.MinValue;
  285.         startPoint = region.GeometricExtents.MinPoint;
  286.         endPoint = new Gm.Point3d(
  287.           region.GeometricExtents.MaxPoint.X,
  288.           region.GeometricExtents.MinPoint.Y,
  289.           region.GeometricExtents.MinPoint.Z);
  290.  
  291.         while(startPoint.Y < region.GeometricExtents.MaxPoint.Y) {
  292.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  293.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  294.             if(hits != null) {
  295.               foreach(Br.Hit hit in hits) {
  296.                 if(!hit.IsDisposed)
  297.                   hit.Dispose();
  298.               }
  299.               // чтобы регион полностью находился в границах
  300.               minY = startPoint.Y == region.GeometricExtents.MinPoint.Y ?
  301.                 startPoint.Y : startPoint.Y - dy;
  302.               break;
  303.             }
  304.             else {
  305.               startPoint = new Gm.Point3d(startPoint.X, startPoint.Y + dy,
  306.                 startPoint.Z);
  307.               endPoint = new Gm.Point3d(endPoint.X, endPoint.Y + dy,
  308.                 endPoint.Z);
  309.             }
  310.           }
  311.         }
  312.  
  313.         // Находим max X
  314.         Double maxX = Double.MinValue;
  315.         startPoint = region.GeometricExtents.MaxPoint;
  316.         endPoint = new Gm.Point3d(
  317.           region.GeometricExtents.MaxPoint.X,
  318.           region.GeometricExtents.MinPoint.Y,
  319.           region.GeometricExtents.MinPoint.Z);
  320.  
  321.         while(startPoint.X > region.GeometricExtents.MinPoint.X) {
  322.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  323.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  324.             if(hits != null) {
  325.               foreach(Br.Hit hit in hits) {
  326.                 if(!hit.IsDisposed)
  327.                   hit.Dispose();
  328.               }
  329.               // чтобы регион полностью находился в границах
  330.               maxX = startPoint.X == region.GeometricExtents.MaxPoint.X ?
  331.                 startPoint.X : startPoint.X + dx;
  332.               break;
  333.             }
  334.             else {
  335.               startPoint = new Gm.Point3d(startPoint.X - dx, startPoint.Y,
  336.                 startPoint.Z);
  337.               endPoint = new Gm.Point3d(endPoint.X - dx, endPoint.Y,
  338.                 endPoint.Z);
  339.             }
  340.           }
  341.         }
  342.  
  343.         // Находим max Y
  344.         Double maxY = Double.MinValue;
  345.         startPoint = region.GeometricExtents.MaxPoint;
  346.         endPoint = new Gm.Point3d(
  347.           region.GeometricExtents.MinPoint.X,
  348.           region.GeometricExtents.MaxPoint.Y,
  349.           region.GeometricExtents.MinPoint.Z);
  350.  
  351.         while(startPoint.Y > region.GeometricExtents.MinPoint.Y) {
  352.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  353.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  354.             if(hits != null) {
  355.               foreach(Br.Hit hit in hits) {
  356.                 if(!hit.IsDisposed)
  357.                   hit.Dispose();
  358.               }
  359.               // чтобы регион полностью находился в границах
  360.               maxY = startPoint.Y == region.GeometricExtents.MaxPoint.Y ?
  361.                 startPoint.Y : startPoint.Y + dy;
  362.               break;
  363.             }
  364.             else {
  365.               startPoint = new Gm.Point3d(startPoint.X, startPoint.Y - dy,
  366.                 startPoint.Z);
  367.               endPoint = new Gm.Point3d(endPoint.X, endPoint.Y - dy,
  368.                 endPoint.Z);
  369.             }
  370.           }
  371.         }
  372.         // Возвращаем вычисленный результат
  373.         minPoint = new Gm.Point2d(minX, minY);
  374.         maxPoint = new Gm.Point2d(maxX, maxY);
  375.       }
  376.     }
  377.   }
  378. }
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 03-06-2014, 15:06:37
Попробовал ещё один вариант, предлагаемый Александром Наумовичем:

Цитата: Александр Ривилис
Не хочешь поэкспериментировать со вторым вариантом?
Edge->Curve3d->Curve3d.BoundBlock

Однако результат получил неудовлетворительный:

Код - C# [Выбрать]
  1.     /// <summary>
  2.     /// Получаем значения границ из "GeometricExtents" для регионов (реализация
  3.     /// AutoCAD по умолчанию).
  4.     /// </summary>
  5.     [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup,
  6.       "AcadBoundaryRegion", Rt.CommandFlags.Modal)]
  7.     public void AcadBoundaryRegion() {
  8.       Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  9.       if(doc == null)
  10.         return;
  11.  
  12.       Db.Database db = doc.Database;
  13.       BUx.RegionTools.SetDatabaseDefaultSettings(db);
  14.       Ed.Editor ed = doc.Editor;
  15.  
  16.       Db.TypedValue[] tv = new Db.TypedValue[1];
  17.       tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
  18.       Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
  19.       Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
  20.       pso.MessageForAdding = "Выберите области (Regions) для добавления " +
  21.         "их в набор";
  22.       pso.MessageForRemoval =
  23.         "Выберите области (Regions), подлежащие удалению из набора";
  24.       pso.SingleOnly = false;
  25.       pso.RejectObjectsFromNonCurrentSpace = true;
  26.       pso.AllowDuplicates = false;
  27.  
  28.       Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);
  29.  
  30.       if(psr.Status != Ed.PromptStatus.OK) {
  31.         ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
  32.         return;
  33.       }
  34.       else {
  35.         ed.WriteMessage("\nВсего выбрано областей (Regions): {0}\n",
  36.           psr.Value.Count);
  37.       }
  38.       Db.ObjectId[] ids = psr.Value.GetObjectIds();
  39.  
  40.       ed.WriteMessage(Environment.NewLine);
  41.  
  42.       using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  43.  
  44.         Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
  45.          Db.OpenMode.ForRead);
  46.         Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
  47.          Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);
  48.  
  49.         foreach(Db.ObjectId id in ids) {
  50.           Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead)
  51.             as Db.Region;
  52.  
  53.           using(Br.Brep brep = new Br.Brep(region)) {
  54.             Gm.Point3d origin = new Gm.Point3d(0, 0, 0);
  55.             Gm.Vector3d normal = new Gm.Vector3d(0, 0, 1);
  56.             Gm.Plane plane = new Gm.Plane(origin, normal);
  57.             Gm.BoundBlock3d bb = null;
  58.  
  59.             Gm.Point2d minPoint = Gm.Point2d.Origin;
  60.             Gm.Point2d maxPoint = Gm.Point2d.Origin;
  61.  
  62.             Double minX = Double.MaxValue;
  63.             Double minY = Double.MaxValue;
  64.  
  65.             Double maxX = Double.MinValue;
  66.             Double maxY = Double.MinValue;
  67.  
  68.             if(brep.Edges != null) {
  69.               foreach(Br.Edge edge in brep.Edges) {
  70.                 Gm.Point3d min = edge.BoundBlock.GetMinimumPoint();
  71.                 Gm.Point3d max = edge.BoundBlock.GetMaximumPoint();
  72.  
  73.                 if(min.X < minX)
  74.                   minX = min.X;
  75.  
  76.                 if(min.Y < minY)
  77.                   minY = min.Y;
  78.  
  79.                 if(max.X > maxX)
  80.                   maxX = max.X;
  81.  
  82.                 if(max.Y > maxY)
  83.                   maxY = max.Y;
  84.               }
  85.               minPoint = new Gm.Point2d(minX, minY);
  86.               maxPoint = new Gm.Point2d(maxX, maxY);
  87.             }
  88.             else {
  89.               bb = brep.BoundBlock;
  90.               minPoint = bb.GetMinimumPoint().Convert2d(plane);
  91.               maxPoint = bb.GetMaximumPoint().Convert2d(plane);
  92.             }            
  93.  
  94.            
  95.             Db.Polyline pline = new Db.Polyline(4);
  96.             pline.SetDatabaseDefaults();
  97.  
  98.             Gm.Point2d[] points = new Gm.Point2d[]{
  99.               new Gm.Point2d(minPoint.X,maxPoint.Y),
  100.               new Gm.Point2d(maxPoint.X,maxPoint.Y),
  101.               new Gm.Point2d(maxPoint.X,minPoint.Y),
  102.               new Gm.Point2d(minPoint.X, minPoint.Y)};
  103.  
  104.             for(Int32 i = 0; i < points.Length; i++)
  105.               pline.AddVertexAt(i, points[i], 0, 0, 0);
  106.  
  107.             pline.Closed = true;
  108.             pline.ColorIndex = 50;
  109.  
  110.             btr.AppendEntity(pline);
  111.             tr.AddNewlyCreatedDBObject(pline, true);
  112.           }
  113.         }
  114.         tr.Commit();
  115.       }
  116.     }
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 03-06-2014, 15:12:50
А что если вместо edge.BoundBlock использовать edge.Curve.BoundBlock или edge.Curve.OrthoBoundBlock ?
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 03-06-2014, 15:18:46
P.S.: Кстати, вместо манипуляций с minX/minY и maxX/maxY можно пользоваться методом BoundBlock3d.Extend
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 03-06-2014, 15:23:58
А что если вместо edge.BoundBlock использовать edge.Curve.BoundBlock

Код - C# [Выбрать]
  1. Gm.Point3d min = edge.Curve.BoundBlock.GetMinimumPoint();
  2. Gm.Point3d max = edge.Curve.BoundBlock.GetMaximumPoint();

На скрине видим, что границы изменились, но при этом "пошли лесом" и границы лиловых регионов.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 03-06-2014, 15:26:38
А что если вместо edge.BoundBlock использовать ... или edge.Curve.OrthoBoundBlock ?

Код - C# [Выбрать]
  1. Gm.Point3d min = edge.Curve.OrthoBoundBlock.GetMinimumPoint();
  2. Gm.Point3d max = edge.Curve.OrthoBoundBlock.GetMaximumPoint();

Результат получаю такой же, как на предыдущем скрине.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 03-06-2014, 15:33:54
P.S.: Кстати, вместо манипуляций с minX/minY и maxX/maxY можно пользоваться методом BoundBlock3d.Extend

Из SDK:
Цитата: AutoCAD 2009 SDK
BoundBlock3d.Extend Method
Extends the block to include the new point.
Я не понял, о каком "расширении" идёт речь в документации, но похоже, что это ни коим боком не относится к манипуляциям с minX/minY и maxX/maxY.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 03-06-2014, 17:08:44
Еще один вариант: edge.GetCurveAsNurb(pres).BoundBlock
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 03-06-2014, 17:32:09
Еще один вариант: edge.GetCurveAsNurb(pres).BoundBlock

Код - C# [Выбрать]
  1. Gm.Point3d min = edge.GetCurveAsNurb(0.1).BoundBlock.GetMinimumPoint();
  2. Gm.Point3d max = edge.GetCurveAsNurb(0.1).BoundBlock.GetMaximumPoint();

Результат получаю тот же, что и в предыдущих двух случаях. Заниматься гаданием и метанием туда-сюда не хочется. Способ, показанный мною выше работает. На нём и остановился. Если будут конкретные, проверенные на практике альтернативы - с интересом гляну.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 04-06-2014, 01:35:27
Если будут конкретные, проверенные на практике альтернативы - с интересом гляну.
Вот проверенная на практике альтернатива:
(https://adn-cis.org/forum/proxy.php?request=http%3A%2F%2Fimg-fotki.yandex.ru%2Fget%2F9668%2F7842324.1%2F0_dafea_c5303a75_orig.png&hash=b94d372d2c1f787819209014fb56b606)

Кстати, она сразу даёт понять как можно пользоваться методом BoundBlock3d.Extend.

Код - C# [Выбрать]
  1. private void GetVisualBoundary(AcDb.Region region, double dx, double dy,
  2.   ref AcGe.Point2d minPoint, ref AcGe.Point2d maxPoint)
  3. {
  4.   double hord = (dx + dy) * 0.5;
  5.   using (AcGe.BoundBlock3d boundBlk = new AcGe.BoundBlock3d()) {
  6.     using (AcBr.Brep brep = new AcBr.Brep(region)) {
  7.       foreach (AcBr.Edge edge in brep.Edges) {
  8.         AcGe.Curve3d curve = edge.Curve;
  9.         AcGe.Interval interval = curve.GetInterval();
  10.         AcGe.PointOnCurve3d[] pnts =
  11.           curve.GetSamplePoints(interval.LowerBound, interval.UpperBound, hord);
  12.         foreach (AcGe.PointOnCurve3d pt in pnts) {
  13.           if (!boundBlk.IsBox)
  14.             boundBlk.Set(pt.Point, pt.Point);
  15.           else
  16.             boundBlk.Extend(pt.Point);
  17.         }
  18.       }
  19.     }
  20.     // Возвращаем вычисленный результат
  21.     minPoint =
  22.        new AcGe.Point2d(boundBlk.GetMinimumPoint().X, boundBlk.GetMinimumPoint().Y);
  23.     maxPoint =
  24.        new AcGe.Point2d(boundBlk.GetMaximumPoint().X, boundBlk.GetMaximumPoint().Y);
  25.   }
  26. }
P.S.: Передавать оба значения dx и dy в данном случае излишество.
P.S.S.: Я заменил твои алиасы на те, которые использую сам. Думаю, что обратная процедура не должна доставить тебе труда.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 04-06-2014, 12:13:02
Я подправил ваш код, избавившись от dx и dy (заменил их на одно значение, у себя в коде сделал аналогичные замены):

Код - C# [Выбрать]
  1. public static void GetVisualBoundary2(this Db.Region region, Double delta,
  2.   ref Gm.Point2d minPoint, ref Gm.Point2d maxPoint) {
  3.  
  4.   using(Gm.BoundBlock3d boundBlk = new Gm.BoundBlock3d()) {
  5.     using(Br.Brep brep = new Br.Brep(region)) {
  6.       foreach(Br.Edge edge in brep.Edges) {
  7.         Gm.Curve3d curve = edge.Curve;
  8.         Gm.Interval interval = curve.GetInterval();
  9.         Gm.PointOnCurve3d[] pnts =
  10.           curve.GetSamplePoints(interval.LowerBound, interval.UpperBound,
  11.           delta);
  12.         foreach(Gm.PointOnCurve3d pt in pnts) {
  13.           if(!boundBlk.IsBox)
  14.             boundBlk.Set(pt.Point, pt.Point);
  15.           else
  16.             boundBlk.Extend(pt.Point);
  17.         }
  18.       }
  19.     }
  20.     // Возвращаем вычисленный результат
  21.     minPoint = new Gm.Point2d(boundBlk.GetMinimumPoint().X,
  22.       boundBlk.GetMinimumPoint().Y);
  23.     maxPoint = new Gm.Point2d(boundBlk.GetMaximumPoint().X,
  24.       boundBlk.GetMaximumPoint().Y);
  25.   }
  26. }
Однако в процессе работы кода возникает исключение.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 04-06-2014, 16:05:00
Уточни:
1. Исключение возникает у тебя всегда или только на конкретных Region?
2. Если на конкретных, то нужен файл с указанием на каком Region исключение.
3. Версия AutoCAD?
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 04-06-2014, 16:09:23
1. Я запустил код всего два раза. В обоих случаях выделил сразу все регионы (обрабатываются в цикле). На отдельных не запускал (код уже удалил из проекта).
2. Тестировал на том же DWG, который в этой теме прилагался выше. Он же прилагался и в соседней теме о регионах.
3. AutoCAD 2009.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 04-06-2014, 16:11:36
Ну тогда видимо баг в 2009-ом. Как видишь в 2014 код отработал без исключений - результат на картинке выше.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 04-06-2014, 16:13:27
Ну тогда видимо баг в 2009-ом. Как видишь в 2014 код отработал без исключений - результат на картинке выше.
Можете замерить скорость в обоих вариантах? Интересно, какой будет шустрее. С учётом правок текущий вариант таков:
Код - C# [Выбрать]
  1.     /// <summary>
  2.     /// Получить координаты границ левого нижнего и правого верхнего углов для
  3.     /// визуального "GeometricExtents" региона.
  4.     /// </summary>
  5.     /// <param name="region">Регион, для которого следует получить координаты
  6.     /// границ визуального "GeometricExtents".</param>
  7.     /// <param name="delta">Предельная арифметическая погрешность вычислений.
  8.     /// </param>
  9.     /// <param name="minPoint">Ссылка на переменную Point2d, в которой следует
  10.     /// сохранить координаты левого нижнего угла визуального
  11.     /// "GeometricExtents".</param>
  12.     /// <param name="maxPoint">Ссылка на переменную Point2d, в которой следует
  13.     /// сохранить координаты правого верхнего угла визуального
  14.     /// "GeometricExtents".</param>
  15.     internal static void GetVisualBoundary(this Db.Region region, double delta,
  16.       ref Gm.Point2d minPoint, ref Gm.Point2d maxPoint) {
  17.  
  18.       if(region == null)
  19.         throw new ArgumentNullException("region");
  20.       if(region.IsDisposed)
  21.         throw new ArgumentException("region.IsDisposed == true");
  22.       if(delta <= 0)
  23.         throw new ArgumentException("delta <= 0");
  24.  
  25.       using(Br.Brep brep = new Br.Brep(region)) {
  26.  
  27.         // Находим min X
  28.         Double minX = Double.MinValue;
  29.         Gm.Point3d startPoint = region.GeometricExtents.MinPoint;
  30.         Gm.Point3d endPoint = new Gm.Point3d(
  31.           region.GeometricExtents.MinPoint.X,
  32.           region.GeometricExtents.MaxPoint.Y,
  33.           region.GeometricExtents.MinPoint.Z);
  34.  
  35.         while(startPoint.X < region.GeometricExtents.MaxPoint.X) {
  36.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  37.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  38.             if(hits != null) {
  39.               foreach(Br.Hit hit in hits) {
  40.                 if(!hit.IsDisposed)
  41.                   hit.Dispose();
  42.               }
  43.               // чтобы регион полностью находился в границах
  44.               minX = startPoint.X == region.GeometricExtents.MinPoint.X ?
  45.                 startPoint.X : startPoint.X - delta;
  46.               break;
  47.             }
  48.             else {
  49.               startPoint = new Gm.Point3d(startPoint.X + delta, startPoint.Y,
  50.                 startPoint.Z);
  51.               endPoint = new Gm.Point3d(endPoint.X + delta, endPoint.Y,
  52.                 endPoint.Z);
  53.             }
  54.           }
  55.         }
  56.  
  57.         // Находим min Y
  58.         Double minY = Double.MinValue;
  59.         startPoint = region.GeometricExtents.MinPoint;
  60.         endPoint = new Gm.Point3d(
  61.           region.GeometricExtents.MaxPoint.X,
  62.           region.GeometricExtents.MinPoint.Y,
  63.           region.GeometricExtents.MinPoint.Z);
  64.  
  65.         while(startPoint.Y < region.GeometricExtents.MaxPoint.Y) {
  66.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  67.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  68.             if(hits != null) {
  69.               foreach(Br.Hit hit in hits) {
  70.                 if(!hit.IsDisposed)
  71.                   hit.Dispose();
  72.               }
  73.               // чтобы регион полностью находился в границах
  74.               minY = startPoint.Y == region.GeometricExtents.MinPoint.Y ?
  75.                 startPoint.Y : startPoint.Y - delta;
  76.               break;
  77.             }
  78.             else {
  79.               startPoint = new Gm.Point3d(startPoint.X, startPoint.Y + delta,
  80.                 startPoint.Z);
  81.               endPoint = new Gm.Point3d(endPoint.X, endPoint.Y + delta,
  82.                 endPoint.Z);
  83.             }
  84.           }
  85.         }
  86.  
  87.         // Находим max X
  88.         Double maxX = Double.MinValue;
  89.         startPoint = region.GeometricExtents.MaxPoint;
  90.         endPoint = new Gm.Point3d(
  91.           region.GeometricExtents.MaxPoint.X,
  92.           region.GeometricExtents.MinPoint.Y,
  93.           region.GeometricExtents.MinPoint.Z);
  94.  
  95.         while(startPoint.X > region.GeometricExtents.MinPoint.X) {
  96.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  97.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  98.             if(hits != null) {
  99.               foreach(Br.Hit hit in hits) {
  100.                 if(!hit.IsDisposed)
  101.                   hit.Dispose();
  102.               }
  103.               // чтобы регион полностью находился в границах
  104.               maxX = startPoint.X == region.GeometricExtents.MaxPoint.X ?
  105.                 startPoint.X : startPoint.X + delta;
  106.               break;
  107.             }
  108.             else {
  109.               startPoint = new Gm.Point3d(startPoint.X - delta, startPoint.Y,
  110.                 startPoint.Z);
  111.               endPoint = new Gm.Point3d(endPoint.X - delta, endPoint.Y,
  112.                 endPoint.Z);
  113.             }
  114.           }
  115.         }
  116.  
  117.         // Находим max Y
  118.         Double maxY = Double.MinValue;
  119.         startPoint = region.GeometricExtents.MaxPoint;
  120.         endPoint = new Gm.Point3d(
  121.           region.GeometricExtents.MinPoint.X,
  122.           region.GeometricExtents.MaxPoint.Y,
  123.           region.GeometricExtents.MinPoint.Z);
  124.  
  125.         while(startPoint.Y > region.GeometricExtents.MinPoint.Y) {
  126.           using(Gm.Line3d line = new Gm.Line3d(startPoint, endPoint)) {
  127.             Br.Hit[] hits = brep.GetLineContainment(line, 1);
  128.             if(hits != null) {
  129.               foreach(Br.Hit hit in hits) {
  130.                 if(!hit.IsDisposed)
  131.                   hit.Dispose();
  132.               }
  133.               // чтобы регион полностью находился в границах
  134.               maxY = startPoint.Y == region.GeometricExtents.MaxPoint.Y ?
  135.                 startPoint.Y : startPoint.Y + delta;
  136.               break;
  137.             }
  138.             else {
  139.               startPoint = new Gm.Point3d(startPoint.X, startPoint.Y - delta,
  140.                 startPoint.Z);
  141.               endPoint = new Gm.Point3d(endPoint.X, endPoint.Y - delta,
  142.                 endPoint.Z);
  143.             }
  144.           }
  145.         }
  146.         // Возвращаем вычисленный результат
  147.         minPoint = new Gm.Point2d(minX, minY);
  148.         maxPoint = new Gm.Point2d(maxX, maxY);
  149.       }
  150.     }
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 04-06-2014, 16:58:21
Мой алгоритм пошутрее будет:
Цитировать
Время 1=15.4038811 сек. - твой алгоритм
Время 2=1.504086 сек. - мой алгоритм
Интересно, что повторный запуск привел к еще большей разнице в результатах:
Цитировать
Время 1=28.8376495 сек.
Время 2=1.5920911 сек.
Это на тестовом чертеже по сто раз для каждого из Region получался BoundingBox обоими способами.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 04-06-2014, 17:25:00
Еще улучшил результат, обработав ситуацию когда не все контуры региона сплайны. В этом случае нет необходимости в аппроксимации и BoundigBox и та вычисляется правильно:
Код - C# [Выбрать]
  1. private void GetVisualBoundary(AcDb.Region region, double delta,
  2.   ref AcGe.Point2d minPoint, ref AcGe.Point2d maxPoint)
  3. {
  4.   double hord = delta;
  5.   using (AcGe.BoundBlock3d boundBlk = new AcGe.BoundBlock3d()) {
  6.     using (AcBr.Brep brep = new AcBr.Brep(region)) {
  7.       foreach (AcBr.Edge edge in brep.Edges) {
  8.         using (AcGe.Curve3d curve = edge.Curve) {
  9.           AcGe.ExternalCurve3d curve3d = curve as AcGe.ExternalCurve3d;
  10.           // Делать точный расчет нужно только если образующая - сплайн
  11.           // в противном случае достаточно получить BoundBlock
  12.           if (curve3d != null && curve3d.IsNurbCurve) {
  13.             AcGe.Interval interval = curve.GetInterval();
  14.             AcGe.PointOnCurve3d[] pnts =
  15.               curve.GetSamplePoints(interval.LowerBound, interval.UpperBound, hord);
  16.             foreach (AcGe.PointOnCurve3d pt in pnts) {
  17.               if (!boundBlk.IsBox)
  18.                 boundBlk.Set(pt.Point, pt.Point);
  19.               else
  20.                 boundBlk.Extend(pt.Point);
  21.             }
  22.           } else {
  23.             if (!boundBlk.IsBox) {
  24.               boundBlk.Set(edge.BoundBlock.GetMinimumPoint(), edge.BoundBlock.GetMaximumPoint());
  25.             } else {
  26.               boundBlk.Extend(edge.BoundBlock.GetMinimumPoint());
  27.               boundBlk.Extend(edge.BoundBlock.GetMaximumPoint());
  28.             }
  29.           }
  30.         }
  31.       }
  32.     }
  33.     // Возвращаем вычисленный результат
  34.     minPoint = new AcGe.Point2d(boundBlk.GetMinimumPoint().X, boundBlk.GetMinimumPoint().Y);
  35.     maxPoint = new AcGe.Point2d(boundBlk.GetMaximumPoint().X, boundBlk.GetMaximumPoint().Y);
  36.   }
  37. }
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 05-06-2014, 10:49:14
1. Исключение возникает у тебя всегда или только на конкретных Region?
В AutoCAD 2009 возникает при обработке любого сплайна из обозначенного примера.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 05-06-2014, 11:25:30
Выборочно проверил работу в др. версиях AutoCAD: 2015, 2012, 2010 - в них работает. Т.о. не работает только в 2009-м, который мне как раз и нужен.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 05-06-2014, 13:54:18
Не пробовал экспериментировать в 2009-ом со значениями interval? Посмотреть, какие значения получились у interval.LowerBound и interval.UpperBound и скажем "сжать" этот интервал на 1/1000 его длины. Не исключено, что ошибка из-за попадания на границу интервала.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 05-06-2014, 16:13:29
Для того, чтобы ваш вариант заработал в 2009-м, приходится использовать другой перегруженный вариант метода GetSamplePoints. Иначе в 2009-м возникает исключение, обозначенное мною ранее в теме. Т.о. код будет выглядеть так:
Код - C# [Выбрать]
  1.     public static void GetVisualBoundary(this AcDb.Region region, double delta,
  2.   ref AcGe.Point2d minPoint, ref AcGe.Point2d maxPoint) {
  3.       double hord = delta;
  4.       using(AcGe.BoundBlock3d boundBlk = new AcGe.BoundBlock3d()) {
  5.         using(AcBr.Brep brep = new AcBr.Brep(region)) {
  6.           foreach(AcBr.Edge edge in brep.Edges) {
  7.             using(AcGe.Curve3d curve = edge.Curve) {
  8.               AcGe.ExternalCurve3d curve3d = curve as AcGe.ExternalCurve3d;
  9.               // Делать точный расчет нужно только если образующая - сплайн
  10.               // в противном случае достаточно получить BoundBlock
  11.               if(curve3d != null && curve3d.IsNurbCurve) {
  12.                 AcGe.Interval interval = curve.GetInterval();
  13. #if AUTOCAD_2009
  14.                 // В AutoCAD 2009 перегруженный вариант метода GetSamplePoints,
  15.                 // с сигнатурой
  16.                 //
  17.                 // public PointOnCurve3d[] GetSamplePoints(
  18.                 //  double fromParameter,
  19.                 //  double toParameter,
  20.                 //  double chordHeight);
  21.                 //
  22.                 // работает некорректно, выдавая ошибку в процессе своей
  23.                 // работы. Поэтому приходится использовать следующую
  24.                 // перегруженную версию метода:
  25.                 AcGe.Point3d[] pnts = curve.GetSamplePoints(
  26.                   (Int32)(interval.UpperBound * 0.5 / delta)); // Уменьшаем
  27.                 // количество точек в 2 раза
  28.                 foreach(AcGe.Point3d pt in pnts) {
  29.                   if(!boundBlk.IsBox)
  30.                     boundBlk.Set(pt, pt);
  31.                   else
  32.                     boundBlk.Extend(pt);
  33.                 }
  34. #else
  35.                 AcGe.PointOnCurve3d[] pnts = curve.GetSamplePoints(
  36.                   interval.LowerBound, interval.UpperBound, hord);
  37.                 foreach(AcGe.PointOnCurve3d pt in pnts) {
  38.                   if(!boundBlk.IsBox)
  39.                     boundBlk.Set(pt.Point, pt.Point);
  40.                   else
  41.                     boundBlk.Extend(pt.Point);
  42.                 }
  43. #endif
  44.               }
  45.               else {
  46.                 if(!boundBlk.IsBox) {
  47.                   boundBlk.Set(edge.BoundBlock.GetMinimumPoint(),
  48.                     edge.BoundBlock.GetMaximumPoint());
  49.                 }
  50.                 else {
  51.                   boundBlk.Extend(edge.BoundBlock.GetMinimumPoint());
  52.                   boundBlk.Extend(edge.BoundBlock.GetMaximumPoint());
  53.                 }
  54.               }
  55.             }
  56.           }
  57.         }
  58.         // Возвращаем вычисленный результат
  59.         minPoint = new AcGe.Point2d(boundBlk.GetMinimumPoint().X,
  60.           boundBlk.GetMinimumPoint().Y);
  61.         maxPoint = new AcGe.Point2d(boundBlk.GetMaximumPoint().X,
  62.           boundBlk.GetMaximumPoint().Y);
  63.       }
  64.     }
В случае AutoCAD 2009 обрабатываться будет гораздо больше точек, чем для др. версий AutoCAD, т.к. используем др. перегруженный вариант метода GetSamplePoints, поэтому делим их на два (по вашему совету).
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 08-06-2014, 01:34:40
Еще немного потестировал и пришел к выводу, что так будет лучше:
Код - C# [Выбрать]
  1. private void GetVisualBoundary(AcDb.Region region, double delta,
  2.   ref AcGe.Point2d minPoint, ref AcGe.Point2d maxPoint)
  3. {
  4.   using (AcGe.BoundBlock3d boundBlk = new AcGe.BoundBlock3d()) {
  5.     using (AcBr.Brep brep = new AcBr.Brep(region)) {
  6.       foreach (AcBr.Edge edge in brep.Edges) {
  7.         using (AcGe.Curve3d curve = edge.Curve) {
  8.           AcGe.ExternalCurve3d curve3d = curve as AcGe.ExternalCurve3d;
  9.           // Делать точный расчет нужно только если образующая - сплайн
  10.           // в противном случае достаточно получить BoundBlock
  11.           if (curve3d != null && curve3d.IsNurbCurve) {
  12.             using (AcGe.NurbCurve3d nurbCurve = curve3d.NativeCurve as AcGe.NurbCurve3d) {
  13.               AcGe.Interval interval = nurbCurve.GetInterval();
  14.               for (double par = interval.LowerBound; par <= interval.UpperBound; par += (delta * 2.0)) {
  15.                 AcGe.Point3d p = nurbCurve.EvaluatePoint(par);
  16.                 if (!boundBlk.IsBox)
  17.                   boundBlk.Set(p, p);
  18.                 else
  19.                   boundBlk.Extend(p);
  20.               }
  21.             }
  22.           } else {
  23.             if (!boundBlk.IsBox) {
  24.               boundBlk.Set(edge.BoundBlock.GetMinimumPoint(), edge.BoundBlock.GetMaximumPoint());
  25.             } else {
  26.               boundBlk.Extend(edge.BoundBlock.GetMinimumPoint());
  27.               boundBlk.Extend(edge.BoundBlock.GetMaximumPoint());
  28.             }
  29.           }
  30.         }
  31.       }
  32.     }
  33.     // Возвращаем вычисленный результат
  34.     minPoint = new AcGe.Point2d(boundBlk.GetMinimumPoint().X, boundBlk.GetMinimumPoint().Y);
  35.     maxPoint = new AcGe.Point2d(boundBlk.GetMaximumPoint().X, boundBlk.GetMaximumPoint().Y);
  36.   }
  37. }
По непонятной для меня причине NurbCurve3d.EvaluatePoint вычисляется на порядок быстрее, чем ExternalCurve3d.EvaluatePoint, хотя в данном случае это вроде бы одно и тоже. По результатам этот вариант в AutoCAD 2014 работает приблизительно с той же скоростью, что и вариант (недоступный в AutoCAD 2009 из-за  бага) c GetSamplePoints(startParam, endParam, hord).
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 09-06-2014, 10:56:06
По непонятной для меня причине NurbCurve3d.EvaluatePoint вычисляется на порядок быстрее, чем ExternalCurve3d.EvaluatePoint
И при этом данный вариант корректно работает в AutoCAD 2009. :) Спасибо!
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 09-06-2014, 12:37:41
AutoCAD 2009.

Обнаружилась проблема при использовании обозначенного выше варианта GetVisualBoundary: при его использовании в коде моего метода CloneAndMesh, генерируется исключение при определённых значениях параметра delta. Проблему наблюдаю при обработке одного из сплайнов (показан на скрине). Все прочие регионы отработаны корректно.

Если delta равен 0.1, то возникает исключение, показанное на скрине. Но если delta, к примеру, равен 1, то указанный сплайн режется корректно. Предыдущий вариант GetVisualBoundary от Александра Наумовича (тот, в котором я добавил директивы препроцессора для 2009-го), выдаёт такую же проблему.

В чём может быть причина возникновения eGeneralModelingFailure в данном случае? Насколько я понимаю, если даже вдруг окажется, что оба региона не пересекаются, то в этом случае у обоих IsNull будет true. А тут возникает исключение...
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 09-06-2014, 12:56:34
Мой "Топорный" (http://adn-cis.org/forum/index.php?topic=495.msg3007#msg3007) вариант отработал корректно. Сейчас попробую скомпилировать для 2009-го последний вариант Дима_, предложенный в соседней ветке и проверить на нём.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 09-06-2014, 13:29:57
И при этом данный вариант корректно работает в AutoCAD 2009. :) Спасибо!
И тоже даёт приличный выигрыш в скорости?
В чём может быть причина возникновения eGeneralModelingFailure в данном случае?
Причина в ограничениях ACIS. :( Видимо слишком близко расположенные линии должны пересечься, а точность выходит за максимальную в 1e-6.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 09-06-2014, 13:32:47
И тоже даёт приличный выигрыш в скорости?
Да, но толку-то, если это работает лишь от случая к случаю.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 09-06-2014, 13:35:24
Да, но толку-то, если это работает лишь от случая к случаю.
Как я понял сам метод работает не от случая к случаю, а постоянно и даёт более точный результат за меньшее время. Другое дело, что эта точность приводит к проблемам в другом месте твоего кода.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 09-06-2014, 13:39:41
Да, но толку-то, если это работает лишь от случая к случаю.
Как я понял сам метод работает не от случая к случаю, а постоянно и даёт более точный результат за меньшее время. Другое дело, что эта точность приводит к проблемам в другом месте твоего кода.
Да, несомненно, сам по себе метод работает. Однако в данном случае мне приходится рассматривать использование полученных им результатов в контексте работы метода BooleanOperation... :(
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 09-06-2014, 13:51:56
1. Смотрим размеры области, которую ты пытаешься сечь с шагом 0.1:
(https://adn-cis.org/forum/proxy.php?request=http%3A%2F%2Fimg-fotki.yandex.ru%2Fget%2F4907%2F7842324.1%2F0_db6d4_f13123dd_orig.png&hash=155957dff9551673693962680bff5a66)
Очевидно, что ты находишься на пределе допустимой точности.
2. Что можно посоветовать? Во-первых, не разрешать такой мелкий шаг. Во-вторых, если такое исключение возникло, то немного изменить секущий прямоугольник и попробовать с ним. Кстати, для BoundBlock3d есть специальный метод для этого: BoundBlock3d.Swell
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 10-06-2014, 11:54:23
Кстати, для BoundBlock3d есть специальный метод для этого: BoundBlock3d.Swell
Оно!
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Получить координаты границ левого нижнего и правого верхнего углов для
  3. /// визуального "GeometricExtents" региона.
  4. /// </summary>
  5. /// <param name="region">Регион, для которого следует получить координаты
  6. /// границ визуального "GeometricExtents".</param>
  7. /// <param name="delta">Предельная арифметическая погрешность вычислений.
  8. /// </param>
  9. /// <param name="minPoint">Ссылка на переменную Point2d, в которой следует
  10. /// сохранить координаты левого нижнего угла визуального
  11. /// "GeometricExtents".</param>
  12. /// <param name="maxPoint">Ссылка на переменную Point2d, в которой следует
  13. /// сохранить координаты правого верхнего угла визуального
  14. /// "GeometricExtents".</param>
  15. public static void GetVisualBoundary(this Db.Region region, double delta,
  16. ef Gm.Point2d minPoint, ref Gm.Point2d maxPoint) {
  17.   using(Gm.BoundBlock3d boundBlk = new Gm.BoundBlock3d()) {
  18.     using(Br.Brep brep = new Br.Brep(region)) {
  19.       foreach(Br.Edge edge in brep.Edges) {
  20.         using(Gm.Curve3d curve = edge.Curve) {
  21.           Gm.ExternalCurve3d curve3d = curve as Gm.ExternalCurve3d;
  22.           // Делать точный расчет нужно только если образующая - сплайн
  23.           // в противном случае достаточно получить BoundBlock
  24.           if(curve3d != null && curve3d.IsNurbCurve) {
  25.             using(Gm.NurbCurve3d nurbCurve = curve3d.NativeCurve
  26.               as Gm.NurbCurve3d) {
  27.               Gm.Interval interval = nurbCurve.GetInterval();
  28.               for(double par = interval.LowerBound; par <=
  29.                 interval.UpperBound; par += (delta * 2.0)) {
  30.                 Gm.Point3d p = nurbCurve.EvaluatePoint(par);
  31.                 if(!boundBlk.IsBox)
  32.                   boundBlk.Set(p, p);
  33.                 else
  34.                   boundBlk.Extend(p);
  35.               }
  36.             }
  37.           }
  38.           else {
  39.             if(!boundBlk.IsBox) {
  40.               boundBlk.Set(edge.BoundBlock.GetMinimumPoint(),
  41.                 edge.BoundBlock.GetMaximumPoint());
  42.             }
  43.             else {
  44.               boundBlk.Extend(edge.BoundBlock.GetMinimumPoint());
  45.               boundBlk.Extend(edge.BoundBlock.GetMaximumPoint());
  46.             }
  47.           }
  48.         }
  49.       }
  50.     }
  51.     boundBlk.Swell(delta);
  52.     // Возвращаем вычисленный результат
  53.     minPoint = new Gm.Point2d(boundBlk.GetMinimumPoint().X,
  54.       boundBlk.GetMinimumPoint().Y);
  55.     maxPoint = new Gm.Point2d(boundBlk.GetMaximumPoint().X,
  56.       boundBlk.GetMaximumPoint().Y);
  57.   }
  58. }
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 10-06-2014, 11:56:14
И как? Теперь исключение не возникает?
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 10-06-2014, 12:58:28
И как? Теперь исключение не возникает?
Нет, я же написал: "Оно!". :) Но... Возникает другая проблема: выполняю публикацию в PDF, посредством использования Window и полученных с помощью этого метода (GetVisualBoundary) границ.

Однако на выходе часть изображения усекается (см. прикреплённые файлы)... Я полагал, что изображение должно чётко вписаться, однако результат не тот, который ожидался. С чем это может быть связано?

Код публикации региона в PDF:
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Печать объекта области (region)
  3. /// </summary>
  4. /// <param name="regionId">Идентификатор области, подлежащей публикации.
  5. /// </param>
  6. /// <param name="pcsFileName">Наименование PC3 файла</param>
  7. /// <param name="mediaName">Наименование выбранного формата листа</param>
  8. /// <param name="outputFileName">Имя файла, в котором сохраняется результат
  9. /// печати.</param>
  10. public static void PlotRegion(Db.ObjectId regionId, String pcsFileName,
  11.   String mediaName, String outputFileName) {
  12.  
  13.   if(regionId.IsNull)
  14.     throw new ArgumentException("regionId.IsNull == true");
  15.   if(!regionId.IsValid)
  16.     throw new ArgumentException("regionId.IsValid == false");
  17.  
  18.   if(regionId.ObjectClass.Name != "AcDbRegion")
  19.     throw new ArgumentException("regionId.ObjectClass.Name != AcDbRegion");
  20.  
  21.   if(pcsFileName == null)
  22.     throw new ArgumentNullException("pcsFileName");
  23.   if(pcsFileName.Trim() == String.Empty)
  24.     throw new ArgumentException("pcsFileName.Trim() == String.Empty");
  25.  
  26.   if(mediaName == null)
  27.     throw new ArgumentNullException("mediaName");
  28.   if(mediaName.Trim() == String.Empty)
  29.     throw new ArgumentException("mediaName.Trim() == String.Empty");
  30.  
  31.   if(outputFileName == null)
  32.     throw new ArgumentNullException("outputFileName");
  33.   if(outputFileName.Trim() == String.Empty)
  34.     throw new ArgumentException("outputFileName.Trim() == String.Empty");
  35.  
  36.   Db.Database previewDb = Hs.WorkingDatabase;
  37.   Db.Database db = null;
  38.   Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  39.   if(doc == null || doc.IsDisposed)
  40.     return;
  41.  
  42.   Ed.Editor ed = doc.Editor;
  43.   try {
  44.     if(regionId.Database != null && !regionId.Database.IsDisposed) {
  45.       Hs.WorkingDatabase = regionId.Database;
  46.       db = regionId.Database;
  47.     }
  48.     else {
  49.       db = doc.Database;
  50.     }
  51.  
  52.     using(doc.LockDocument()) {
  53.       using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  54.         Db.Region region = tr.GetObject(regionId,
  55.         Db.OpenMode.ForRead) as Db.Region;
  56.  
  57.         Db.Extents3d extends = region.GeometricExtents;
  58.         Db.ObjectId modelId = Us.GetBlockModelSpaceId(db);
  59.         Db.BlockTableRecord model = tr.GetObject(modelId,
  60.         Db.OpenMode.ForRead) as Db.BlockTableRecord;
  61.  
  62.         Db.Layout layout = tr.GetObject(model.LayoutId,
  63.         Db.OpenMode.ForRead) as Db.Layout;
  64.  
  65.         using(Pt.PlotInfo pi = new Pt.PlotInfo()) {
  66.           pi.Layout = model.LayoutId;
  67.  
  68.           using(Db.PlotSettings ps = new Db.PlotSettings(layout.ModelType)
  69.             ) {
  70.             Db.PlotSettingsValidator psv = Db.PlotSettingsValidator
  71.               .Current;
  72.  
  73.             Gm.Point2d bottomLeft = Gm.Point2d.Origin;
  74.             Gm.Point2d topRight = Gm.Point2d.Origin;
  75.  
  76.             region.GetVisualBoundary(RegionTools.Delta, ref bottomLeft,
  77.               ref topRight);
  78.  
  79.             Db.Extents2d extents = new Db.Extents2d(bottomLeft.X,
  80.               bottomLeft.Y, topRight.X, topRight.Y);
  81.  
  82.             // Прежде чем методу SetPlotType присвоить в качестве второго
  83.             // параметра значение PlotType.Window, необходимо вызвать метод
  84.             // SetPlotWindowArea, указав через него границы Window.
  85.             // В противном случае возникнет runtime error.
  86.             psv.SetPlotWindowArea(ps, extents);
  87.             psv.SetPlotType(ps, Db.PlotType.Window);
  88.  
  89.             psv.SetUseStandardScale(ps, true);
  90.             psv.SetStdScaleType(ps, Db.StdScaleType.ScaleToFit);
  91.             psv.SetPlotCentered(ps, true);
  92.  
  93.             // We'll use the standard DWF PC3, as
  94.             // for today we're just plotting to file
  95.             psv.SetPlotConfigurationName(ps, pcsFileName, mediaName);
  96.  
  97.             // We need to link the PlotInfo to the
  98.             // PlotSettings and then validate it
  99.             pi.OverrideSettings = ps;
  100.             Pt.PlotInfoValidator piv = new Pt.PlotInfoValidator();
  101.             piv.MediaMatchingPolicy = Pt.MatchingPolicy.MatchEnabled;
  102.             piv.Validate(pi);
  103.  
  104.             // A PlotEngine does the actual plotting
  105.             // (can also create one for Preview)
  106.             if(Pt.PlotFactory.ProcessPlotState == Pt.ProcessPlotState
  107.               .NotPlotting) {
  108.               Pt.PlotEngine pe = Pt.PlotFactory.CreatePublishEngine();
  109.               using(pe) {
  110.                 // Create a Progress Dialog to provide info
  111.                 // and allow thej user to cancel
  112.  
  113.                 using(Pt.PlotProgressDialog ppd =
  114.                   new Pt.PlotProgressDialog(false, 1, true)) {
  115.                   ppd.set_PlotMsgString(
  116.                   Pt.PlotMessageIndex.DialogTitle, "Custom Plot Progress");
  117.  
  118.                   ppd.set_PlotMsgString(
  119.                     Pt.PlotMessageIndex.CancelJobButtonMessage,
  120.                     "Cancel Job");
  121.  
  122.                   ppd.set_PlotMsgString(
  123.                   Pt.PlotMessageIndex.CancelSheetButtonMessage,
  124.                   "Cancel Sheet");
  125.  
  126.                   ppd.set_PlotMsgString(
  127.                   Pt.PlotMessageIndex.SheetSetProgressCaption,
  128.                   "Sheet Set Progress");
  129.  
  130.                   ppd.set_PlotMsgString(
  131.                     Pt.PlotMessageIndex.SheetProgressCaption,
  132.                    "Sheet Progress");
  133.  
  134.                   ppd.LowerPlotProgressRange = 0;
  135.                   ppd.UpperPlotProgressRange = 100;
  136.                   ppd.PlotProgressPos = 0;
  137.  
  138.                   // Let's start the plot, at last
  139.                   ppd.OnBeginPlot();
  140.                   ppd.IsVisible = true;
  141.                   pe.BeginPlot(ppd, null);
  142.  
  143.                   // We'll be plotting a single document
  144.                   pe.BeginDocument(pi, doc.Name, null, 1, true,
  145.                     // Let's plot to file
  146.                    outputFileName);
  147.                   // Which contains a single sheet
  148.                   ppd.OnBeginSheet();
  149.                   ppd.LowerSheetProgressRange = 0;
  150.                   ppd.UpperSheetProgressRange = 100;
  151.                   ppd.SheetProgressPos = 0;
  152.                   Pt.PlotPageInfo ppi = new Pt.PlotPageInfo();
  153.                   pe.BeginPage(ppi, pi, true, null);
  154.                   pe.BeginGenerateGraphics(null);
  155.                   pe.EndGenerateGraphics(null);
  156.  
  157.                   // Finish the sheet
  158.                   pe.EndPage(null);
  159.                   ppd.SheetProgressPos = 100;
  160.                   ppd.OnEndSheet();
  161.  
  162.                   // Finish the document
  163.                   pe.EndDocument(null);
  164.  
  165.                   // And finish the plot
  166.                   ppd.PlotProgressPos = 100;
  167.                   ppd.OnEndPlot();
  168.                   pe.EndPlot(null);
  169.                 }
  170.               }
  171.             }
  172.             else {
  173.               ed.WriteMessage("\nAnother plot is in progress.");
  174.             }
  175.           }
  176.         }
  177.         tr.Commit();
  178.       }
  179.     }
  180.   }
  181.   finally {
  182.     Hs.WorkingDatabase = previewDb;
  183.   }
  184. }

Команда, использующая обозначенный выше код:

Код - C# [Выбрать]
  1. /// <summary>
  2. /// Опубликовать указанную пользователем область (объект Region) в PDF
  3. /// формат.
  4. /// </summary>
  5. [Rt.CommandMethod("plotRegion", Rt.CommandFlags.Modal)]
  6. public void PlotRegion() {
  7.   Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  8.   if(doc == null || doc.IsDisposed)
  9.     return;
  10.  
  11.   Ed.Editor ed = doc.Editor;
  12.   Db.Database db = doc.Database;
  13.  
  14.   using(doc.LockDocument()) {
  15.     Ed.PromptEntityOptions peo = new Ed.PromptEntityOptions(
  16.      "Укажите область (объект Region), подлежащую публикации в PDF формат."
  17.      );
  18.  
  19.     peo.SetRejectMessage("Указанный объект не является областью (region)."
  20.       );
  21.     peo.AddAllowedClass(typeof(Db.Region), false);
  22.  
  23.     Ed.PromptEntityResult per = ed.GetEntity(peo);
  24.  
  25.     if(per.Status != Ed.PromptStatus.OK) {
  26.       ed.WriteMessage("\nВыполнение команды прервано.\n");
  27.       return;
  28.     }
  29.  
  30.     Db.ObjectId regionId = per.ObjectId;
  31.  
  32.     Microsoft.Win32.SaveFileDialog saveFileDialog = new Microsoft.Win32
  33.       .SaveFileDialog();
  34.     saveFileDialog.Title =
  35.       "Публикация выбранной области (Region) в файл формата PDF";
  36.     saveFileDialog.Filter = "PDF-файлы|*.pdf";
  37.     bool? result = saveFileDialog.ShowDialog();
  38.  
  39.     if(!result.HasValue || !result.Value) {
  40.       ed.WriteMessage("\nВыполнение команды прервано, т.к. не был указан "
  41.         + "PDF-файл.");
  42.       return;
  43.     }
  44.  
  45.     String pdfFileName = saveFileDialog.FileName;
  46.  
  47.     RegionTools.PlotRegion(regionId, "DWG To PDF.pc3",
  48.       "ISO_A4_(210.00_x_297.00_MM)", pdfFileName);
  49.  
  50.     ed.WriteMessage("\nВыбранная Вами область опубликована в файл " +
  51.           "\"{0}\"\n", pdfFileName);
  52.   }
  53. }
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 10-06-2014, 13:06:07
С чем это может быть связано?
Поля виртуального принтера...
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 10-06-2014, 13:07:50
Но ведь я в коде установил опции центрирования и вписывания:
Код - C# [Выбрать]
  1. psv.SetStdScaleType(ps, Db.StdScaleType.ScaleToFit);
  2. psv.SetPlotCentered(ps, true);
Почему этого не происходит? Если публикацию выполняю вручную, устанавливая эти опции, то всё публикуется корректно.
Название: Re: Нарезка 3D тел
Отправлено: Александр Ривилис от 10-06-2014, 13:13:13
Попробуй тоже самое проделать вручную, т.е. задать те же самые параметры для окна и напечатать в PDF.
Название: Re: Нарезка 3D тел
Отправлено: Андрей Бушман от 10-06-2014, 13:16:38
Попробуй тоже самое проделать вручную, т.е. задать те же самые параметры для окна и напечатать в PDF.
Я пробовал. Выше в сообщении это обозначено.