C# CAD二次开发自定义实体实现

在 C#中进行 AutoCAD .NET API 二次开发时,继承 Entity 类并重写其关键方法是创建自定义实体的核心。Entity 类是 AutoCAD 中所有图形对象(如直线、圆、多段线)的基类,位于 Autodesk.AutoCAD.DatabaseServices 命名空间。

核心步骤与代码示例

  1. 创建自定义实体类

    创建一个新的类,继承自 Autodesk.AutoCAD.DatabaseServices.Entity。必须使用 [CustomObject] 特性进行装饰,以便 AutoCAD 能够识别和注册该自定义对象。

    csharp 复制代码
    using 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;
        }
    }
  2. 重写关键方法

    为了确保自定义实体能被正确绘制、选择、夹点编辑和序列化,必须重写以下核心虚方法:

    方法名 作用 是否必须重写
    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;
    }
}
```
  1. 注册与使用自定义实体

    创建实体后,需要通过 RXClass 在 AutoCAD 中注册,然后才能添加到数据库。

    csharp 复制代码
    using 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 方法可以定义实体上的对象捕捉点(如端点、中点、圆心等)。
相关推荐
huaqianzkh1 天前
WinForm C#:循环修改控件UI闪烁卡顿?SuspendLayout / ResumeLayout 与 BeginUpdate 优化
开发语言·ui·c#
用户3721574261352 天前
C# 实现 Excel 单元格保护:锁定公式并允许编辑指定区域
c#·ai编程·编程语言
公子小六2 天前
基于.NET的Windows窗体编程之WinForms数据持久化(1)
windows·microsoft·c#·.net·winforms
三雷科技2 天前
第6章第4节:自定义分词器
开发语言·c#
zhanghaha13142 天前
C# 系列教程_12 字符串 string 保姆级系统教程(面向新手,大量示例 + 原理讲解)
开发语言·数据库·c#
Coder_Shenshen3 天前
罗克韦尔ACD工程归档离线变量树解析
算法·c#
寂寞的船3 天前
【Unity知识速递】第10期 Unity GC 零分配实战:从 UnityEngine.Pool 到手动 GC 控制的全链路优化
jvm·unity·c#·游戏引擎·lucene
1-1=03 天前
C# 数据结构和特殊类型
数据结构·c#·.net·.netcore
波力海苔夹心脆6755 天前
C# WPF 资源字典详解:ResourceDictionary、MergedDictionaries 与自定义控件案例
开发语言·c#·wpf·入门教程·resource·资源字典
AI导出鸭PC端5 天前
Perplexity批量导出没有PDF模式?AI导出鸭插件补上最后一块拼图
人工智能·pdf·c#·ai导出鸭