在 C#中进行 AutoCAD .NET API 二次开发时,继承 Entity 类并重写其关键方法是创建自定义实体的核心。Entity 类是 AutoCAD 中所有图形对象(如直线、圆、多段线)的基类,位于 Autodesk.AutoCAD.DatabaseServices 命名空间。
核心步骤与代码示例
-
创建自定义实体类
创建一个新的类,继承自
Autodesk.AutoCAD.DatabaseServices.Entity。必须使用[CustomObject]特性进行装饰,以便 AutoCAD 能够识别和注册该自定义对象。csharpusing Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; [CustomObject] public class MyCustomEntity : Entity { // 自定义属性:例如圆心和半径 private Point3d _center; private double _radius; public Point3d Center { get { return _center; } set { _center = value; } } public double Radius { get { return _radius; } set { _radius = value; } } // 默认构造函数(序列化必需) public MyCustomEntity() { _center = Point3d.Origin; _radius = 1.0; } // 带参数的构造函数 public MyCustomEntity(Point3d center, double radius) { _center = center; _radius = radius; } } -
重写关键方法
为了确保自定义实体能被正确绘制、选择、夹点编辑和序列化,必须重写以下核心虚方法:
方法名 作用 是否必须重写 WorldDraw在模型空间或图纸空间进行图形绘制。 是 ViewportDraw在特定视口中进行图形绘制(通常可委托给 WorldDraw)。通常委托 GetGripPoints返回实体的夹点位置,用于夹点编辑。 推荐 MoveGripPointsAt处理夹点移动时的实体变形逻辑。 推荐 GetGeomExtents返回实体的几何边界(用于显示范围、缩放等)。 是 TransformBy对实体应用几何变换(如移动、旋转、缩放)。 是 GetStretchPoints返回可用于拉伸操作的基点。 可选 MoveStretchPointsAt处理拉伸点移动时的逻辑。 可选 IntersectWith计算与其他实体的交点(用于修剪、延伸等)。 强烈推荐 Clone创建实体的副本。 是(通常调用基类方法) 关键方法实现示例:
csharp{ // ... 属性与构造函数同上 ... // 1. 重写 WorldDraw 方法:定义实体在图形中的外观 public override bool WorldDraw(Autodesk.AutoCAD.GraphicsInterface.WorldDraw wd) { if (wd == null) return false; // 使用 WorldDraw 的 Geometry 对象进行绘制 // 例如,绘制一个圆(这里用多段线模拟一个圆) if (_radius > 0) { // 创建表示圆的多段线(实际开发中可能使用更底层的图形原语) using (Polyline pl = new Polyline()) { pl.AddVertexAt(0, new Point2d(_center.X - _radius, _center.Y), 0, 0, 0); pl.AddVertexAt(1, new Point2d(_center.X, _center.Y + _radius), 0, 0, 0); pl.AddVertexAt(2, new Point2d(_center.X + _radius, _center.Y), 0, 0, 0); pl.AddVertexAt(3, new Point2d(_center.X, _center.Y - _radius), 0, 0, 0); pl.Closed = true; // 将多段线的几何数据提交给 WorldDraw 进行渲染 wd.Geometry.Draw(pl); }
// 也可以绘制圆心标记
wd.Geometry.Circle(_center, Vector3d.ZAxis, _radius);
}
return true;
}
// 2. 重写 GetGeomExtents 方法:返回实体的包围盒
public override Extents3d GetGeomExtents()
{
// 根据圆心和半径计算圆的边界
return new Extents3d(
new Point3d(_center.X - _radius, _center.Y - _radius, _center.Z),
new Point3d(_center.X + _radius, _center.Y + _radius, _center.Z)
);
}
// 3. 重写 TransformBy 方法:实现对实体的移动、旋转、缩放 public override void TransformBy(Matrix3d transform)
{
// 变换圆心点 _center = _center.TransformBy(transform);
// 对于缩放,需要同时处理半径。这里假设变换是均匀缩放。
// 注意:这是一个简化处理,复杂变换(如非均匀缩放)需要更精细的计算。
Vector3d xAxis = transform.CoordinateSystem3d.Xaxis;
double scale = xAxis.Length; // 获取X方向的缩放因子 _radius *= scale;
}
// 4. 重写 GetGripPoints 方法:定义夹点位置
public override void GetGripPoints(
Point3dCollection gripPoints,
IntegerCollection osnapModes,
IntegerCollection geomIds)
{
base.GetGripPoints(gripPoints, osnapModes, geomIds); // 可调用基类获取默认夹点
// 添加圆心和四个象限点作为夹点 gripPoints.Add(_center); // 圆心
gripPoints.Add(new Point3d(_center.X + _radius, _center.Y, _center.Z)); // 右 gripPoints.Add(new Point3d(_center.X, _center.Y + _radius, _center.Z)); // 上
gripPoints.Add(new Point3d(_center.X - _radius, _center.Y, _center.Z)); // 左 gripPoints.Add(new Point3d(_center.X, _center.Y - _radius, _center.Z)); // 下
}
// 5. 重写 MoveGripPointsAt 方法:响应夹点拖动 public override void MoveGripPointsAt(
IntegerCollection indices,
Vector3d offset)
{
// indices是被拖动的夹点索引,offset 是移动向量
foreach (int index in indices)
{
if (index == 0) // 拖动圆心夹点
{
_center += offset;
}
else if (index >= 1 && index <= 4) // 拖动象限点夹点,改变半径 {
// 简化逻辑:计算新点到圆心的距离作为新半径
Point3d draggedPoint = GetGripPointByIndex(index) + offset;
_radius = _center.DistanceTo(draggedPoint);
}
}
}
// 辅助方法:根据索引获取夹点原始位置
private Point3d GetGripPointByIndex(int index)
{
switch (index)
{
case 0: return _center;
case 1: return new Point3d(_center.X + _radius, _center.Y, _center.Z);
case 2: return new Point3d(_center.X, _center.Y + _radius, _center.Z);
case 3: return new Point3d(_center.X - _radius, _center.Y, _center.Z);
case 4: return new Point3d(_center.X, _center.Y - _radius, _center.Z);
default: return _center;
}
}
// 6. 重写 Clone 方法:创建实体副本
public override DBObject Clone()
{
// 创建新实例并复制属性 MyCustomEntity cloned = new MyCustomEntity(_center, _radius);
// 复制从 Entity 基类继承的属性(如图层、颜色、线型等)
cloned.SetPropertiesFrom(this);
return cloned;
}
}
```
-
注册与使用自定义实体
创建实体后,需要通过
RXClass在 AutoCAD 中注册,然后才能添加到数据库。csharpusing Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; public class MyCommands { [CommandMethod("ADDMYCUSTOM")] public void AddMyCustomEntity() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; using (Transaction tr = db.TransactionManager.StartTransaction()) { // 1. 获取当前空间块表记录(模型空间或图纸空间) BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); // 2. 创建自定义实体实例 MyCustomEntity myEnt = new MyCustomEntity(new Point3d(0, 0, 0), 5.0); myEnt.ColorIndex = 1; // 设置为红色 // 3. 将实体添加到块表记录并通知事务 btr.AppendEntity(myEnt); tr.AddNewlyCreatedDBObject(myEnt, true); // 4. 提交事务 tr.Commit(); } } }
关键注意事项
- 序列化支持 :自定义实体类必须有一个无参数的公共构造函数,以便 AutoCAD 在读取 DWG 文件时能够反序列化(重建)该对象。
- 性能考量 :
WorldDraw和IntersectWith等方法在交互操作中会被频繁调用,其实现应尽可能高效。 - 复杂几何 :对于复杂形状,
WorldDraw中可能需要使用Autodesk.AutoCAD.GraphicsInterface命名空间下的底层图形接口(如WorldGeometry)进行绘制,而非创建临时Entity。 - 自定义数据 :除了几何图形,还可以重写
GetXData和SetXData方法来存储和读取扩展数据。 - 对象捕捉 :重写
GetOsnapPoints方法可以定义实体上的对象捕捉点(如端点、中点、圆心等)。