Сети SubDMesh и грани Face
Несмотря на то, что PolyFaceMesh называется "многогранной", всё же она имеет ограничение на величину граней в ~65 тысяч объектов. Вероятно всего, вы не не столкнетесь в обычной практике программирования с этим лимитом, но в противном случае важно знать, что существует ещё один трехмерный объект "Сеть" с неограниченным числом граней, описываемый классом SubDMesh.
Объект "3D-грань" описывается классом Face и может сотоять из 3 или 4 вершин. В примере "Чтение сети" ниже именно она и создается на гранях сети SubDMesh.
Создание сети
В примере ниже создается многогранная сеть в пространстве модели.
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
[CommandMethod("CreatePolyfaceMesh")]
public static void CreatePolyfaceMesh()
{
// Get the current document and database, and start a transaction
Document acDoc = Application.DocumentManager.MdiActiveDocument;
Database acCurDb = acDoc.Database;
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
// Open the Block table record for read
BlockTable acBlkTbl;
acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
OpenMode.ForRead) as BlockTable;
// Open the Block table record Model space for write
BlockTableRecord acBlkTblRec;
acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
OpenMode.ForWrite) as BlockTableRecord;
// Create a polyface mesh
using (PolyFaceMesh acPFaceMesh = new PolyFaceMesh())
{
// Add the new object to the block table record and the transaction
acBlkTblRec.AppendEntity(acPFaceMesh);
acTrans.AddNewlyCreatedDBObject(acPFaceMesh, true);
// Before adding vertexes, the polyline must be in the drawing
Point3dCollection acPts3dPFMesh = new Point3dCollection();
acPts3dPFMesh.Add(new Point3d(4, 7, 0));
acPts3dPFMesh.Add(new Point3d(5, 7, 0));
acPts3dPFMesh.Add(new Point3d(6, 7, 0));
acPts3dPFMesh.Add(new Point3d(4, 6, 0));
acPts3dPFMesh.Add(new Point3d(5, 6, 0));
acPts3dPFMesh.Add(new Point3d(6, 6, 1));
foreach (Point3d acPt3d in acPts3dPFMesh)
{
PolyFaceMeshVertex acPMeshVer = new PolyFaceMeshVertex(acPt3d);
acPFaceMesh.AppendVertex(acPMeshVer);
acTrans.AddNewlyCreatedDBObject(acPMeshVer, true);
}
using (FaceRecord acFaceRec1 = new FaceRecord(1, 2, 5, 4))
{
acPFaceMesh.AppendFaceRecord(acFaceRec1);
acTrans.AddNewlyCreatedDBObject(acFaceRec1, true);
}
using (FaceRecord acFaceRec2 = new FaceRecord(2, 3, 6, 5))
{
acPFaceMesh.AppendFaceRecord(acFaceRec2);
acTrans.AddNewlyCreatedDBObject(acFaceRec2, true);
}
}
// Open the active viewport
ViewportTableRecord acVportTblRec;
acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
OpenMode.ForWrite) as ViewportTableRecord;
// Rotate the view direction of the current viewport
acVportTblRec.ViewDirection = new Vector3d(-1, -1, 1);
acDoc.Editor.UpdateTiledViewportsFromDatabase();
// Save the new objects to the database
acTrans.Commit();
}
}
Чтение сети
Чтение сети имеет особенность, дело в том, что информация о гранях сохраняется в формат одномерного массива int, а грань может состоять из 3 или 4 вершин. В этом случае чтение информации о гранях должно быть аккуратным. В примере ниже приводится такая реализация. Для существующей сети в месте вершин создаются точки красного цвета, а также создаются грани типа Face.
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
[CommandMethod("ExtractSubDMesh")]
public void ExtractSubDMesh()
{
Document acCurDoc = Application.DocumentManager.MdiActiveDocument;
Database acCurDb = acCurDoc.Database;
PromptEntityOptions selMeshOpts = new PromptEntityOptions("\nSelect a SubDMesh");
selMeshOpts.AddAllowedClass(typeof(SubDMesh), true);
PromptEntityResult selMeshResult = acCurDoc.Editor.GetEntity(selMeshOpts);
if (selMeshResult.Status != PromptStatus.OK) return;
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
// Open the Block table record for read
BlockTable acBlkTbl;
acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
OpenMode.ForRead) as BlockTable;
// Open the Block table record Model space for write
BlockTableRecord acBlkTblRec;
acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
OpenMode.ForWrite) as BlockTableRecord;
SubDMesh? meshEntity = acTrans.GetObject(selMeshResult.ObjectId, OpenMode.ForRead) as SubDMesh;
if (meshEntity == null) return;
acCurDoc.Editor.WriteMessage($"SubDMesh Vertices {meshEntity.NumberOfVertices}");
acCurDoc.Editor.WriteMessage($"SubDMesh Faces {meshEntity.NumberOfFaces}");
acCurDoc.Editor.WriteMessage($"SubDMesh Faces's vertextes {meshEntity.FaceArray.Count}");
for (int vertexIndex = 0; vertexIndex < meshEntity.NumberOfVertices; vertexIndex++)
{
DBPoint meshVertex = new DBPoint(meshEntity.Vertices[vertexIndex]);
meshVertex.ColorIndex = 1;
acBlkTblRec.AppendEntity(meshVertex);
acTrans.AddNewlyCreatedDBObject(meshVertex, true);
}
// Because the faces can contains 3 or 4 vertexes, we need read it with accuracy in tmpFaceRecordDef
int[] tmpFaceRecordDef = new int[] { };
int tmpCounter = -1;
for (int faceVertexIndex = 0; faceVertexIndex < meshEntity.FaceArray.Count; faceVertexIndex++)
{
int data = meshEntity.FaceArray[faceVertexIndex];
if (tmpFaceRecordDef.Length == 0) tmpFaceRecordDef = new int[data];
else tmpFaceRecordDef[tmpCounter] = data;
tmpCounter++;
if (tmpCounter == tmpFaceRecordDef.Length)
{
faceProcessing();
}
}
faceProcessing();
void faceProcessing()
{
if (tmpFaceRecordDef.Length < 3) return;
// Create a 3dface
Face meshFace = new Face();
Point3d vertex1 = meshEntity.Vertices[tmpFaceRecordDef[0]];
Point3d vertex2 = meshEntity.Vertices[tmpFaceRecordDef[1]];
Point3d vertex3 = meshEntity.Vertices[tmpFaceRecordDef[2]];
Point3d vertex4 = new Point3d();
if (tmpFaceRecordDef.Length == 4) vertex4 = meshEntity.Vertices[tmpFaceRecordDef[3]];
if (tmpFaceRecordDef.Length == 3) meshFace = new Face(vertex1, vertex2, vertex3, true, true, true, true);
else meshFace = new Face(vertex1, vertex2, vertex3, vertex4, true, true, true, true);
meshFace.ColorIndex = 1;
acBlkTblRec.AppendEntity(meshFace);
acTrans.AddNewlyCreatedDBObject(meshFace, true);
// Reset temp data
tmpCounter = -1;
tmpFaceRecordDef = new int[] { };
}
acTrans.Commit();
}
}