Нарезка 3D тел

Автор Тема: Нарезка 3D тел  (Прочитано 27475 раз)

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

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Нарезка 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 тоже перепишу - будет два параметра: точка и вектор (так будет проще и "читабельней").
« Последнее редактирование: 30-01-2014, 20:43:35 от Андрей Бушман »

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Нарезка 3D тел
« Ответ #1 : 31-01-2014, 10:20:45 »
Цитировать
Как проверить, попадает ли часть нужного 3D солида в сечение (очень уж хочется избавиться от Exception...)?
При нарезке строк на столбцы проверять лежит ли координата X сечения между габаритными координатами X разрезаемого тела (то есть взять габариты тела перед нарезкой - т.к. они меняются на каждой итерации) т.е.
ext = rowsolid.GeometricExtents;
ext.MinPoint.X<X<ext.MaxPoint.X // X - координата через которую проходит сечение (от которой оно строится)
и если условие не совпадает, то резать там уже не надо (все уже украдено до Вас  :) ).
Аналогично, только для Y координаты, проверять при нарезке строк (беря обновленные габариты после каждого "обрезания") т.к. исходный Region может представлять из себя 2 непересекающихся прямоугольника (один над другим), но при этом быть одним целым (как материал летающей тарелки у братьев Стругацких - две пластины закрепленные на пустоте между ними).

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #2 : 31-01-2014, 10:35:26 »
При нарезке строк на столбцы проверять лежит ли координата X сечения между габаритными координатами X разрезаемого тела
Да, спасибо, пока ехал с утра на работу, что называется "на свежую голову" прокручивал в голове код и тоже обнаружил эту ошибку, но вы меня уже опередили с ответом :).

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #3 : 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. }
« Последнее редактирование: 01-02-2014, 10:10:56 от Андрей Бушман »

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Нарезка 3D тел
« Ответ #4 : 31-01-2014, 17:29:58 »
Да со сплайнами была "беда" c габаритами (помню по vla-getboundingbox), по моему они корректно работали с IntersectWith, но ИХМО это метод еще более извратный чем проверка ошибки  :(

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #5 : 31-01-2014, 17:34:19 »
Да со сплайнами была "беда" c габаритами (помню по vla-getboundingbox), по моему они корректно работали с IntersectWith, но ИХМО это метод еще более извратный чем проверка ошибки 
Тут ещё нюанс ведь в том, что в коде я работаю не непосредственно со сплайнами, а с регионами созданными на их основе. Если  будет чертёж передан со стороны, то х.з. на основе чего они (регионы) там были созданы (на основе сплайна или же на основе чего-то другого). Получается, что регионы наследуют значение этого свойства от сплайнов, а затем 3D тела, в свою очередь, наследуют значение этого свойства от регионов, на основе которых эти тела создавались.

P.S. Проектировщики говорят, что у них вряд ли будут регионы, созданные посредством сплайнов. Тем не менее всё же хочу разобраться с проблемой, т.к. если есть даже минимальный шанс, наступления в гуано (т.е. в качестве исходных данных получить регионы, созданные с помощью сплайнов), то это, к сожалению, обязательно произойдёт (закон жизни).
« Последнее редактирование: 01-02-2014, 10:17:34 от Андрей Бушман »

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Нарезка 3D тел
« Ответ #6 : 31-01-2014, 18:31:00 »
Тут ещё нюанс ведь в том, что в коде я работаю не непосредственно со сплайнами, а с регионами созданными на их основе. Если  будет чертёж передан со стороны, то х.з. на основе чего они (регионы) там были созданы (на основе сплайна или же на основе чего-то другого).
Габариты REGION ты можешь получить еще и при помощи BREP .NET API, хотя возможно проще будет расчленить регион (Entity.Explode) - в данном случае ты получишь SPLINE. Точные его габариты методом GeometricExtents ты не получишь - причина в том, что точное определение габаритов для сплайна операция достаточно затратная по времени. Поэтому они пошли простым путем - вычисляют габариты по опорным точкам. Если тебе нужны точные координаты, то альтернативный вариант - с мелким шагом (например, с шагом (Curve.EndParam - Curve.StartParam) * 1e-6) получай координаты точки на сплайне (Curve.GetPointAtParameter) и получай габариты.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #7 : 31-01-2014, 19:13:25 »
Сделаю, в понедельник (сегодня уже домой пора).

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Нарезка 3D тел
« Ответ #8 : 31-01-2014, 19:18:12 »
Функцию для довольно быстрого и точного определения границ сплайна с использованием метода Ньютона я делал на лиспе, лисп-код в этом сообщении: http://forum.dwg.ru/showpost.php?p=466002&postcount=51. Там же есть функция для его тестирования (рисования прямоугольничков вокруг сплайнов :)). В этой же теме есть еще варианты поиска границ сплайна.
Если результат устроит, могу попробовать вспомнить алгоритм поиска.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #9 : 31-01-2014, 19:24:25 »
Функцию для довольно быстрого и точного определения границ сплайна с использованием метода Ньютона я делал на лиспе, лисп-код в этом сообщении: http://forum.dwg.ru/showpost.php?p=466002&postcount=51. Там же есть функция для его тестирования (рисования прямоугольничков вокруг сплайнов . В этой же теме есть еще варианты поиска границ сплайна.
Если результат устроит, могу попробовать вспомнить алгоритм поиска.
В понедельник можно будет сравнить этот способ с тем, который я нацарапаю. Добавь (если тебе не сложно) в обозначенный LISP пару строк кода, сообщающих о времени выполнения, чтобы можно было более точно сравнивать в т.ч. и по временным затратам.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Нарезка 3D тел
« Ответ #10 : 31-01-2014, 19:52:15 »
Или может есть какой-то иной способ избавиться от генерации Exception?
К сожалению метода TrySlice (по аналогии с Database.TryGetObjectId, который тебя когда-то спасал) нет. Так что я вижу три варианта:
1) try/catch
2) BREP .NET API и проверка есть пересечение или нет
3) P/Invoke native метод из ObjectARX <---- если всё остальное не поможет, то скажи. Постараюсь помочь с этим
« Последнее редактирование: 31-01-2014, 21:15:03 от Александр Ривилис »
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #11 : 27-05-2014, 22:43:55 »
Проблему с областями на основе сплайнов решил путём её разрезки на более мелкие области, вместо манипуляций с солидами, описанных выше. Полученные области, при желании, в дальнейшем можно выдавливать в солиды.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #12 : 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. }

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Нарезка 3D тел
« Ответ #13 : 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.     }

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Нарезка 3D тел
« Ответ #14 : 03-06-2014, 15:12:50 »
А что если вместо edge.BoundBlock использовать edge.Curve.BoundBlock или edge.Curve.OrthoBoundBlock ?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение