1. Entity对象基础操作入门
在CAD二次开发中,Entity对象是所有图形实体的基类,理解它的基本操作是开发的基础。我们先从最简单的实体创建和属性修改开始。
创建直线实体的完整示例代码如下:
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; public void CreateLine() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; using (Transaction tr = db.TransactionManager.StartTransaction()) { // 获取模型空间块表记录 BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord btr = (BlockTableRecord)tr.GetObject( bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); // 创建直线对象 Line line = new Line( new Point3d(0, 0, 0), new Point3d(100, 100, 0)); // 设置线型属性 line.ColorIndex = 1; // 红色 line.LineWeight = LineWeight.LineWeight030; // 添加到模型空间 btr.AppendEntity(line); tr.AddNewlyCreatedDBObject(line, true); tr.Commit(); } }这段代码展示了Entity对象的基本生命周期:创建→设置属性→添加到数据库→提交事务。实际开发中我经常遇到新手忘记提交事务导致操作不生效的情况,这点需要特别注意。
2. 几何变换实战技巧
几何变换是CAD开发中最常用的功能之一,Entity对象提供了多种变换方法:
2.1 平移变换
public void MoveEntity(ObjectId entId, Vector3d offset) { using (Transaction tr = entId.Database.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForWrite); ent.TransformBy(Matrix3d.Displacement(offset)); tr.Commit(); } }2.2 旋转变换
public void RotateEntity(ObjectId entId, Point3d basePoint, double angle) { using (Transaction tr = entId.Database.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForWrite); ent.TransformBy(Matrix3d.Rotation( angle, Vector3d.ZAxis, basePoint)); tr.Commit(); } }2.3 缩放变换
public void ScaleEntity(ObjectId entId, Point3d basePoint, double scaleFactor) { using (Transaction tr = entId.Database.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForWrite); ent.TransformBy(Matrix3d.Scaling(scaleFactor, basePoint)); tr.Commit(); } }在实际项目中,我经常需要组合这些变换。比如先旋转再平移,这时候要注意变换矩阵的乘法顺序会影响最终结果。正确的做法是:
Matrix3d mat = Matrix3d.Rotation(angle, Vector3d.ZAxis, center) * Matrix3d.Displacement(offset); ent.TransformBy(mat);3. 批量处理实战
批量处理能显著提高操作效率,以下是几种常见场景的实现:
3.1 批量修改图层
public void ChangeLayer(ObjectIdCollection ids, string layerName) { if (ids.Count == 0) return; Database db = ids[0].Database; using (Transaction tr = db.TransactionManager.StartTransaction()) { // 验证图层是否存在 LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead); if (!lt.Has(layerName)) { tr.Abort(); throw new Exception($"图层{layerName}不存在"); } // 批量修改 foreach (ObjectId id in ids) { Entity ent = (Entity)tr.GetObject(id, OpenMode.ForWrite); ent.Layer = layerName; } tr.Commit(); } }3.2 批量删除实体
public void DeleteEntities(ObjectIdCollection ids) { if (ids.Count == 0) return; Database db = ids[0].Database; using (Transaction tr = db.TransactionManager.StartTransaction()) { foreach (ObjectId id in ids) { Entity ent = (Entity)tr.GetObject(id, OpenMode.ForWrite); ent.Erase(); } tr.Commit(); } }在批量处理时,我建议先对ObjectIdCollection进行非空检查,避免无效操作。同时要注意事务的范围,不要把整个循环放在事务外,这样会导致性能下降。
4. 高级交互操作
4.1 使用Jig实现动态绘制
EntityJig可以实现实时的交互式绘图效果,下面是绘制多边形的实现:
public class PolyJig : EntityJig { private Polyline pline; private Point3d currentPoint; private int vertexCount = 0; public PolyJig() : base(new Polyline()) { pline = (Polyline)Entity; pline.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0); vertexCount++; } protected override SamplerStatus Sampler(JigPrompts prompts) { JigPromptPointOptions opts = new JigPromptPointOptions("\n指定下一点: "); opts.UserInputControls = UserInputControls.Accept3dCoordinates | UserInputControls.NoZeroResponseAccepted | UserInputControls.NoNegativeResponseAccepted; PromptPointResult res = prompts.AcquirePoint(opts); if (res.Status == PromptStatus.Cancel) return SamplerStatus.Cancel; if (res.Value == currentPoint) return SamplerStatus.NoChange; currentPoint = res.Value; return SamplerStatus.OK; } protected override bool Update() { pline.AddVertexAt(vertexCount, new Point2d(currentPoint.X, currentPoint.Y), 0, 0, 0); vertexCount++; return true; } public Entity GetEntity() { return pline; } }使用时只需要创建Jig实例并启动拖拽操作:
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PolyJig jig = new PolyJig(); PromptResult res = ed.Drag(jig); if (res.Status == PromptStatus.OK) { // 添加到数据库 Database db = HostApplicationServices.WorkingDatabase; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord btr = (BlockTableRecord)tr.GetObject( bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); btr.AppendEntity(jig.GetEntity()); tr.AddNewlyCreatedDBObject(jig.GetEntity(), true); tr.Commit(); } }4.2 实体捕捉与修改
实现夹点编辑功能:
public void EditWithGrips(ObjectId entId) { Database db = entId.Database; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForWrite); // 获取实体夹点 Point3dCollection grips = new Point3dCollection(); IntegerCollection snapModes = new IntegerCollection(); IntegerCollection geomIds = new IntegerCollection(); ent.GetGripPoints(grips, snapModes, geomIds); // 让用户选择夹点 PromptIntegerResult res = ed.GetInteger("\n选择要移动的夹点(1-"+grips.Count+"):"); if (res.Status != PromptStatus.OK) return; int index = res.Value - 1; if (index < 0 || index >= grips.Count) { ed.WriteMessage("\n无效的夹点索引"); return; } // 获取移动向量 PromptPointOptions opts = new PromptPointOptions("\n指定新位置:") .UseBasePoint(grips[index]); opts.UserInputControls = UserInputControls.NoZeroResponseAccepted | UserInputControls.NoNegativeResponseAccepted; PromptPointResult ptRes = ed.GetPoint(opts); if (ptRes.Status != PromptStatus.OK) return; // 移动夹点 Vector3d offset = ptRes.Value - grips[index]; IntegerCollection indices = new IntegerCollection(); indices.Add(index); ent.MoveGripPointsAt(indices, offset); tr.Commit(); } }5. 性能优化技巧
在处理大量实体时,性能优化尤为重要:
5.1 使用OpenMode.ForRead
只读访问时使用ForRead模式:
using (Transaction tr = db.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForRead); Extents3d extents = ent.GeometricExtents; // 只读操作 // 不需要tr.Commit() }5.2 批量操作时重用事务
public void ProcessEntities(ObjectIdCollection ids) { if (ids.Count == 0) return; Database db = ids[0].Database; using (Transaction tr = db.TransactionManager.StartTransaction()) { foreach (ObjectId id in ids) { Entity ent = (Entity)tr.GetObject(id, OpenMode.ForWrite); // 处理逻辑... } tr.Commit(); } }5.3 使用索引加速查询
public ObjectIdCollection FindEntitiesInArea(Extents3d area) { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; // 创建选择过滤器 TypedValue[] values = new TypedValue[] { new TypedValue((int)DxfCode.Start, "LINE,ARC,CIRCLE") // 只选择这些类型 }; SelectionFilter filter = new SelectionFilter(values); // 使用窗口选择 PromptSelectionResult res = ed.SelectCrossingWindow( area.MinPoint, area.MaxPoint, filter); if (res.Status == PromptStatus.OK) return new ObjectIdCollection(res.Value.GetObjectIds()); else return new ObjectIdCollection(); }6. 常见问题解决方案
6.1 实体无法修改
常见原因是事务未提交或打开模式不正确:
using (Transaction tr = db.TransactionManager.StartTransaction()) { // 错误:使用ForRead模式尝试修改 // Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForRead); // 正确:使用ForWrite模式 Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForWrite); ent.ColorIndex = 1; tr.Commit(); // 必须提交 }6.2 实体克隆问题
克隆实体时要注意处理扩展数据:
public Entity CloneEntity(ObjectId entId, Database targetDb) { using (Transaction tr = entId.Database.TransactionManager.StartTransaction()) { Entity source = (Entity)tr.GetObject(entId, OpenMode.ForRead); // 简单克隆(不包含扩展数据) Entity clone = (Entity)source.Clone(); // 如果需要完整克隆(包含扩展数据) IdMapping mapping = new IdMapping(); DBObjectCollection clones = new DBObjectCollection(); source.DeepClone(source.Database, clones, mapping); if (clones.Count > 0) { clone = (Entity)clones[0]; } return clone; } }6.3 处理复杂实体
对于多段线等复杂实体,需要特殊处理顶点:
public void ProcessPolyline(ObjectId plineId) { using (Transaction tr = plineId.Database.TransactionManager.StartTransaction()) { Polyline pline = (Polyline)tr.GetObject(plineId, OpenMode.ForWrite); // 遍历所有顶点 for (int i = 0; i < pline.NumberOfVertices; i++) { Point2d pt = pline.GetPoint2dAt(i); // 处理顶点... } // 添加新顶点 pline.AddVertexAt(pline.NumberOfVertices, new Point2d(100, 100), 0, 0, 0); tr.Commit(); } }7. 实战案例:批量偏移曲线
结合前面介绍的技术,我们实现一个实用的批量偏移功能:
public void OffsetCurves(ObjectIdCollection curveIds, double offsetDist) { if (curveIds.Count == 0) return; Database db = curveIds[0].Database; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord btr = (BlockTableRecord)tr.GetObject( bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); foreach (ObjectId id in curveIds) { Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead); if (ent is Curve curve) { try { // 创建偏移曲线 DBObjectCollection offsetCurves = curve.GetOffsetCurves(offsetDist); // 添加到模型空间 foreach (DBObject obj in offsetCurves) { Entity offsetEnt = (Entity)obj; btr.AppendEntity(offsetEnt); tr.AddNewlyCreatedDBObject(offsetEnt, true); } } catch (Autodesk.AutoCAD.Runtime.Exception ex) { Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( $"\n无法偏移实体 {id}: {ex.Message}"); } } } tr.Commit(); } }这个例子展示了如何:
- 处理多种曲线类型(直线、圆弧、多段线等)
- 进行批量操作
- 添加错误处理
- 维护事务完整性
8. 扩展应用:自定义实体操作
除了内置实体类型,我们还可以操作自定义实体:
public void ProcessCustomEntity(ObjectId entId) { using (Transaction tr = entId.Database.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForRead); // 检查是否为自定义实体 if (ent is ProxyEntity proxy) { // 获取代理实体的原始类名 string originalClass = proxy.OriginalClassName; // 处理代理实体... } else if (ent.XData != null) { // 处理带有扩展数据的实体 foreach (TypedValue tv in ent.XData) { // 解析扩展数据... } } tr.Commit(); } }9. 最佳实践总结
经过多个项目的实践,我总结了以下Entity对象操作的最佳实践:
事务管理:
- 每个独立操作使用单独事务
- 批量操作合并到一个事务中
- 及时释放事务资源
错误处理:
- 捕获特定异常(如eInvalidInput)
- 提供有意义的错误信息
- 确保事务在异常时正确回滚
性能优化:
- 最小化事务范围
- 合理使用OpenMode
- 避免频繁的数据库查询
代码组织:
- 封装常用操作为独立方法
- 使用扩展方法增强可读性
- 保持代码与CAD版本兼容
下面是一个封装好的实用工具方法示例:
public static class EntityExtensions { public static void SafeTransform(this Entity ent, Matrix3d mat) { try { ent.TransformBy(mat); } catch (Autodesk.AutoCAD.Runtime.Exception ex) { if (ex.ErrorStatus == ErrorStatus.InvalidGeometry) { // 处理无效几何体情况 } throw; } } public static bool IsVisible(this Entity ent) { return ent.Visible && !ent.IsErased && ent.LayerId.IsValid && ent.ColorIndex != 0; // 0表示BYBLOCK } }使用时只需要:
ent.SafeTransform(transformationMatrix); if (ent.IsVisible()) { /*...*/ }