Задача стояла следующая: необходимо для указанных областей (regions) создать их копию, нарезанную либо на прямоугольники заданного размера, либо на заданное количество строк и столбцов. При этом исходные области (regions), подлежащие разрезке могут быть абсолютно любой, произвольной формы: например, созданными на основании сплайнов и содержащими в себе отверстия.
В данной заметке приведён исходный код разрезки областей различными способами, а так же предоставлен дополнительный программный функционал, который может быть полезен при работе с областями в ходе решения иных задач.
В процессе тестирования производилась разрезка различных областей, в т.ч. и созданных при помощи сплайнов (на скрине выделены коричневым цветом):
Код определяет ряд пользовательских команд:
1. Команда AcadBoundaryRegion создаёт жёлтую рамку вокруг регионов. Эта рамка показывает визуальные границы BoundaryBox для этих примитивов/ Габариты берутся именно те, которые AutoCAD API показывает программистам в свойствах примитивов:
1. Команда AcadBoundaryRegion создаёт жёлтую рамку вокруг регионов. Эта рамка показывает визуальные границы BoundaryBox для этих примитивов/ Габариты берутся именно те, которые AutoCAD API показывает программистам в свойствах примитивов:
Команду AcadBoundaryRegion я использую при написании программ, чтобы визуально увидеть границы, которые AutoCAD возвращает для запрошенного примитива, и тем самым понять, почему в той или иной ситуации мой код делает не совсем то, что я от него хотел. Обратите внимание на то, что области, выполненные из сплайнов, не чётко вписываются в эти границы. Однако в процессе программирования нам порой нужно точно знать габариты нашего примитива. Для этого была написана следующая команда.
2. Команда BoundaryRegion показывает корректные визуальные границы наших областей.
2. Команда BoundaryRegion показывает корректные визуальные границы наших областей.
Как видим, созданные с помощью BoundaryRegion границы имеют зелёный цвет, для их визуального отличия от границ, созданных при помощи команды AcadBoundaryRegion.
Эту команду я использую для того, чтобы визуально убедиться в том, что вычисленные мною габариты верны.
3. Команда MeshRegionThroughCellSize нарезает копии наших исходных областей на ячейки, заданного размера:
Можно задавать произвольное значение высоты и ширины ячеек.
4. Команда MeshRegionThroughRowsAndColumnsCount режет копии наших областей на указанное количество строк и столбцов:
Можно задавать произвольное значение высоты и ширины ячеек.
4. Команда MeshRegionThroughRowsAndColumnsCount режет копии наших областей на указанное количество строк и столбцов:
Используя команду MeshRegionThroughRowsAndColumnsCount можно нарезать области произвольным образом, например так:
Скорость работы команды MeshRegionThroughCellSize и MeshRegionThroughRowsAndColumnsCount зависит от количества областей, которые должны получиться на выходе, а так же от значения предельно допустимой абсолютной погрешности, указанной пользователем.
Исходный код программы разбит на несколько файлов. Далее приведено их полное содержимое.
Исходный код программы разбит на несколько файлов. Далее приведено их полное содержимое.
Файл PluginEnvironment.cs
/* PluginEnvironment.cs * © Андрей Бушман, 2014 * Централизованное хранение общей информации, используемой в коде различных * методов. Т.о. при необходимости достаточно будет поменять её в одном месте * и все изменения автоматически подхватятся. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Bushman.CAD { public static class PluginEnvironment { /// <summary> /// Наименование группы команд AutoCAD, определённых в составе данной /// сборки. /// </summary> public const String DefaultCommandGroup = "Bushman"; } }
Файл ExtensionApplication.cs
/* ExtensionApplication.cs * © Андрей Бушман, 2014 * Информация, отправляемая в консоль ActiveDocument при загрузке данной * сборки. Если в текущий момент ActiveDocument равен null, то информация будет * выведена в консоль первого открытого в дальнейшем документа. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Reflection; #if AUTOCAD using cad = Autodesk.AutoCAD.ApplicationServices.Application; using Ap = Autodesk.AutoCAD.ApplicationServices; using Db = Autodesk.AutoCAD.DatabaseServices; using Ed = Autodesk.AutoCAD.EditorInput; using Rt = Autodesk.AutoCAD.Runtime; using Gm = Autodesk.AutoCAD.Geometry; using Wn = Autodesk.AutoCAD.Windows; using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices; using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices; #endif namespace Bushman.CAD { public sealed class ExtensionApplication : Rt.IExtensionApplication { #region IExtensionApplication Members public void Initialize() { Ap.Document doc = cad.DocumentManager.MdiActiveDocument; if(doc == null) { cad.DocumentManager.DocumentActivated += DocMng_DocActivated; return; } Db.Database db = doc.Database; Ed.Editor ed = doc.Editor; WriteAboutInfo(ed); } public void Terminate() { } #endregion internal static void WriteAboutInfo(Ed.Editor ed) { try { String title = GetAssemblyAttribute<AssemblyTitleAttribute>( a => a.Title); String copyright = GetAssemblyAttribute<AssemblyCopyrightAttribute>( a => a.Copyright); // String version = GetAssemblyAttribute<AssemblyVersionAttribute>( // a => a.Version); String description = GetAssemblyAttribute<AssemblyDescriptionAttribute>( a => a.Description); ed.WriteMessage("\n{0}\n{1}. {2}\n{3}\n", Assembly.GetExecutingAssembly().Location, title, copyright, description); } catch(Exception ex) { ed.WriteMessage("{0}\n", ex.Message); } } void DocMng_DocActivated(object sender, Ap.DocumentCollectionEventArgs e) { cad.DocumentManager.DocumentActivated -= DocMng_DocActivated; WriteAboutInfo(e.Document.Editor); } internal static String GetAssemblyAttribute<T>(Func<T, String> value) where T : Attribute { T attribute = (T)Attribute.GetCustomAttribute( Assembly.GetExecutingAssembly(), typeof(T)); return value.Invoke(attribute); } } }
Файл About.cs
/* About.cs * © Андрей Бушман, 2014 * Команда, предоставляющая пользователю общую информацию о библиотеке. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using BUc = Bushman.CAD; #if AUTOCAD using cad = Autodesk.AutoCAD.ApplicationServices.Application; using Ap = Autodesk.AutoCAD.ApplicationServices; using Db = Autodesk.AutoCAD.DatabaseServices; using Ed = Autodesk.AutoCAD.EditorInput; using Rt = Autodesk.AutoCAD.Runtime; using Gm = Autodesk.AutoCAD.Geometry; using Wn = Autodesk.AutoCAD.Windows; using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices; using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices; #endif [assembly: Rt.CommandClass(typeof(Bushman.CAD.Commands.About))] namespace Bushman.CAD.Commands { public class About { [Rt.CommandMethod(BUc.PluginEnvironment.DefaultCommandGroup, "About", Rt.CommandFlags.Modal)] public void AboutCommand() { Ap.Document doc = cad.DocumentManager.MdiActiveDocument; if(doc == null || doc.IsDisposed) return; BUc.ExtensionApplication.WriteAboutInfo(doc.Editor); } } }
Файл RegionTools.cs
/* RegionTools.cs * © Андрей Бушман, 2014 * Набор методов, предназначенных для создания разрезанной на части копии * исходной области (region). */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; #if AUTOCAD using cad = Autodesk.AutoCAD.ApplicationServices.Application; using Ap = Autodesk.AutoCAD.ApplicationServices; using Db = Autodesk.AutoCAD.DatabaseServices; using Ed = Autodesk.AutoCAD.EditorInput; using Rt = Autodesk.AutoCAD.Runtime; using Gm = Autodesk.AutoCAD.Geometry; using Wn = Autodesk.AutoCAD.Windows; using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices; using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices; using Br = Autodesk.AutoCAD.BoundaryRepresentation; #endif namespace Bushman.CAD.Extensions { /// <summary> /// Статический класс, предоставляющий дополнительный набор методов для /// работы с объектами Region. /// </summary> public static class RegionTools { static Double dx = 10.0; static Double dy = 10.0; /// <summary> /// Предельно допустимое значение ширины ячейки. Свойство влияет на работу /// команды MeshRegionThroughCellSize. /// </summary> public static Double CellWidth { get { return dx; } set { if(dx <= 0) throw new ArgumentException(value.ToString()); dx = value; } } /// <summary> /// Предельно допустимое значение высоты ячейки. Свойство влияет на работу /// команды MeshRegionThroughCellSize. /// </summary> public static Double CellHeight { get { return dy; } set { if(dy <= 0) throw new ArgumentException(value.ToString()); dy = value; } } static Int32 rowsCount = 1; static Int32 columnsCount = 1; /// <summary> /// Количество строк в нарезаемой сетке. Свойство влияет на работу /// команды MeshRegionThroughRowsAndColumnsCount. /// </summary> public static Int32 RowsCount { get { return rowsCount; } set { if(rowsCount <= 0) throw new ArgumentException(value.ToString()); rowsCount = value; } } /// <summary> /// Количество колонок в нарезаемой сетке. Свойство влияет на работу /// команды MeshRegionThroughRowsAndColumnsCount. /// </summary> public static Int32 ColumnsCount { get { return columnsCount; } set { if(columnsCount <= 0) throw new ArgumentException(value.ToString()); columnsCount = value; } } // Предельная абсолютная погрешность точности измерения визуальных границ // примитива. static Double delta = 0.1; /// <summary> /// Предельная абсолютная погрешность точности измерения визуальных границ /// "GeometricExtents" региона. Чем меньше погрешность, тем точнее граница, /// но тем дольше она высчитывается. Свойство влияет на работу команды /// BoundaryRegion. /// </summary> public static Double Delta { get { return delta; } set { if(delta <= 0) throw new ArgumentException(value.ToString()); delta = value; } } /// <summary> /// Настройка базы данных чертежа: установка нужных единиц измерения, /// системы кооринат и т.п. /// </summary> /// <param name="db">Целевая база данных</param> internal static void SetDatabaseDefaultSettings(Db.Database db) { if(db == null) throw new ArgumentNullException("db"); if(db.IsDisposed) throw new ArgumentException("db.IsDisposed == true"); // Устанавливаем текущей метрическую систему измерения и миллиметры в // качестве используемых единиц измерения db.Measurement = Db.MeasurementValue.Metric; db.Insunits = Db.UnitsValue.Millimeters; // Устанавливаем текущей мировую систему координат db.WorldUcsBaseOrigin(Db.OrthographicView.TopView); } /// <summary> /// Создать прямоугольную область (Region), представляющую собой "ячейку" /// нарезаемой сетки. /// </summary> /// <param name="topLeftCorner">Координата верхнего левого угла "ячейки". /// </param> /// <param name="dx">Длина "ячейки".</param> /// <param name="dy">Высота "ячейки".</param> /// <returns>Возвращается новый объект Region. Программист должен либо /// добавить полученный объект в Database, либо по завершению работы с ним /// не забыть его уничтожить, вызвав метод Dispose (дабы избежать утечки /// памяти).</returns> public static Db.Region GetRegionCell(Gm.Point2d topLeftCorner, Double dx, Double dy) { #region Проверка входных данных if(dx <= 0) throw new ArgumentException("dx <= 0"); if(dy <= 0) throw new ArgumentException("dy <= 0"); #endregion Db.Region regCell = null; using(Db.Polyline pline = new Db.Polyline()) { pline.SetDatabaseDefaults(); pline.AddVertexAt(0, new Gm.Point2d(topLeftCorner.X, topLeftCorner.Y), 0, 0, 0); pline.AddVertexAt(1, new Gm.Point2d(topLeftCorner.X + dx, topLeftCorner.Y), 0, 0, 0); pline.AddVertexAt(2, new Gm.Point2d(topLeftCorner.X + dx, topLeftCorner.Y - dy), 0, 0, 0); pline.AddVertexAt(3, new Gm.Point2d(topLeftCorner.X, topLeftCorner.Y - dy), 0, 0, 0); pline.Closed = true; regCell = Db.Region.CreateFromCurves(new Db.DBObjectCollection() { pline }).Cast<Db.Region>().First(); } return regCell; } /// <summary> /// Получить массив областей (region) путём разреки копии указанной /// области на отдельные прямоугольные (по возможности) части. /// </summary> /// <param name="region">Область, копия которой будет создана и затем /// разрезана на части.</param> /// <param name="delta">Предельная абсолютная погрешность точности /// измерения визуальных границ региона. Чем меньше погрешность, тем точнее /// граница, но тем дольше она высчитывается.</param> /// <param name="dx">Максимальная длина получаемых сегментов.</param> /// <param name="dy">Максимальная высота получаемых сегментов.</param> /// <returns>Возвращается массив объектов Region. Программист должен либо /// добавить их в Database, либо по завершению своей работы с ними не /// забыть уничтожить, вызвав для каждого элемента его метод Dispose (дабы /// избежать утечки памяти).</returns> public static Db.Region[] CloneAndMesh(this Db.Region region, Double delta, Double dx, Double dy) { #region Проверка входных данных if(region == null) throw new ArgumentNullException("region"); if(region.IsDisposed) throw new ArgumentException("region.IsDisposed == true"); if(dx < 0) throw new ArgumentException("dx < 0"); if(dy < 0) throw new ArgumentException("dy < 0"); #endregion List<Db.Region> result = new List<Db.Region>(); Gm.Point2d minPoint = Gm.Point2d.Origin; Gm.Point2d maxPoint = Gm.Point2d.Origin; region.GetVisualBoundary(delta, ref minPoint, ref maxPoint); #region Если исходный регион не превышает ячейку, то резать нечего Double length = maxPoint.X - minPoint.X; Double height = maxPoint.Y - minPoint.Y; // Если исходный регион меньше ячейки нарезаемой сетки, то возвращаем // пустой массив, т.к. разрезать нечего if(dx >= length && dy >= height) return result.ToArray(); #endregion for(Double offsetY = height; offsetY > -dy; offsetY -= dy) { Db.Region regionClone = region.Clone() as Db.Region; Gm.Point2d topLeftCorner = new Gm.Point2d(minPoint.X, minPoint.Y + offsetY); Db.Region regionRow = GetRegionCell(topLeftCorner, length, dy); regionClone.BooleanOperation(Db.BooleanOperationType.BoolIntersect, regionRow); // Если значения обоих свойств regionClone.IsNull и regionRow.IsNull // равны true, значит пересечение отсутствует // Если логическая операция выполнена успешно, то её результат // сохраняем, т.к. его ещё предстоит резать вертикально if(!regionClone.IsNull && regionRow.IsNull) { // Теперь полученную горионтальную часть режем вертикально if(length > dx) { Db.Region row = regionClone; Double rowLength = maxPoint.X - minPoint.X; for(Double offsetX = rowLength - dx; offsetX > -dx; offsetX -= dx) { Db.Region rowClone = row.Clone() as Db.Region; topLeftCorner = new Gm.Point2d(minPoint.X + offsetX, minPoint.Y + offsetY); Db.Region regionCell = GetRegionCell(topLeftCorner, dx, dy); regionCell.LayerId = region.LayerId; rowClone.BooleanOperation( Db.BooleanOperationType.BoolIntersect, regionCell); // Если логическая операция выполнена успешно, то её результат // сохраняем, т.к. его ещё предстоит резать вертикально if(!rowClone.IsNull && regionCell.IsNull) { result.AddRange(ExplodeIfUnitedRegions(rowClone)); if(!regionCell.IsDisposed) regionCell.Dispose(); } // Предотвращаем утечку памяти else { if(!regionCell.IsDisposed) regionCell.Dispose(); if(!rowClone.IsDisposed) rowClone.Dispose(); } } row.Dispose(); } else { result.AddRange(ExplodeIfUnitedRegions(regionClone)); } if(!regionRow.IsDisposed) regionRow.Dispose(); } // Предотвращаем утечку памяти else { if(!regionRow.IsDisposed) regionRow.Dispose(); if(!regionClone.IsDisposed) regionClone.Dispose(); } } return result.ToArray(); } /// <summary> /// Метод получает на входе объект Region и проверяет, состоит ли он из /// нескольких объединённых областей. Если состоит, то эти области /// извлекаются и записываются в массив объектов Region, после чего объект /// исходной области, переданной методу в качестве параметра, уничтожается. /// /// Если исходная область не состоит из нескольких объединённых частей, то /// объект этой области добавляется в массив объектов Region. /// </summary> /// <param name="region">Исходная область, подлежащая обработке.</param> /// <returns>Возвращается массив областей. Как минимум, в массиве будет /// присутствовать хотя бы один объект.</returns> public static Db.Region[] ExplodeIfUnitedRegions(Db.Region region) { if(region == null) throw new ArgumentNullException("region"); if(region.IsDisposed) throw new ArgumentException("region.IsDisposed == true"); List<Db.Region> result = new List<Db.Region>(); using(Br.Brep brep = new Br.Brep(region)) { if(brep.Complexes != null && brep.Complexes.Count() > 1) { using(Db.DBObjectCollection explodeResult = new Db.DBObjectCollection()) { region.Explode(explodeResult); foreach(Db.DBObject item in explodeResult) { Db.Region n = (Db.Region)item; n.LayerId = region.LayerId; result.Add(n); } } if(!region.IsDisposed) region.Dispose(); } else result.Add(region); } return result.ToArray(); } // Код метода GetVisualBoundary был написан Александром Ривилисом здесь: // http://adn-cis.org/forum/index.php?topic=495.msg3043#msg3043 /// <summary> /// Получить координаты границ левого нижнего и правого верхнего углов для /// визуального "GeometricExtents" региона. /// </summary> /// <param name="region">Регион, для которого следует получить координаты /// границ визуального "GeometricExtents".</param> /// <param name="delta">Предельная арифметическая погрешность вычислений. /// </param> /// <param name="minPoint">Ссылка на переменную Point2d, в которой следует /// сохранить координаты левого нижнего угла визуального /// "GeometricExtents".</param> /// <param name="maxPoint">Ссылка на переменную Point2d, в которой следует /// сохранить координаты правого верхнего угла визуального /// "GeometricExtents".</param> public static void GetVisualBoundary(this Db.Region region, Double delta, ref Gm.Point2d minPoint, ref Gm.Point2d maxPoint) { using(Gm.BoundBlock3d boundBlk = new Gm.BoundBlock3d()) { using(Br.Brep brep = new Br.Brep(region)) { foreach(Br.Edge edge in brep.Edges) { using(Gm.Curve3d curve = edge.Curve) { Gm.ExternalCurve3d curve3d = curve as Gm.ExternalCurve3d; // Делать точный расчет нужно только если образующая - сплайн // в противном случае достаточно получить BoundBlock if(curve3d != null && curve3d.IsNurbCurve) { using(Gm.NurbCurve3d nurbCurve = curve3d.NativeCurve as Gm.NurbCurve3d) { Gm.Interval interval = nurbCurve.GetInterval(); for(double par = interval.LowerBound; par <= interval.UpperBound; par += (delta * 2.0)) { Gm.Point3d p = nurbCurve.EvaluatePoint(par); if(!boundBlk.IsBox) boundBlk.Set(p, p); else boundBlk.Extend(p); } } } else { if(!boundBlk.IsBox) { boundBlk.Set(edge.BoundBlock.GetMinimumPoint(), edge.BoundBlock.GetMaximumPoint()); } else { boundBlk.Extend(edge.BoundBlock.GetMinimumPoint()); boundBlk.Extend(edge.BoundBlock.GetMaximumPoint()); } } } } } // Возвращаем вычисленный результат minPoint = new Gm.Point2d(boundBlk.GetMinimumPoint().X, boundBlk.GetMinimumPoint().Y); maxPoint = new Gm.Point2d(boundBlk.GetMaximumPoint().X, boundBlk.GetMaximumPoint().Y); } } } }
Файл RegionCommands.cs
/* RegionCommands.cs * © Андрей Бушман, 2014 * Дополнительные команды CAD для работы с объектами областей (region). */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using BUc = Bushman.CAD; using BUx = Bushman.CAD.Extensions; // для возможности использования методов расширений using Bushman.CAD.Extensions; #if AUTOCAD using cad = Autodesk.AutoCAD.ApplicationServices.Application; using Ap = Autodesk.AutoCAD.ApplicationServices; using Db = Autodesk.AutoCAD.DatabaseServices; using Ed = Autodesk.AutoCAD.EditorInput; using Rt = Autodesk.AutoCAD.Runtime; using Gm = Autodesk.AutoCAD.Geometry; using Wn = Autodesk.AutoCAD.Windows; using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices; using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices; using Br = Autodesk.AutoCAD.BoundaryRepresentation; #endif [assembly: Rt.CommandClass(typeof(Bushman.CAD.Commands.RegionCommands))] namespace Bushman.CAD.Commands { public sealed class RegionCommands { /// <summary> /// Команда CAD, при помощи которой формируется "нарезка" копий указанных /// областей (Regions) на прямоугольные (по возможности) части, не /// превышающие размеров dx и dy, заданных пользователем. /// </summary> [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup, "MeshRegionThroughCellSize", Rt.CommandFlags.Modal)] public void MeshRegionThroughCellSize() { Ap.Document doc = cad.DocumentManager.MdiActiveDocument; if(doc == null) return; Db.Database db = doc.Database; BUx.RegionTools.SetDatabaseDefaultSettings(db); Ed.Editor ed = doc.Editor; Db.TypedValue[] tv = new Db.TypedValue[1]; tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION"); Ed.SelectionFilter filter = new Ed.SelectionFilter(tv); Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions(); pso.MessageForAdding = "Выберите области (Regions) для добавления " + "их в набор"; pso.MessageForRemoval = "Выберите области (Regions), подлежащие удалению из набора"; pso.SingleOnly = false; pso.RejectObjectsFromNonCurrentSpace = true; pso.AllowDuplicates = false; Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter); if(psr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n"); return; } else { ed.WriteMessage("\nВсего выбрано областей (Regions): {0}\n", psr.Value.Count); } Db.ObjectId[] ids = psr.Value.GetObjectIds(); Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions("dx"); pdo.AllowNegative = false; pdo.AllowZero = false; pdo.AllowNone = false; pdo.DefaultValue = RegionTools.CellWidth; pdo.UseDefaultValue = true; Ed.PromptDoubleResult pdr = ed.GetDouble(pdo); if(pdr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Выполнение команды прервано\n"); return; } RegionTools.CellWidth = pdr.Value; ed.WriteMessage(Environment.NewLine); pdo.Message = "dy"; pdo.DefaultValue = RegionTools.CellHeight; pdr = ed.GetDouble(pdo); if(pdr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Выполнение команды прервано\n"); return; } RegionTools.CellHeight = pdr.Value; pdo = new Ed.PromptDoubleOptions( "Предельная абсолютная погрешность"); pdo.AllowNegative = false; pdo.AllowZero = false; pdo.AllowNone = false; pdo.DefaultValue = RegionTools.Delta; pdo.UseDefaultValue = true; pdr = ed.GetDouble(pdo); if(pdr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Выполнение команды прервано\n"); return; } RegionTools.Delta = pdr.Value; ed.WriteMessage(Environment.NewLine); using(Db.Transaction tr = db.TransactionManager.StartTransaction()) { Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead); Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[ Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite); DateTime start = DateTime.Now; Int32 count = 0; foreach(Db.ObjectId id in ids) { Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead) as Db.Region; Db.Region[] regions = region.CloneAndMesh(RegionTools.Delta, RegionTools.CellWidth, RegionTools.CellHeight); count += regions.Length; foreach(Db.Region item in regions) { btr.AppendEntity(item); tr.AddNewlyCreatedDBObject(item, true); } } tr.Commit(); DateTime end = DateTime.Now; ed.WriteMessage( "\nСоздано областей (region): {0}\nЗатраченное время: {1}\n", count, end - start); } } /// <summary> /// Команда CAD, при помощи которой формируется "нарезка" копий указанных /// областей (Regions) на прямоугольные (по возможности) части согласно /// указанному количеству строк и столбцов. /// </summary> [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup, "MeshRegionThroughRowsAndColumnsCount", Rt.CommandFlags.Modal)] public void MeshRegionThroughRowsAndColumnsCount() { Ap.Document doc = cad.DocumentManager.MdiActiveDocument; if(doc == null) return; Db.Database db = doc.Database; BUx.RegionTools.SetDatabaseDefaultSettings(db); Ed.Editor ed = doc.Editor; Db.TypedValue[] tv = new Db.TypedValue[1]; tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION"); Ed.SelectionFilter filter = new Ed.SelectionFilter(tv); Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions(); pso.MessageForAdding = "Выберите области (Regions) для добавления " + "их в набор"; pso.MessageForRemoval = "Выберите области (Regions), подлежащие удалению из набора"; pso.SingleOnly = false; pso.RejectObjectsFromNonCurrentSpace = true; pso.AllowDuplicates = false; Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter); if(psr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n"); return; } else { ed.WriteMessage("\nВсего выбрано областей (Regions): {0}\n", psr.Value.Count); } Db.ObjectId[] ids = psr.Value.GetObjectIds(); Ed.PromptIntegerOptions pio = new Ed.PromptIntegerOptions( "Количество столбцов"); pio.AllowNegative = false; pio.AllowZero = false; pio.AllowNone = false; pio.DefaultValue = RegionTools.ColumnsCount; pio.UseDefaultValue = true; Ed.PromptIntegerResult pir = ed.GetInteger(pio); if(pir.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Выполнение команды прервано\n"); return; } RegionTools.ColumnsCount = pir.Value; ed.WriteMessage(Environment.NewLine); pio.Message = "Количество строк"; pio.DefaultValue = RegionTools.RowsCount; pir = ed.GetInteger(pio); if(pir.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Выполнение команды прервано\n"); return; } RegionTools.RowsCount = pir.Value; if(RegionTools.ColumnsCount != 1 || RegionTools.RowsCount != 1) { Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions( "Предельная абсолютная погрешность"); pdo.AllowNegative = false; pdo.AllowZero = false; pdo.AllowNone = false; pdo.DefaultValue = RegionTools.Delta; pdo.UseDefaultValue = true; Ed.PromptDoubleResult pdr = ed.GetDouble(pdo); if(pdr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Выполнение команды прервано\n"); return; } RegionTools.Delta = pdr.Value; ed.WriteMessage(Environment.NewLine); using(Db.Transaction tr = db.TransactionManager.StartTransaction()) { Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead); Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[ Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite); DateTime start = DateTime.Now; Int32 count = 0; foreach(Db.ObjectId id in ids) { Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead) as Db.Region; Gm.Point2d minPoint = Gm.Point2d.Origin; Gm.Point2d maxPoint = Gm.Point2d.Origin; region.GetVisualBoundary(RegionTools.Delta, ref minPoint, ref maxPoint); Double width = (maxPoint.X - minPoint.X) / RegionTools.ColumnsCount; Double height = (maxPoint.Y - minPoint.Y) / RegionTools.RowsCount; Db.Region[] regions = region.CloneAndMesh(RegionTools.Delta, width, height); count += regions.Length; foreach(Db.Region item in regions) { btr.AppendEntity(item); tr.AddNewlyCreatedDBObject(item, true); } } tr.Commit(); DateTime end = DateTime.Now; ed.WriteMessage( "\nСоздано областей (region): {0}\nЗатраченное время: {1}\n", count, end - start); } } else { ed.WriteMessage("\nЕсли количество строк и столбцов одновременно " + "равны 1, то создавать нечего.\n"); } } /// <summary> /// Получение значения визуальных границ регионов (аналог реализации от /// AutoCAD по умолчанию). В случаях, когда контуры областей выполнены /// сплайнами, команда BoundaryRegion покажет более точные границы, чем /// команда AcadBoundaryRegion. Назначение AcadBoundaryRegion в том, чтобы /// визуально показать программисту границы контейнера области. Это может /// помочь понять, почему в некоторых случаях программный код даёт не те /// результаты, которые ожидались на выходе. /// </summary> [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup, "AcadBoundaryRegion", Rt.CommandFlags.Modal)] public void AcadBoundaryRegion() { Ap.Document doc = cad.DocumentManager.MdiActiveDocument; if(doc == null) return; Db.Database db = doc.Database; BUx.RegionTools.SetDatabaseDefaultSettings(db); Ed.Editor ed = doc.Editor; Db.TypedValue[] tv = new Db.TypedValue[1]; tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION"); Ed.SelectionFilter filter = new Ed.SelectionFilter(tv); Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions(); pso.MessageForAdding = "Выберите области (Regions) для добавления " + "их в набор"; pso.MessageForRemoval = "Выберите области (Regions), подлежащие удалению из набора"; pso.SingleOnly = false; pso.RejectObjectsFromNonCurrentSpace = true; pso.AllowDuplicates = false; Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter); if(psr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n"); return; } else { ed.WriteMessage("\nВсего выбрано областей (Regions): {0}\n", psr.Value.Count); } Db.ObjectId[] ids = psr.Value.GetObjectIds(); ed.WriteMessage(Environment.NewLine); using(Db.Transaction tr = db.TransactionManager.StartTransaction()) { Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead); Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[ Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite); foreach(Db.ObjectId id in ids) { Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead) as Db.Region; using(Br.Brep brep = new Br.Brep(region)) { Gm.Point3d origin = new Gm.Point3d(0, 0, 0); Gm.Vector3d normal = new Gm.Vector3d(0, 0, 1); Gm.Plane plane = new Gm.Plane(origin, normal); Gm.BoundBlock3d bb = brep.BoundBlock; Gm.Point2d minPoint = Gm.Point2d.Origin; Gm.Point2d maxPoint = Gm.Point2d.Origin; Double minX = Double.MaxValue; Double minY = Double.MaxValue; Double maxX = Double.MinValue; Double maxY = Double.MinValue; if(brep.Edges != null) { foreach(Br.Edge edge in brep.Edges) { Gm.Point3d min = bb.GetMinimumPoint(); Gm.Point3d max = bb.GetMaximumPoint(); if(min.X < minX) minX = min.X; if(min.Y < minY) minY = min.Y; if(max.X > maxX) maxX = max.X; if(max.Y > maxY) maxY = max.Y; } minPoint = new Gm.Point2d(minX, minY); maxPoint = new Gm.Point2d(maxX, maxY); } else { minPoint = bb.GetMinimumPoint().Convert2d(plane); maxPoint = bb.GetMaximumPoint().Convert2d(plane); } Db.Polyline pline = new Db.Polyline(4); pline.SetDatabaseDefaults(); Gm.Point2d[] points = new Gm.Point2d[]{ new Gm.Point2d(minPoint.X,maxPoint.Y), new Gm.Point2d(maxPoint.X,maxPoint.Y), new Gm.Point2d(maxPoint.X,minPoint.Y), new Gm.Point2d(minPoint.X, minPoint.Y)}; for(Int32 i = 0; i < points.Length; i++) pline.AddVertexAt(i, points[i], 0, 0, 0); pline.Closed = true; pline.ColorIndex = 50; btr.AppendEntity(pline); tr.AddNewlyCreatedDBObject(pline, true); } } tr.Commit(); } } /// <summary> /// Получение значения визуальных границ регионов. В случаях, когда контуры /// областей выполнены сплайнами, данная команда покажет более точные /// границы, чем команда AcadBoundaryRegion. Данная команда предназначена /// для демонстрирования работы статического метода /// RegionTools.GetVisualBoundary(...). /// </summary> [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup, "BoundaryRegion", Rt.CommandFlags.Modal)] public void BoundaryRegion() { Ap.Document doc = cad.DocumentManager.MdiActiveDocument; if(doc == null) return; Db.Database db = doc.Database; BUx.RegionTools.SetDatabaseDefaultSettings(db); Ed.Editor ed = doc.Editor; Db.TypedValue[] tv = new Db.TypedValue[1]; tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION"); Ed.SelectionFilter filter = new Ed.SelectionFilter(tv); Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions(); pso.MessageForAdding = "Выберите области (Regions) для добавления " + "их в набор"; pso.MessageForRemoval = "Выберите области (Regions), подлежащие удалению из набора"; pso.SingleOnly = false; pso.RejectObjectsFromNonCurrentSpace = true; pso.AllowDuplicates = false; Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter); if(psr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n"); return; } else { ed.WriteMessage("\nВсего выбрано областей (Regions): {0}\n", psr.Value.Count); } Db.ObjectId[] ids = psr.Value.GetObjectIds(); ed.WriteMessage(Environment.NewLine); Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions( "Предельная абсолютная погрешность"); pdo.AllowNegative = false; pdo.AllowZero = false; pdo.AllowNone = false; pdo.DefaultValue = RegionTools.Delta; pdo.UseDefaultValue = true; Ed.PromptDoubleResult pdr = ed.GetDouble(pdo); if(pdr.Status != Ed.PromptStatus.OK) { ed.WriteMessage("Выполнение команды прервано\n"); return; } RegionTools.Delta = pdr.Value; using(Db.Transaction tr = db.TransactionManager.StartTransaction()) { Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead); Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[ Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite); Int32 count = 0; DateTime start = DateTime.Now; foreach(Db.ObjectId id in ids) { Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead) as Db.Region; using(Br.Brep brep = new Br.Brep(region)) { Gm.Point3d origin = new Gm.Point3d(0, 0, 0); Gm.Vector3d normal = new Gm.Vector3d(0, 0, 1); Gm.Plane plane = new Gm.Plane(origin, normal); Gm.Point2d minPoint = Gm.Point2d.Origin; Gm.Point2d maxPoint = Gm.Point2d.Origin; region.GetVisualBoundary(RegionTools.Delta, ref minPoint, ref maxPoint); Db.Polyline pline = new Db.Polyline(4); pline.SetDatabaseDefaults(); Gm.Point2d[] points = new Gm.Point2d[]{ new Gm.Point2d(minPoint.X,maxPoint.Y), new Gm.Point2d(maxPoint.X,maxPoint.Y), new Gm.Point2d(maxPoint.X,minPoint.Y), new Gm.Point2d(minPoint.X, minPoint.Y)}; for(Int32 i = 0; i < points.Length; i++) pline.AddVertexAt(i, points[i], 0, 0, 0); pline.Closed = true; pline.ColorIndex = 80; btr.AppendEntity(pline); tr.AddNewlyCreatedDBObject(pline, true); ++count; } } tr.Commit(); DateTime end = DateTime.Now; ed.WriteMessage("\nСоздано границ: {0}\nЗатраченное время: {1}\n", count, end - start); } } } }
Комментариев нет:
Отправить комментарий