AutoCAD вообще очень медленно работает с объектами Region, примерно так же медленно, как и с телами (объектами Solid3d). Причём AutoCAD 2014 и 2015 работают с областями и телами даже в два раза медленней, чем более старые версии. Это обусловлено тем, что в обозначенных версиях AutoCAD компанией Autodesk был переписан код по работе с Region и Solid3d. но при этом не было произведено надлежащего тестирования обновлённого функционала.
1. Команда AcadBoundaryRegion создаёт жёлтую рамку вокруг регионов. Эта рамка показывает визуальные границы BoundaryBox для этих примитивов/ Габариты берутся именно те, которые AutoCAD API показывает программистам в свойствах примитивов:
2. Команда BoundaryRegion показывает корректные визуальные границы наших областей.
Как видим, созданные с помощью BoundaryRegion границы имеют зелёный цвет, для их визуального отличия от границ, созданных при помощи команды AcadBoundaryRegion.
Эту команду я использую для того, чтобы визуально убедиться в том, что вычисленные мною габариты верны.
Можно задавать произвольное значение высоты и ширины ячеек.
4. Команда MeshRegionThroughRowsAndColumnsCount режет копии наших областей на указанное количество строк и столбцов:
Исходный код программы разбит на несколько файлов. Далее приведено их полное содержимое.
/* 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
* © Андрей Бушман, 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
* © Андрей Бушман, 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
* © Андрей Бушман, 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
* © Андрей Бушман, 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);
}
}
}
}