// SliceOperations.cs
// © Andrey Bushman, 2014
// Команда SliceGrid запрашивает у пользователя объекты ОБЛАСТЬ (REGION), которые
// необходимо выдавить не некоторую высоту, создав тем самым объекты 3D ТЕЛ
// (3D SOLID), после чего ОБЛАСТИ удаляются, а 3D-ТЕЛА разрезаются по осям X
// и Y на куски определённого размера.
// Команда GeomExt - для указанных примитивов показывает границы GeometricExtents
// (по контуру этой границы создаётся полилиния).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using App = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Gem = Autodesk.AutoCAD.Geometry;
using Rtm = Autodesk.AutoCAD.Runtime;
using System.IO;
[assembly: Rtm.CommandClass(typeof(Bushman.CAD.Commands.SliceOperations))]
namespace Bushman.CAD.Commands {
public class SliceOperations : Rtm.IExtensionApplication {
const String ns = "Bushman";
/// <summary>
/// Тестовая команда преобразования регионов в 3D-тела с последующей их нарезкой
/// ломтиками по горизонтали и вертикали
/// </summary>
[Rtm.CommandMethod(ns, "SliceGrid", Rtm.CommandFlags.Modal |
Rtm.CommandFlags.NoPaperSpace)]
public void SliceGrid() {
App.Document doc = cad.DocumentManager.MdiActiveDocument;
Db.Database db = doc.Database;
Ed.Editor ed = doc.Editor;
Double dx = 50.0;
Double dy = 50.0;
Double height = 1000.0;
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 = "Выберите регионы для добавления их в набор";
pso.MessageForRemoval = "Выберите регионы, подлежащие удалению из набора";
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Всего выбрано регионов: {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 = 50.0;
pdo.UseDefaultValue = true;
Ed.PromptDoubleResult pdr = ed.GetDouble(pdo);
if (pdr.Status != Ed.PromptStatus.OK) {
ed.WriteMessage("Выполнение команды прервано\n");
return;
}
dx = pdr.Value;
pdo.Message = "dy";
pdr = ed.GetDouble(pdo);
if (pdr.Status != Ed.PromptStatus.OK) {
ed.WriteMessage("Выполнение команды прервано\n");
return;
}
dy = pdr.Value;
pdo.Message = "Высота выдавливания";
pdo.AllowNegative = true;
pdo.DefaultValue = 1000.0;
pdr = ed.GetDouble(pdo);
if (pdr.Status != Ed.PromptStatus.OK) {
ed.WriteMessage("Выполнение команды прервано\n");
return;
}
height = pdr.Value;
DateTime start = DateTime.Now;
Db.ObjectId[] result = SliceGrid(dx, dy, height, ids, true);
DateTime end = DateTime.Now;
ed.WriteMessage("\nСоздано тел: {0}\nЗатраченное время: {1}\n",
result.Length, end - start);
}
/// <summary>
/// Для указанных областей, метод выдавливает 3D-тела и каждое из них нарезает
/// сеткой.
/// </summary>
/// <param name="dx">Ширина ячейки сетки</param>
/// <param name="dy">Высота ячейки сетки</param>
/// <param name="height">Высота выдавлениваемых объектов.</param>
/// <param name="ids">Идентификаторы областей, подлежащих выдавливанию.
/// </param>
/// <param name="eraseRegions">true - удалять исходные регионы,
/// false - не удалять.</param>
/// <returns>Возвращается массив идентификаторов созданных 3D-тел.</returns>
public static Db.ObjectId[] SliceGrid(Double dx, Double dy, Double height,
Db.ObjectId[] ids, Boolean eraseRegions) {
App.Document doc = cad.DocumentManager.MdiActiveDocument;
Db.Database db = doc.Database;
Ed.Editor ed = doc.Editor;
List<Db.ObjectId> result = new List<Db.ObjectId>();
using (doc.LockDocument()) {
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) {
if (!id.IsValid || id.IsNull || id.IsErased) {
ed.WriteMessage("Неверный идентификатор объекта\n");
continue;
}
Db.Entity ent = tr.GetObject(id, Db.OpenMode.ForWrite) as Db.Entity;
if (ent == null) {
ed.WriteMessage("Не удалось привести объект к типу Entity.\n");
continue;
}
// Для начала нужно выдавить исходную область в 3D-тело, подлежащее
// дальнейшей обработке
Db.Solid3d solid_base = new Db.Solid3d(); // Исходное тело
solid_base.SetDatabaseDefaults();
solid_base.Extrude((Db.Region)ent, height, 0); // Выдавил тело
solid_base.LayerId = ent.LayerId;
solid_base.ColorIndex = ent.ColorIndex;
solid_base.Color = ent.Color;
btr.AppendEntity(solid_base);
tr.AddNewlyCreatedDBObject(solid_base, true);
result.Add(solid_base.ObjectId);
if (eraseRegions) ent.Erase();
Db.Extents3d ext_base = solid_base.GeometricExtents;
Int32 rowsCount = (Int32)((ext_base.MaxPoint.Y - ext_base.MinPoint.Y)
/ dy);
Int32 columnsCount = (Int32)((ext_base.MaxPoint.X - ext_base.MinPoint.X)
/ dx);
// Часть тела, отрезанная в виде горизонтального куска, высотой dy
// (если смотреть сверху).
Db.Solid3d solid_added_row = null;
// Режу твёрдотельный 3D-объект на "строки"
Int32 row_index = 1;
Double hor_section = 0.0; // Координата Y очередного гориз. сечения
Db.Solid3d solid_current_row = solid_base;
// Режу по горизонтали до тех пор, пока секущая плоскость находится в
// пределах габаритов исходного тела
while ((hor_section = ext_base.MinPoint.Y + row_index * dy) <
solid_current_row.GeometricExtents.MaxPoint.Y) {
// Получаю границы текущего тела, т.к. зачастую, в очередном цикле
// итерации, это уже будет др. тело, со своими границами
Db.Extents3d ext_current = solid_current_row.GeometricExtents;
// Если тело не попадает в данное сечение - перехожу к следующей
// итерации
if (hor_section <= ext_current.MinPoint.Y) {
++row_index;
continue;
}
// Очередная горизонтальная секущая плоскость
Gem.Plane hor_plane = new Gem.Plane(
// Точка пересечения осей X и Y
new Gem.Point3d(ext_base.MinPoint.X, ext_base.MinPoint.Y + row_index *
dy, ext_base.MinPoint.Z),
// Направление вектора плоскости. Это направление противоположно тому,
// в какую стороны будет производиться нарезка 3D тела
new Gem.Vector3d(0, -1, 0));
solid_added_row = solid_current_row.Slice(hor_plane, true);
solid_added_row.LayerId = solid_current_row.LayerId;
solid_added_row.ColorIndex = solid_current_row.ColorIndex;
solid_added_row.Color = solid_current_row.Color;
btr.AppendEntity(solid_added_row);
tr.AddNewlyCreatedDBObject(solid_added_row, true);
result.Add(solid_added_row.ObjectId);
Db.Solid3d solid_current_column = solid_current_row;
SliceRow(dx, ref ext_base, ref solid_current_column, ref result);
++row_index;
solid_current_row = solid_added_row;
}
SliceRow(dx, ref ext_base, ref solid_current_row, ref result);
}
tr.Commit();
}
}
return result.ToArray();
}
/// <summary>
/// Разрезать 3D-тело по вертикали с определённым шагом
/// </summary>
/// <param name="dx">Горизонтальный шаг резки</param>
/// <param name="ext_base">Границы исходного объекта</param>
/// <param name="solid">Объект, подлежащий резке параллельно оси Y.
/// </param>
/// <param name="result">Ссылка на список, в который следует добавлять
/// идентификаторы новых 3D тел.
/// </param>
private static void SliceRow(Double dx, ref Db.Extents3d ext_base,
ref Db.Solid3d solid, ref List<Db.ObjectId> result) {
App.Document doc = cad.DocumentManager.MdiActiveDocument;
Db.Database db = doc.Database;
Ed.Editor ed = doc.Editor;
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 column_index = 1;
Double vert_section = 0.0; // Координата X очередного верт. сечения
// Часть тела, отрезанная от горизонтального куска (rowSolid) в виде
// вертикального куска, шириной dx (если смотреть сверху).
Db.Solid3d columnSolid = null;
// Режу по вертикали до тех пор, пока секущая плоскость находится в
// пределах габаритов исходного тела
while ((vert_section = ext_base.MinPoint.X + column_index * dx) <
solid.GeometricExtents.MaxPoint.X) {
// Получаю границы текущего тела, т.к. зачастую, в очередном цикле
// итерации, это уже будет др. тело, со своими границами:
Db.Extents3d ext_current2 = solid.GeometricExtents;
// Если тело не попадает в данное сечение - перехожу к следующей
// итерации
if (vert_section <= ext_current2.MinPoint.X) {
++column_index;
continue;
}
// Очередная вертикальная секущая плоскость
Gem.Plane vert_plane = new Gem.Plane(
// Точка пересечения осей X и Y
new Gem.Point3d(ext_base.MinPoint.X + column_index * dx,
ext_base.MinPoint.Y, ext_base.MinPoint.Z),
// Направление вектора плоскости. Это направление противоположно тому,
// в какую стороны будет производиться нарезка 3D тела
new Gem.Vector3d(-1, 0, 0));
columnSolid = solid.Slice(vert_plane, true);
columnSolid.LayerId = solid.LayerId;
columnSolid.ColorIndex = solid.ColorIndex;
columnSolid.Color = solid.Color;
btr.AppendEntity(columnSolid);
tr.AddNewlyCreatedDBObject(columnSolid, true);
result.Add(columnSolid.ObjectId);
++column_index;
solid = columnSolid;
}
tr.Commit();
}
}
/// <summary>
/// Создать прямоугольники (замкнутые плосские полилинии), демонстрирующие границы
/// выбранных объектов. Построение выполняется на основании значений точек
/// Entity.GeometricExtents.MinPoint и Entity.GeometricExtents.MaxPoint.
/// </summary>
[Rtm.CommandMethod(ns, "GeomExt", Rtm.CommandFlags.Modal |
Rtm.CommandFlags.NoPaperSpace)]
public void GeomExt() {
App.Document doc = cad.DocumentManager.MdiActiveDocument;
Db.Database db = doc.Database;
Ed.Editor ed = doc.Editor;
Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
pso.MessageForAdding = "Выберите примитивы для добавления их в набор";
pso.MessageForRemoval = "Выберите примитивы, подлежащие удалению из набора";
pso.SingleOnly = false;
pso.RejectObjectsFromNonCurrentSpace = true;
pso.AllowDuplicates = false;
Ed.PromptSelectionResult psr = ed.GetSelection(pso);
if (psr.Status != Ed.PromptStatus.OK) {
ed.WriteMessage("Ничего не выбрано. Выполнение команды прервано\n");
return;
}
else {
ed.WriteMessage("\nВсего выбрано примитивов: {0}\n", psr.Value.Count);
}
Db.ObjectId[] ids = psr.Value.GetObjectIds();
using (doc.LockDocument()) {
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) {
if (!id.IsValid || id.IsNull || id.IsErased) {
ed.WriteMessage("Неверный идентификатор объекта\n");
continue;
}
Db.Entity ent = tr.GetObject(id, Db.OpenMode.ForWrite) as Db.Entity;
if (ent == null) {
ed.WriteMessage("Не удалось привести объект к типу Entity.\n");
continue;
}
Db.Polyline pline = new Db.Polyline();
pline.AddVertexAt(0,
new Gem.Point2d(ent.GeometricExtents.MinPoint.X,
ent.GeometricExtents.MinPoint.Y), 0, 0, 0);
pline.AddVertexAt(1,
new Gem.Point2d(ent.GeometricExtents.MinPoint.X,
ent.GeometricExtents.MaxPoint.Y), 0, 0, 0);
pline.AddVertexAt(1,
new Gem.Point2d(ent.GeometricExtents.MaxPoint.X,
ent.GeometricExtents.MaxPoint.Y), 0, 0, 0);
pline.AddVertexAt(1,
new Gem.Point2d(ent.GeometricExtents.MaxPoint.X,
ent.GeometricExtents.MinPoint.Y), 0, 0, 0);
pline.Closed = true;
pline.SetDatabaseDefaults();
btr.AppendEntity(pline);
tr.AddNewlyCreatedDBObject(pline, true);
}
tr.Commit();
}
}
}
#region IExtensionApplication Members
public void Initialize() {
App.Document doc = cad.DocumentManager.MdiActiveDocument;
Db.Database db = doc.Database;
Ed.Editor ed = doc.Editor;
ed.WriteMessage("\n{0}. © Andrey Bushman, 2014\n",
Path.GetFileName(this.GetType().Assembly.Location));
}
public void Terminate() {
}
#endregion
}
}