# Fundamentals of CAD Secondary Development **Repository Path**: xiaopickles/fundamentals-of-cad-secondary-development ## Basic Information - **Project Name**: Fundamentals of CAD Secondary Development - **Description**: CAD二次开发文档 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 3 - **Created**: 2025-03-16 - **Last Updated**: 2025-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: cad二次开发 ## README # 搭建开发环境 ## `Framework`搭建开发环境 使用 NuGet 程序包安装 Cad 类库。 :one: 点击引用,选择管理 NuGet 程序包。 ![img](assets/1740814624973-ab6c35ad-2fe2-4486-aeaf-1615a67db98d.png) :two: 搜索` autocad .net`,选择22.00版本。 img :three: 调试选择 `CAD `外部程序。 img 测试开发环境 ```csharp public class Learn { //CAD命令 [CommandMethod("sayhello")] public void SayHello() { //当前文档cad命令行对象 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; //命令行输出hello CAD ed.WriteMessage("Hello CAD"); } } ``` 用`netload` 加载类库文件,在命令行中输入`sayHello`。 ## `.Net Standard` 使用标准类库创建需配置文件 ```c# net9.0 //netframework4.8 net48 //ExcludeAssets="runtime",本地复制改为false ``` ## 命令优化 :one: CAD载入.Net程序集时,首先会查找`ExtensionApplication`属性,把与该属性关联的类作为程序入口进行初始化工作,没有找到便放弃初始化; :two: 还会查找`CommendClass`属性,并在关联`CommendClass`属性的类中检索命令 :red_circle:注意要将`ExtensionApplication`属性和`ExtensionApplication`属性分别关联相应的类,原因是CAD可能会只将关联`ExtensionApplication`属性的视为入口进行初始化工作,而不会检索其中注册的命令 ```c# //初始化 [assembly:ExtensionApplication(typeof(MyCad.IntialTool))] namespace MyCad { public class IntialTool : IExtensionApplication { public void Initialize() { //初始化 } public void Terminate() { //卸载时运行 } } } //命令类 [assembly: CommandClass(typeof(MyCad.Program))] namespace MyCad { public class Program { [CommandMethod("1")] public void Add() { Application.ShowAlertDialog("检测命令"); } } } ``` # API ```mermaid flowchart TD A(Application) --> B(DocumentManager) B--> Document Document --> Database ``` 1. `Application`对象是所有对象的根对象,通过`Application`对象可以获取文档对象集合。 2. `Document`对象是当前文档对象,通过`Document`对象可以操作文档所对应的数据库。 ## Application :one: 属性 | 属性 | 作用 | | ------------------------------------------- | --------------- | | `static DocumentCollection DocumentManager` | 文档对象集合 | | `static System.Version Version` | 当前CAD内部版本 | :two: 函数 | 函数签名 | 作用 | | ------------------------------------------------------------ | -------------------------- | | `static void ShowAlertDialog(string message );` | 警告窗口 | | `static object GetSystemVariable(string name );` | 获取系统变量 | | `static void SetSystemVariable(string name, object value );` | 设置系统变量的值,变量/值· | :bookmark:版本 ```c# Application.ShowAlertDialog(Application.Version.ToString());//22.0.0.0(2018版本CAD) ``` :bookmark:操作窗口 ```c# Window win = Application.MainWindow; win.WindowState = Window.State.Minimized;//最小化 ``` :bookmark:添加上下文菜单: ```c# //菜单项 MenuItem item = new MenuItem("新增文档"); //添加事件 item.Click += (o, e) => { Application.DocumentManager.Add(""); }; //菜单集合 var contextEx = new ContextMenuExtension() { Title = "自定义菜单" }; contextEx.MenuItems.Add(item); Application.AddDefaultContextMenuExtension(contextEx); //不能添加重复名称的菜单 Application.AddObjectContextMenuExtension(RXObject.GetClass(typeof(Line)), contextEx); ``` :bookmark:事件简单使用: ```c# Application.BeginQuit += (o, e) => { //关闭所有文档后提示即将退出 Application.ShowAlertDialog("即将退出"); }; Application.BeginCloseAll += (o, e) => { //先提示关闭文档 Application.ShowAlertDialog("即将关闭所有文档"); }; ``` ## Document :book: 属性 | 属性 | 作用 | | ------------------- | -------------------- | | `Editor` | 命令行 | | `string Name` | 文档名称 | | `Database Database` | 当前文档的图形数据库 | :book: 常用函数 | 函数签名 | 作用 | | ------------------------------------------------------------ | --------------------------------------------- | | `Document Open(this DocumentCollection doc, string fileName,bool forReadOnly );` | 打开文档,名称为绝对地址,参数2,false为可写, | | `Document Open(this DocumentCollection doc, string fileName);` | 打开文档,默认上锁 | | `Document Add( this DocumentCollection docCol,string templateFileName );` | 新增文档 | | `DocumentLock LockDocument();` | 锁定文档(图形文件被保护) | | `void UpgradeDocOpen();` | 解锁 | | `void DowngradeDocOpen(bool bPromptForSave);` | 上锁:red_circle:未搞懂机理,暂时搁浅 | :bookmark:打开文档 ```c# DocumentCollection docs = Application.DocumentManager; Document currentDoc = docs.Open(@"C:\Users\Administrator\Desktop\通用.dwg"); currentDoc.UpgradeDocOpen();//由只读升为写 ``` :bookmark:遍历文档 ```c# //文档集合 DocumentCollection coll = Application.DocumentManager; foreach (Document doc in coll) { //弹出警告框 Application.ShowAlertDialog(doc.Name); } ``` :bookmark:新增文档 ```c# //文档集合 DocumentCollection coll = Application.DocumentManager; coll.Add("填入模板名称");//DocumentCollectionExtion方法 ``` :bookmark: 锁定文档 在某些情况下修改对象,会导致程序崩溃,解决办法是将要修改的文档进行锁定,修改结束后解锁。 1. 访问打开的非当前文档 ```c# Document newDocument = Application.DocumentManager.Add("acad.dwt"); //锁定要操作的文档 using (DocumentLock l = newDocument.LockDocument()) { newDocument.Database.Insert(Matrix3d.Identity, db, true); } ``` 在`using`语句即将结束时,自动调用`Dispose`方法解锁以及释放资源。 ## Color 设置颜色 :one:通过`Color`类的静态函数`FromColorIndex`设置颜色 ```c# public static Color FromColorIndex(ColorMethod colorMethod, short colorIndex); ``` `ColorMethod`用于指定颜色的设置方法,例如通过索引、图层、块或RGB值。常见枚举成员: * ByLayer:颜色继承自对象所在的图层颜色,索引`256`。 * ByBlock:颜色继承自块定义的颜色,索引`0`。 * Byaci(AutoCAD Color Index):使用标准的AutoCAD颜色索引值(如1表示红色,3表示绿色等)。 :two:通过`Color`类的静态函数`FromRgb`设置颜色 ```c# public Color FromRgb(byte red, byte green, byte blue); ``` # 创建图形对象 ## `Database` 数据库结构: ```mermaid flowchart TD A(数据库) --> B(BlockTable) A--> LayerTable A-->otherTable A-->命名词典 ``` 所有数据库能存放的对象均已`DBObject`为基类,数据库对象存在以下两种状态: * 已实例化但未添加到图形数据库,可任意更改其非只读属性 * 添加到图形数据库需要已正确的模式打开。 * 已添加到图形数据库的对象均有一个`objectId`用于检索。 构造函数: ```c# public Database( bool buildDefaultDrawing, bool noDocument); //参数1:true时,建立的对象包含符号表等,flase时建立的对象完全为空 //参数2,是否和当前文档关联,true不关联(用户读取外部文件), //false为关联当前文档,可能会影响界面如图层 ``` :one: 属性 `database`中存放的各式表`id`,如下 | 表ID | 解释 | | ----------------- | ------------ | | `BlockTableId` | 块表ID | | `LayerTableId` | 层表ID | | `DimStyleTableId` | 尺寸样式表ID | | `...` | `...` | | 其他属性 | 解释 | | -------------------- | ---------------- | | `ObjectId TextStyle` | 设置当前文字样式 | | `ObjectId CLayer` | 设置当前图层 | | `ObjectId DimStyle` | 设置当前尺寸样式 | | `FileName` | 文件全地址(只读) | :two: 常用方法: | 签名 | 解释 | | ------------------------------------------------------------ | -------------------- | | `void ReadDwgFile` | 载入dwg格式文件 | | `void SaveAs(string fileName,DwgVersion version)` | 保存文件 | | `Void LoadLineTypeFile(string LineTypeName,string fileName)` | 载入线型文件 | | `Void Insert(Matrix3d transform,Database dataBase,bool preserveSourceDatabase )` | 插入实体(含块) | | `Database WbLock()` | 数据库之间的克隆 | | ` void SetDimstyleData(DimStyleTableRecord style)` | 防止出现样式替代问题 | :book:载入`dwg`格式文件 ```c# Document doc = Application.DocumentManager.MdiActiveDocument; Database currentDb = doc.Database; //database继承IDisposable接口,独立数据库必须释放资源 using (Database db = new Database(false, true)) { db.ReadDwgFile("C:\\Users\\Administrator\\Desktop\\user.dwg", FileOpenMode.OpenForReadAndAllShare,true,null); currentDb.Insert(Matrix3d.Identity, db, false); } ``` :red_circle: 使用`ReadDwgFile`方法时,需要一个完全为空的`Database`对象。 ```c# public void ReadDwgFile(string fileName, FileShare fileSharing, bool allowCPConversion, string password); //参数1:完整路径 //参数2 打开方式 //参数3:控制与操作系统编码不同的文件转换,如中文操作系统读取日文,true则直接转换,false询问用户。 //参数4,密码,无则null或空字符串。 ``` :book:`FileOpenMode`枚举类型 | 成员 | 系统变量 | 行为描述 | | :--------------------------- | :----------: | ------------------------------------------------------------ | | `OpenForReadAndAllShare` | `_SH_DENYNO` | 允许其他进程对文件进行读写。若文件已被其他进程以写入模式打开,仍可读取。 | | `OpenForReadAndReadShare` | `_SH_DENYWR` | 阻止其他进程写入文件,但允许读取。若文件已被其他进程写入,则读取失败。 | | `OpenForReadAndWriteNoShare` | `_SH_DENYRW` | 完全独占文件,禁止其他进程读写。若文件已被打开或为只读,则操作失败。 | | `OpenTryForReadShare` | - | 先尝试 `_SH_DENYWR`,失败后回退到 `_SH_DENYNO`(即允许读写共享)。 | :book:保存文件 ```c# Database db = HostApplicationServices.WorkingDatabase; string fileName = @"C:\Users\Administrator\Desktop\通用.dwg"; //完整路径,当前版本 db.SaveAs(fileName, DwgVersion.Current); ``` :book:加载线型 ```c# //当前活动文档 Document doc = Application.DocumentManager.MdiActiveDocument; //当前数据库 Database db = doc.Database; //加载线型,参数线型名称(支持通配符),线型文件名 db.LoadLineTypeFile("*", "acad.lin"); ``` :book:插入实体 ```c# Database db = HostApplicationServices.WorkingDatabase; Document doc = Application.DocumentManager.MdiActiveDocument; Document newDocument = Application.DocumentManager.Add("acad.dwt"); //锁定要操作的文档 using (DocumentLock l = newDocument.LockDocument()) { //定义对象插入时的矩阵变换,目标数据库,是否保留源数据 newDocument.Database.Insert(Matrix3d.Identity, db, true);//此处使用单位矩阵,不进行变换 } ``` 使用`WbLock`插入实体 ```c# Database db = HostApplicationServices.WorkingDatabase; Document doc = Application.DocumentManager.MdiActiveDocument; Document newDocument = Application.DocumentManager.Add("acad.dwt"); //锁定要操作的文档 using (DocumentLock l = newDocument.LockDocument()) { //临时数据库 Database copiedDb = db.Wblock(); //定义对象插入时的矩阵变换,目标数据库,不保留源数据 newDocument.Database.Insert(Matrix3d.Identity, copiedDb, false);//此处使用单位矩阵,不进行变换 } ``` 图元的继承关系如下: ```mermaid flowchart TD A(RXObject) --> B(Drawable) B --> C(DBobject) C --> D(Entity实体_可视化对象的基类) D --> E(Curve曲线) D --> F(Hatch填充) D-->DBText单行文字 D-->MText多行文字 D-->Dimension标注 D-->BlockReference E--> G(Line) E -->H(Circle) E -->I(Arc) E --> M(PolyLine) E --> Ellipse BlockReference --> Table ``` :bookmark:`Entity`常用属性 | 属性 | 解释 | | --------------------------------------- | ----------------------------------------------- | | ` public string Layer{set;get}` | 图层名称(层表记录名称),为实体指定图层 | | `public ObjectId LayerId{set;get};` | 图层`ObjectId`,为实体指定图层 | | `public string LineType{set;get}` | 线型名称,设置当前实体线型 | | `public ObjectId LineTypeId{set;get}` | 线型`Id`,设置当前实体线型 | | `public double LinetypeScale{set;get}` | 设置线型比例 | | `public LineWeight LineWeight{set;get}` | 设置实体线宽,枚举类型,可指定大小或随层,随块 | | `public Color Color{set;get;}` | 指定实体颜色 | | `public int ColorIndex{set;get}` | 通过颜色索引指定实体颜色 | | `bool Visible{get;set}` | 实体可视性 | | `string BlockName{get;}` | 所属块的名称,默认`Model_Space` | | `ObjectId BlockId{get;}` | 所属块的id,默认是`ModeSpace`的Id | :bookmark:`Entity`常用方法 | 方法 | 返回类型 | 作用 | | ----------------------------------------- | -------- | ------------------------ | | `IntersectWith` | void | 求实体交点(见集合计算) | | ` GetTransformedCopy(Matrix3d transform)` | `Entity` | 获取变换后的实体 | | `TransformBy(Matrix3d transform )` | `void` | 变换实体 | ## 直线 Line 类中提供两个构造函数: 1. `Line line = new Line();后续通过创建`Point3d`对象分别给直线的StartPoint以及 EndPoint`属性赋值。 2. `Line line = new Line(Point3d startLine,Pointd3 endLine)`,在创建直线对象时直接传入参数。 | 属性与方法 | 解释 | | ----------------------------------- | ---------------------------------------------------------- | | `double line.Angle{get;}` | 与 x 轴正方向夹角的弧度值 radian,如点(2,-2),弧度值为 5.49 | | `double line.Length{get;}` | 直线的长度 | | `Point3d line.StartPoint{set;get;}` | 起点 | | `Point3d line.EndPoint{set;get;}` | 终点 | | `Vector3d Delta{get;}` | 线段起点指向终点的向量 | ```csharp public void LineDemo() { //创建直线对象 Line line = new Line(); //声明两个坐标点对象 Point3d fir = new Point3d(0,0,0); Point3d sec = new Point3d(100, 100, 0); //将坐标点赋值给直线对象的属性 line.StartPoint = fir; line.EndPoint = sec; } ``` 此时的对象在内存中存储,我们还需要将其加载到`CAD`的模型空间中(`BlockTableRecord`)。 ![img](assets/1740818340396-8ef03490-ca60-49b9-91c2-5689c8ce1a81.png) 图形数据库由表组成,有块表,层表,样式表等;表的基本单位是记录。块表记录中存储的就是绘制的图形,我们要将直线添加到块表记录中。 :one:获取当前活动的图形数据库 `Database` :two:开启事务处理: 1.通知事务处理以只读方式打开块表 2.通知事务处理以写方式打开块表记录,即 CAD 的模型空间。 3.将实体添加到块表记录中 4.将对象添加到事务处理中 5.提交事务处理 事务处理优先选择`OpenColseTransaction`类,相较于`Transaction`读写效率高。 :book:方法 | 方法 | 解释 | | -------------------------------------------------- | -------------------------- | | `Abort()` | 撤销事务 | | `AddNewlyCreatedDBObject(DBObject obj,bool add );` | 将新增的对象添加进事务处理 | | `Commit()` | 提交事务,并关闭打开的对象 | | `GetObject( ObjectId id,OpenModemode mode)` | 打开对象,须在事务中进行 | 改变对象状态后可使用`Commit()`保存改变后的状态,如果发生异常,使用`Abort()`进行异常处理,撤销改变: ```c# using (OpenCloseTransaction trans = new OpenCloseTransaction()) { /// try { trans.Commit(); } catch { trans.Abort();//撤销事务 } } ``` :bookmark:事务处理的嵌套: ![image-20250417203012453](assets/image-20250417203012453.png) 在当前事务中新增一个事务处理,即事务的嵌套。 :one:在提交外层事务前,需要先提交内层事务。 :two:如果外层事务未提交或撤销改变,则任何操作都不会反映到文档。 ## 封装事务处理与直线 ### 事务处理封装 新建一个` Base.cs` 类库,封装将图元添加到实体的方法 `AddElemntToEntity`。 ```csharp public static class Base { /// /// 将单个线条添加到模型空间 /// /// 图形数据库 /// 图形对象 /// 返回图形对象的ObjcetId public static ObjectId AddElementToEntity(this Database db,Entity entity) { //开启事务处理 using (OpenCloseTransaction trans = new OpenCloseTransaction()) { //只读方式打开块表 BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId,OpenMode.ForRead); //以只写方式打开块表记录 BlockTableRecord btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace],OpenMode.ForWrite); //将线条添加到块表记录中 ObjectId objectId = btr.AppendEntity(entity); //将实体添加到事务处理中 trans.AddNewlyCreatedDBObject(entity,true); //提交事务 trans.Commit(); return objectId; } } /// /// 将多个图形对象一次性添加到模型空间 /// /// 图形数据库 /// 实体对象数组 /// 返回对象的ObjcetId数组 public static ObjectId[] AddElemntToEntity(this Database db, params Entity[] entities) { ObjectId[] objects = new ObjectId[entities.Length]; //开启事务处理 using (Transaction trans = db.TransactionManager.StartTransaction()) { //只读方式打开块表 BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead); //以只写方式打开块表记录 BlockTableRecord btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); for (int i = 0; i < entities.Length; i++) { //将线条添加到块表记录中 objects[i] = btr.AppendEntity(entities[i]); //将对象添加到事务处理 trans.AddNewlyCreatedDBObject(entities[i],true); } //提交事务 trans.Commit(); return objects; } } } ``` 每创建一次实体,`CAD` 会为实体对象分配一个` ObjectId`,关闭文件时,`ID` 被销毁。每次打开文件,`ObjectId`都不相同,但是每次编辑时`ObjectId` 都相同。 实体的`ObjectId`存储在`BlockTableRecord`中,遍历块表即可得到实体`id`; ```c# foreach(var obj in btr) { //遍历块表记录的到实体id } ``` ### 直线封装 新建一个` DraWTool.cs `文件,添加 `DrawLine` 方法。 ```csharp internal static class DrawTool { /// /// 画直线(单条) /// /// 图形数据库 /// 直线对象 /// 返回ObjectId public static ObjectId DrawLine(this Database db, Line line) { return db.AddLineToEntity(line); } /// /// 根据起点,长度,角度画线 /// /// 图形数据库 /// 起点 /// 长度 /// 角度 /// 返回图形ObjectId public static ObjectId DrawLine(this Database db,Point3d startPoint, double Length,double degree) { //角度转弧度 double radian = degree.GetDegreeToRadian(); //获取终点x,y值。 double x = startPoint.X + Math.Cos(radian)*Length; double y = startPoint.Y + Math.Sin(radian)*Length; //添加直线到模型空间,返回一个ObjcetId return db.AddLineToEntity(new Line(startPoint,new Point3d(x,y,0))); } } ``` 在角度画线时,先将角度转成弧度,在 `MathTool` 类中封装。 ```csharp internal static class MathTool { /// /// 角度转弧度 /// /// 角度 /// 返回弧度 public static double GetDegreeToRadian(this double degree) { return Math.PI * degree / 180; } } ``` ## 圆弧 ### 创建圆弧对象 提供三种构造函数。 1. `Arc(Point3d center, Vector3d normal, double radius, double startAngle, double endAngle)`中心,平面法向量,半径,起始弧度,终止弧度 2. `Arc(Point3d center, double radius, double startAngle, double endAngle)`——中心,半径,起始弧度,终止弧度 3. `Arc()`,默认创建半径为 0,法向量(0,0,1),平行于 xy 平面的圆弧 ```csharp public void ArcDemo() { //构造函数1 Arc arc = new Arc(new Point3d(0,0,0),20,0,Math.PI/2); //构造函数2 Arc arc1 = new Arc(new Point3d(0, 0, 0), new Vector3d(0, 0, 1), 100, 0, Math.PI / 2); //获取当前图形数据库 Database db = HostApplicationServices.WorkingDatabase; //添加实体 db.AddLineToEnity(arc,arc1); } ``` `Vector3d normal`法向量`(x,y,z)`,(0,0,1)代表当前平面平行于` xy` 平面,(1,0,0)代表 平行于 `yz` 平面 :red_circle: 在AutoCAD中圆弧是以逆时针方向画的。 ### 三点画圆弧 :red_circle: `CircularArc3d` 是几何类对象,不能添加到图形数据库,但提供一些数据计算。 :red_circle: 用 `Vector3d`对象获取圆心到起点向量以及中心到终点的向量,然后分别计算与 X 轴正方向的弧度。 或者使用二维向量的 `Angel `属性获取与 X 轴正方向的弧度(参照封装代码)。 ```csharp public void ArcDemo() { Database db = HostApplicationServices.WorkingDatabase; Point3d startPoint = new Point3d(50, 100, 0); Point3d onArcPonit = new Point3d(0,200, 0); Point3d endPoint = new Point3d(-100, 300, 0); ///接受三个点,起点,在圆上的点,以及终点 CircularArc3d cArc = new CircularArc3d(startPoint,onArcPonit,endPoint); //中心到起点的向量 Vector3d cs = cArc.Center.GetVectorTo(startPoint); //中心到终点的向量 Vector3d ce = cArc.Center.GetVectorTo(endPoint); //x轴正方向的单位向量 Vector3d x = new Vector3d(1, 0, 0); //计算向量之间的弧度 double startAnge = cs.Y>0? cs.GetAngleTo(x):-cs.GetAngleTo(x); double endAngle = ce.Y >0?ce.GetAngleTo(x):-cs.GetAngleTo(x); Arc arc = new Arc(cArc.Center,cArc.Radius,startAnge,endAngle); db.AddLineToEnity(arc); } ``` ### 封装 ```csharp internal static class DraWTool { /// /// 圆心,半径,起始角度,终止角度画弧 /// /// 图形数据库 /// 圆心 /// 半径 /// 起始角度 /// 终止角度 /// public static ObjectId DrawArc(this Database db,Point3d center, double radius,double startDegree,double endDegree) { //角度转弧度 double startRadian = startDegree.GetDegreeToRadian(); double endRadian = endDegree.GetDegreeToRadian(); Arc arc = new Arc(center,radius,startRadian,endRadian); return db.AddLineToEntity(arc); } /// /// 三点画弧 /// /// 图形数据库 /// 起点 /// 圆弧上的点 /// 终点 /// 返回图形的ObjectId public static ObjectId DrawArc(this Database db,Point2d startPoint,Point2d onArcPoint,Point2d endPoint) { ///判断三点是否共线 if(startPoint.JudgeIsCollinear(onArcPoint,endPoint)) { //共线返回Null return ObjectId.Null; } else { CircularArc2d cArc = new CircularArc2d(startPoint, onArcPoint, endPoint); //获取圆心半径 double radius = cArc.Radius; Point3d center = new Point3d(cArc.Center.X, cArc.Center.Y, 0); //获取起始弧度终止弧度 Vector2d cs = cArc.Center.GetVectorTo(startPoint); double startRadian = cs.Angle;//起始弧度 Vector2d ce = cArc.Center.GetVectorTo(endPoint); double endRadian = ce.Angle;//终止弧度 Arc arc = new Arc(center, radius, startRadian, endRadian); return db.AddLineToEntity(arc); } } /// /// 起点,圆心,角度画弧 /// /// 图形数据库 /// 起点 /// 圆心 /// 角度(不能为负数) /// public static ObjectId DrawArc(this Database db,Point3d startPoint,Point3d center,double degree) { if(degree <= 0) { return ObjectId.Null; } else { //获取半径 double radius = center.DistanceTo(startPoint); Vector2d temp = new Vector2d(startPoint.X - center.X, startPoint.Y - center.Y); //起点弧度 double startRadian = temp.Angle; //终点弧度 double endRadian = startRadian + degree.GetDegreeToRadian(); Arc arc = new Arc(center, radius, startRadian, endRadian); return db.AddLineToEntity(arc); } } } ``` 三点画弧时,需要先判断三点是否共线。 ```csharp /// /// 判断三点是否共线 /// /// 第一个点 /// 第二个点 /// 第三个点 /// 共线返回true,否则返回false public static bool JudgeIsCollinear(this Point2d startPoint, Point2d onArcPoint, Point2d endPoint) { //获取从中点到端点的两个向量 Vector2d v1 = onArcPoint.GetVectorTo(startPoint); Vector2d v2 = onArcPoint.GetVectorTo(endPoint); //计算两个向量的夹角 if(v1.GetAngleTo(v2) == 0 || v1.GetAngleTo(v2)==Math.PI) { return true; } else { return false; } } ``` ## 圆 圆有两个构造函数: :one:是空的构造函数 :two:要求传入参数:圆心,平面法向量,半径。 ```csharp Circle circle =new Circle(Point3d center,Vector3d normal,double radius) //Vector3d对象是矢量对象,(0,0,1)代表与Z轴平行,与xy平面垂直,Vector3d.ZAxis与(0,0,1)相同 ``` ```csharp public void CircleDemo() { //当前图形数据库 Database db = HostApplicationServices.WorkingDatabase; //创建圆对象 Circle circle = new Circle(new Point3d(0, 0, 0), new Vector3d(0, 0, 1), 20); //添加到实体 db.AddLineToEnity(circle); } ``` 封装到 `DrawTool `类中。 ```csharp internal static class DrawTool { /// /// 圆心半径画圆 /// /// 图形数据库 /// 圆心 /// 半径 /// 图形的ObjectId public static ObjectId DrawCircle(this Database db,Point3d center,double radius) { //创建圆对象 Circle circle = new Circle(center,new Vector3d(0,0,1),radius); return db.AddLineToEntity(circle); } /// /// 两点画圆 /// /// 图形数据库 /// 起点 /// 终点 /// 返回图形的ObjectId public static ObjectId DrawCircle(this Database db,Point3d startPoint,Point3d endPoint) { //获取半径 double radius = startPoint.DistanceTo(endPoint)/2; //获取圆心 Point3d center = new Point3d((startPoint.X+endPoint.X)/2, (startPoint.Y+endPoint.Y)/2,0); return db.DrawCircle(center, radius); } /// /// 三点画圆 /// /// 图形数据库 /// 第一个点 /// 第二个点 /// 第三个点 /// public static ObjectId DrawCircle(this Database db,Point3d p1,Point3d p2,Point3d p3) { //使用CircularArc3d对象 CircularArc3d cArc = new CircularArc3d(p1, p2, p3); Point3d center = cArc.Center; double radius = cArc.Radius; return db.DrawCircle(center, radius); } } ``` ## 椭圆 :book: 构造参数 ```csharp Ellipse el = new Ellipse(new Point3d(0,0,0),Vector3d.ZAxis, new Vector3d(-45,-45,0),0.3,0,Math.PI/2); //参数1:椭圆中心 Point3d //参数2 所在平面的法向量,ZAxis,即z轴,xy平面的法向量 //参数3 椭圆长半轴向量(主矢向量),从椭圆中心指向椭圆起点,即从此处逆时针画椭圆 //参4,短轴/长轴比例,不能大于1,否则会创建长半轴与短半轴均为1的椭圆 //参5.6,起点弧度与终点弧度,但椭圆特殊,是以长半轴向量作为起点开始画弧 //参5,6主要作用是提供椭圆的总弧度 ``` :package: 封装 ```csharp internal static class EllipseTool { /// /// 椭圆 /// /// /// 椭圆中心 /// 长半轴 /// 短半轴 /// 主轴与X轴方向的夹角 /// ObjectId public static ObjectId DrawEl(this Database db, Point3d center, double longRadius, double shortRadius, double degree) { //声明主轴矢量 Vector3d majorAxis = new Vector3d(longRadius * Math.Cos(degree.GetDegreeToRadian()), longRadius * Math.Sin(degree.GetDegreeToRadian()), 0); //比例 double rate = shortRadius / longRadius; //半个椭圆 Ellipse el = new Ellipse(center, Vector3d.ZAxis, majorAxis, rate, 0, Math.PI); return db.AddLineToEntity(el); } /// /// 端点,短半轴画椭圆,以右端点逆时针绘制 /// /// 图形数据库 /// 左端点 /// 右端点 /// 短半轴 /// 返回ObjectId public static ObjectId DrawEl(this Database db,Point3d p1,Point3d p2,double shortRadius) { //输入的短半轴比长半轴长,则退出 double longRadius = p1.DistanceTo(p2) / 2; if(shortRadius> longRadius) { return ObjectId.Null; } //计算椭圆中心 Point3d center = new Point3d((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2,0); //将p1作为长轴左端点,p2作为长轴右端点 if(p1.X > p2.X) { Point3d temp = p1; p1 = p2; p2 = temp; } //声明主轴矢量 Vector3d majorAxis = center.GetVectorTo(p2); double rate = shortRadius / center.DistanceTo(p2);//获取比例 Ellipse el = new Ellipse(center, Vector3d.ZAxis, majorAxis, rate, 0, Math.PI); return db.AddLineToEntity(el); } /// /// 两点画椭圆 /// /// 图形数据库 /// 对角点1 /// 对角点2 /// ObjectId public static ObjectId DrawEl(this Database db,Point3d p1,Point3d p2) { //判断两点是否共线 if (p1.X == p2.X || p1.Y == p2.Y) return ObjectId.Null; //不共线则 //计算圆心(自定义方法,求两点的中心点) Point3d center = MathTool.GetMidPoint(p1,p2); //计算X方向的短半轴 double radiusX = Math.Abs(p1.X - center.X); //计算Y方向的短半轴 double radiusY = Math.Abs(p1.Y - center.Y); //确定比例 double rate = radiusX > radiusY ? (radiusY / radiusX) : (radiusX / radiusY); //判断主矢方向 Vector3d majorAxis; if (radiusX >= radiusY) { //以X方向半轴为长半轴 majorAxis = new Vector3d(radiusX, 0, 0); } else { //以Y方向半轴为长半轴 majorAxis = new Vector3d(0, radiusY, 0); } //声明椭圆对象 Ellipse el = new Ellipse(center, Vector3d.ZAxis, majorAxis, rate, 0, Math.PI * 2); return db.AddElementToEntity(el); } } ``` ## 多段线 构造函数参数: :one:`Polyline p = new Polyline() `,空构造参数 :two:`Polyline p = new Polyline(int points) `,指定顶点数量(数量大时,提前指定数目,会在堆中开辟一个空间,注意此时的顶点为0,目的是不必在后期反复扩容,优化性能) ```:one: public void CircleDemo() { Polyline pl = new Polyline(); Point2d point1 = new Point2d(0, 0); Point2d point2 = new Point2d(100, 100); Point2d point3 = new Point2d(100, 200); pl.AddVertexAt(0, point1, 0, 0, 0);//添加顶点Vertex pl.AddVertexAt(1, point2, 0, 0,0); pl.AddVertexAt(2, point3, 0, 0, 0); pl.Closed = true;//闭合 pl.ConstantWidth = 10;//整体宽度,以线段为中心,向两边各加5 Database db = HostApplicationServices.WorkingDatabase; db.AddLineToEnity(pl); } ``` :bookmark: 为多段线添加顶点,`pl.AddVertexAt(int index, point2d point, double 凸度, double 起始宽度,double 终止宽度);` 参数:one: 插入点,使用`NumberOfVertices`属性按顺序插入,也可以将`index`设置为0,后续点将占据0点位置。 参数:two: 表示要添加的顶点`vertex`位置 参数:three:凸度,当前顶点与下一个顶点形成弧的角度的四分之一的正切值。 1. 凸度为正值,起点逆时针到终点 ,若为负值,起点顺时针到终点。 2. 0 为直线,1 为半圆。 3. 小于 1 叫劣弧,大于 1 叫优弧 ```csharp //绘制实心圆示例 public void CircleDemo() { Database db = HostApplicationServices.WorkingDatabase; Polyline pl = new Polyline(); //使用源点加向量方式创建点 pl.AddVertexAt(pl.NumberOfVertices, Point2d.Origin, 1,1, 1); pl.AddVertexAt(pl.NumberOfVertices,Point2d.Origin + Vector2d.XAxis, 1, 1, 1); pl.Closed = true; db.AddElementToEntity(pl); } ``` :bookmark: 多段线常用属性与方法 ![image-20250320221705048](assets/image-20250320221705048.png) :red_circle:两个点设置闭合无效,效果等同于不闭合。 :red_circle:使用`SetXXX`,`RemoveVertexAt`方法时,若实体已经添加进模型空间,须开启事务处理进行设置。 :package: 封装 ```csharp public static class PolyLineTool { /// /// 多段线 /// /// 图形数据库 /// 是否闭合 /// 整体宽度 /// 顶点对象 /// 返回多段线ObjectId public static ObjectId DrawPolyLine(this Database db, bool isClosed, double width, params Point2d[] vertex) { if (vertex.Length < 2) { //无法创建多段线 return ObjectId.Null; } else { Polyline polyline = new Polyline(vertex.Length); for (int i = 0; i < vertex.Length; i++) { //添加顶点 polyline.AddVertexAt(i, vertex[i], 0, 0, 0); } if (isClosed) { polyline.Closed = true; } //设置整体宽度 polyline.ConstantWidth = width; return db.AddLineToEntity(polyline); } } } ``` ### 矩形 :key:根据指定两点计算四个矩形点坐标,并加入到多段线对象中。 ```csharp public static ObjectId DrawRectangle(this Database db, Point2d p1, Point2d p2) { //判断是否能画矩形 if (p1.X == p2.X || p1.Y == p2.Y) { Editor editor = Application.DocumentManager.MdiActiveDocument.Editor; editor.WriteMessage("样点错误,无法绘制矩形"); return ObjectId.Null; } //若能画矩形,继续向下执行 //起点从x最小,y最小处逆时针绘制 Point2d recP1 = new Point2d(Math.Min(p1.X,p2.X),Math.Min(p1.Y,p2.Y)); Point2d recP2 = new Point2d(Math.Max(p1.X, p2.X), Math.Min(p1.Y, p2.Y)); Point2d recP3 = new Point2d(Math.Max(p1.X, p2.X), Math.Max(p1.Y, p2.Y)); Point2d recP4 = new Point2d(Math.Min(p1.X, p2.X), Math.Max(p1.Y, p2.Y)); return db.DrawPolyLine(true, 0, recP1, recP2, recP3, recP4); } ``` ### 正多边形 :bookmark:内接于圆正多边形封装(根据圆心与角度计算顶点的坐标): ```csharp public static ObjectId DrawPloygon(this Database db,Point2d center,double radius,int sideNum,double degree) { //边数小于3无法创建多边形 if(sideNum < 3) { return ObjectId.Null; } Polyline pLine = new Polyline(); //角度转弧度 double radian = degree.GetDegreeToRadian(); for(int i = 0; i < sideNum; i++) { pLine.AddVertexAt(i,new Point2d(center.X + radius*Math.Cos(radian),center.Y+ radius*Math.Sin(radian)), 0,0,0); radian += (2 * Math.PI / sideNum); } //闭合 pLine.Closed = true; return db.AddLineToEntity(pLine); } ``` :key:外切于圆要先转换为内接于圆。 ## 图案填充 ### 单个图形填充 填充的基本步骤 ```mermaid flowchart TD a(创建Hatch对象) --> b(填充比例,PatternScale) b --> c(设置填充图案:SetHatchPattern) b --> d(设置填充颜色) subgraph 填充颜色 d --> 1(线条颜色) d -->2(背景颜色) end 1 --> e(添加到模型空间) 2 --> e c --> e e --> f(设置填充弧度,PatternAngel) f --> g(设置图元与填充的关联性Associate) g --> h(添加边界,AppendLoop) h --> i(计算填充,EvaluateHatch) i-->j(提交事务) ``` :book:`AppendLoop`方法 ```c# //需要图形添加进数据处理库 public void AppendLoop(HatchLoopTypes loopType, ObjectIdCollection dbObjIds); //可使用内存中的实体对象 public void AppendLoop(HatchLoop hatchLoop) //多段线示例,此时边界类型必须为PolyLine HatchLoop hpp = new HatchLoop(HatchLoopTypes.Polyline); for (int i = 0; i < pl.NumberOfVertices; i++) {//pl是定义的多段线对象 //添加带凸度的顶点 hpp.Polyline.Add(new BulgeVertex(pl.GetPoint2dAt(i),pl.GetBulgeAt(i))); } //多段线需要封闭 hpp.Polyline.Add(new BulgeVertex(pl.GetPoint2dAt(0), pl.GetBulgeAt(0))); ha.AppendLoop(hpp); //圆示例 HatchLoop hp = new HatchLoop(HatchLoopTypes.Default); CircularArc2d a = new CircularArc2d(Point2d.Origin + Vector2d.YAxis * 0.4 - Vector2d.XAxis * 0.7, 0.2); hp.Curves.Add(a); ha.AppendLoop(hp); ``` :book:`HatchLoop`属性 | 属性 | 作用 | | -------------------------------- | ------------------ | | `Curve2dCollection Curves` | 曲线封闭边界 | | `BulgeVertexCollection Polyline` | 多段线作为封闭边界 | :red_circle:非闭合图元在添加边界时会报错。 ```csharp public void HatchDemo() { Database db = HostApplicationServices.WorkingDatabase; ObjectId objId = db.DrawCircle(new Point3d(0, 0, 0), 220); //objectid集合,与泛型相似 ObjectIdCollection objs = new ObjectIdCollection(); objs.Add(objId); //开启事务处理 using (Transaction trans = db.TransactionManager.StartTransaction()) { Hatch hatch = new Hatch();//创建一个空的hatch对象 hatch.PatternScale = 5;//填充比例 hatch.SetHatchPattern(HatchPatternType.PreDefined, "ANGLE");//设置填充图案 //打开块表 BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; //打开块表记录 BlockTableRecord btr = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; //将hatch对象添加到模型空间 btr.AppendEntity(hatch); //更新 trans.AddNewlyCreatedDBObject(hatch, true); hatch.PatternAngle = Math.PI / 4;//设置填充角度 //设置关联,线条改变则填充区域跟着改变 hatch.Associative = true; //添加填充边界 hatch.AppendLoop(HatchLoopTypes.Outermost, objs); //计算填充并显示 hatch.EvaluateHatch(true); //提交 trans.Commit(); } } ``` CAD 填充 0 度默认图形于X轴正方向夹角45 度填充,需要隐式减去`Π/4` ```csharp internal static class HatchTool { public enum HatchPattern { ANSI34,//金属 ANSI35, ANSI36, ANSI37//非金属 } public static ObjectId ToHatch(this Database db, HatchPattern hatchName, double scale ,double hatchDegree,Color bkColor,int colorIndex,ObjectId id) { ObjectId patternId = ObjectId.Null; //事务处理 using (Transaction trans = db.TransactionManager.StartTransaction()) { Hatch ha = new Hatch();//创建Hatch对象 ha.PatternScale = scale;//设置填充比例 //转换枚举 string name = hatchName.ToString(); //设置填充样式,使用预定义类型 ha.SetHatchPattern(HatchPatternType.PreDefined, name); //设置背景色 ha.BackgroundColor = bkColor;//本身具有的属性 //前景色 ha.ColorIndex = colorIndex;//继承于Entity //打开块表 BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; //打开块表记录 BlockTableRecord btr = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; //将填充对象添加到模型空间 patternId = btr.AppendEntity(ha); //确认添加 trans.AddNewlyCreatedDBObject(ha,true); //提交之前,设置相关属性 ha.PatternAngle = hatchDegree.GetDegreeToRadian() - Math.Pi/4;//角度设置 ha.Associative = true;//关联图案 ObjectIdCollection ids = new ObjectIdCollection(); ids.Add(id); ha.AppendLoop(HatchLoopTypes.Outermost, ids);//填充边界设置 ha.EvaluateHatch(true);//估算填充并显示 //提交 trans.Commit(); } return patternId; } } ``` 调用程序 ```csharp public void PolyLineDemo() { Database db = HostApplicationServices.WorkingDatabase; ObjectId obj = db.DrawCircle(new Point3d(0, 0, 0), 300); db.ToHatch(HatchTool.HatchPattern.ANSI34,10,0,Color.FromRgb(1,0,0),1,obj); } ``` ### 多个图形填充 填充边界设置`HatchLoopTypes`,有`OuterMost` 与 `Default`,`PolyLine(边界由多段线构成)`: :one: `OuterMost` 从最外边界开始填充,遇到内边界停止填充,直到遇到另一个 内部 边界 开启填充。 :two:如果只有外边界,则会全部填充。 ![img](assets/1741353285220-5fc81d5c-365b-4956-a9c6-43fc1e3fd13c.png) :three:目前依旧以书籍为准,最外层为边界设置为`OuterMost` ,内层边界设置为 `Default`。 :red_circle: 边界必须是密闭图形,否则会报错。 ```csharp //结构中声明只读字段 public struct PatterName { public static readonly string ANSI34 = "ANSI34"; } /// /// 多填充 /// /// 图形数据库 /// 填充比例 /// 颜色索引 /// 填充图案 /// 填充角度 /// 图元组id /// 返回填充id public static ObjectId AddHatchToEntity(this Database db,double scale, byte colorIndex, string patterName, double degree, ObjectId[] elementIds) { //填充对象 Hatch ha = new Hatch(); //设置填充比例 ha.PatternScale = scale; //填充颜色 ha.ColorIndex = colorIndex; //填充图案 ha.SetHatchPattern(HatchPatternType.PreDefined, patterName); using (Transaction trans = db.TransactionManager.StartTransaction()) { //打开块表 BlockTable bt = trans.GetObject(db.BlockTableId,OpenMode.ForRead) as BlockTable; //打开块表记录 BlockTableRecord btr = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; //添加进模型空间 ObjectId haId = btr.AppendEntity(ha); trans.AddNewlyCreatedDBObject(ha,true); //设置填充角度 double radian = MathTool.ByDegreeToRadian(degree); ha.PatternAngle = radian - Math.PI / 4; //关联性设置 ha.Associative = true; ObjectIdCollection collection = new ObjectIdCollection(); //添加边界 for (int i = 0; i < elementIds.Length; i++) { //清空 collection.Clear();//或者使用RemoveAt方法 collection.Add(elementIds[i]); //添加边界确保集合内只有一个图元id,给当前图元id添加边界 if(i == 0) ha.AppendLoop(HatchLoopTypes.Outermost, collection); ha.AppendLoop(HatchLoopTypes.Default, collection); } //计算并填充 ha.EvaluateHatch(true); trans.Commit();//提交事务 return haId; } } } ``` ## 文字 ### 单行文字 单行文字`DBText`只有一个无参的构造函数,默认创建插入点(基点),对齐点均为`(0,0,0)`,高度为0的文字。 `cad`中单行文字默认垂直方向基线对齐,即`TextBase`,水平方向为左对齐`TextLeft` ![image-20250417210744164](assets/image-20250417210744164.png) :bookmark:常用属性 | 属性 | 作用 | | --------------------------------------------------- | ---------------- | | `public Point3d Position{set;get}` | 插入点 | | `public string TextString{set;get}` | 文本值 | | `public double Height{set;get}` | 文字高度 | | `public string TextStyleName{get;}` | 获取文字样式名称 | | `ObjectId TextStyleId` | 设置文字样式 | | `public Point3d AlignmentPoint{set;get}` | 文字对齐点 | | `public TextVerticalMode VerticalMode{set;get}` | 垂直对齐方式 | | `public TextHorizontalMode HorizontalMode{set;get}` | 水平对齐方式 | | `public double WidthFactor{set;get}` | 宽度系数 | | `double Rotation{set;get}` | 旋转弧度 | ```c# public void TextDemo() { Database db = HostApplicationServices.WorkingDatabase; DBText text = new DBText();//当行文字对象 text.Position = new Point3d(200, 200, 0);//插入点 text.Height = 3.5 * 20;//文字高度 text.ColorIndex = 2;//文字颜色,继承于Entity text.TextString = "技术要求";//文字内容 text.Rotation = Math.PI / 4;//旋转角度 db.AddElementToEntity(text); } ``` 文字设置对齐时是参照对齐点来进行对齐(左对齐不在此类),如果设置对齐点同时又设置插入点,对齐时会自动调整`Position`属性,所以设置对齐样式时无需插入点。 :red_circle:当`HorizontalMode`为`TextHorizontalMode.TextLeft`,且`VerticalMode`为`TextVerticalMode.TextBase`(垂直方向默认是`TextBase`)时,不要设置`AlignmentPoint`属性,否则会报错. 对齐图例: ![image-20250417210932386](assets/image-20250417210932386.png) 对齐常用枚举值: * `TextHorizontalMode.TextLeft`,水平左对齐(:red_circle:参照插入点) * `TextHorizontalMode.TextCenter`,水平居中(参照对齐点) * `TextHorizontalMode.TextRight`,水平右对齐(参照对齐点) * `TextHorizontalMode.TextMid`,正中(参照对齐点) * `TextVerticalMode.TextVerticalMid`,垂直居中(参照对齐点) ```c# public void TextDemo() { Database db = HostApplicationServices.WorkingDatabase; DBText text = new DBText(); //text.Position = Point3d.Origin + Vector3d.XAxis * 100; text.TextString = "文本"; //text.Rotation = Math.PI / 4; text.Height = 100; text.VerticalMode = TextVerticalMode.TextVerticalMid; text.HorizontalMode = TextHorizontalMode.TextCenter; //对齐点设置在对齐方式之后,因为要重置左对齐为其他对齐方式才能设置对齐点 text.AlignmentPoint = new Point3d(100, 100, 0); db.AddElementToEntity(text); } ``` 特殊字符 特殊字符可使用`unicode`字符或CAD中的控制码表示或者直接使用输入法。 ![image-20250417212126100](assets/image-20250417212126100.png) ```c# struct TextSpecialSymbol { public static readonly string _degree = @"\U+00B0";//° public static readonly string _diameter = @"\U+2205";//直径控制码%%d public static readonly string _tolearance = @"\U+00B1";//公差 public static readonly string _alomostEqual = @"\U+2248";//≈ public static readonly string _underLine = @"%%u";//下划线 控制码 } public class Program { [CommandMethod("1")] public void TextDemo() { Database db = HostApplicationServices.WorkingDatabase; DBText text = new DBText(); //文本下划线设置(用下划线包裹字体就可) text.TextString = $"{TextSpecialSymbol._diameter}2000" + $"{TextSpecialSymbol._tolearance}{TextSpecialSymbol._underLine}2{TextSpecialSymbol._underLine}"; text.Position = Point3d.Origin + Vector3d.YAxis*50; text.Height = 50; db.AddElementToEntity(text); } } ``` ### 多行文字 多行文字多用来在一个区域写文字,设置文本框的名义宽度,一般情况下,文字不会超出文本框宽度。 | 属性 | 作用 | | :------------------------- | ------------------------------------------------------------ | | Location | 文本框插入点(左上角) | | Height | 文本框名义高度(文本可超出该 高度) | | Weight | 文本框名义宽度(若该行以英文单词开头且宽度不足时,会超出文本框宽度;若英文单词从行中间或结尾开始,宽度不足时会换行) | | string Contents {set;get} | 文本内容 | | `string Text{get;}` | 获得去除格式化代码的文本内容 | | double TextHeight{set;get} | 字体高度 | | `Attachment` | 对齐方式,枚举类型 | `Mtext.ParagraphBreak{get;}` 换行,与`\n`作用一致 ```c# public static void AddMText(string value,double height,double width,double textHeight) { Database db = HostApplicationServices.WorkingDatabase; MText text = new MText(); text.Contents = value;//文字内容 text.Location = new Point3d(200, 200, 0);//文字插入点,左上角 text.Width = width; text.TextHeight = textHeight;//文本高度 db.AddElementToEntity(text); } ``` :book: `Attachment`对齐方式: 以`Location`插入点为对齐点,如`AttachmentPoint.MiddleLeft`表示垂直居中水平靠左。 :red_circle:`BaseXX`会报错,可能不是有效的枚举值。 多行文字格式化代码: ![image-20250417220505148](assets/image-20250417220505148.png) ```c# string text = "{\\A1;1\\H<堆叠高度比例>x\\S<上标>^<下标>;}";//单行文字不支持 ``` * `\S`表示强制堆叠,堆叠符号有如下: * `^`,公差堆叠 * `/`,垂直堆叠,有水平线分割 * `#`,对角形式堆叠,如`1/2` ```c# //Φ50+0.1/-0.2mm t.Contents = "{\\A1;%%C50\\S+0.1^-0.2;}{\\A1;mm}"; ``` 多行文字的格式化代码`{}`区域可看作整体,外部对齐由`Attachment`决定,内部对齐方式由`\A`决定(从`\A`起到`}`结束),互不影响。 ## 标注 ![](assets/image-20250402183723542.png) ```mermaid flowchart TD B(Entity)-->A A(Dimesion标注)-->RotatedDimesion A --> AlignedDimesion ``` `Dimesion`类是抽象类,不能实例化,但提供了一些属性。 | 属性 | 释义 | | ------------------------------------ | ---------------- | | `string DimensionText{set;get}` | 替换标注文字 | | `Point3d TextPosition{set;get}` | 标注文本的坐标 | | `double TextRotation{set;get}` | 标注文本旋转弧度 | | `string Prefix{set;get}` | 前缀 | | `Suffix{set;get}` | 后缀 | | `string DimensionStyleName{set;get}` | 标注样式名称 | | `ObjectId Dimblk/Dimblk1/Dimblk2` | 箭头样式Id | 其他属性继承于标注样式即可。 :bookmark:箭头属于块参照,使用前先加载自定义块. `DIMBLK`系统全局变量:在CAD操作界面设置该变量会在`BlockTable`中增加对应的箭头块定义,同时将当前标注样式的箭头设置为对应的箭头。 使用`DIMBLK1`,`DIMBLK2`系统全局变量时,需要设置`DIMSAH`系统变量,而后设置`DIMBLK1/2`系统变量。 :bookmark:`DIMSAH`(系统变量)用于控制尺寸线箭头块的显示,初始为关闭状态。 | 值 | 说明 | | :---- | :------------------------------------------- | | 0关闭 | 由 `DIMBLK` 系统变量设置箭头块 | | 1打开 | 由 `DIMBLK1` 和` DIMBLK2 `系统变量设置箭头块 | > 1. `DIMBLK`的初始值为空字符串`""`,要恢复默认设置(实心闭合箭头显示),输入单个句点 (.) > 2. 常用值有`_DOTSMALL(小点)`,`_OBLIQUE(倾斜),_NONE(无箭头),`详情参考[DIMBLK(系统变量)](https://help.autodesk.com/view/ACD/2018/CHS/?guid=GUID-6E09DCCA-313F-4FF4-BB1B-F41B512B9CC9) ```c# //验证代码 Application.SetSystemVariable("DIMBLK", "."); bool b = Application.GetSystemVariable("DIMBLK").ToString() == ""; Application.ShowAlertDialog(b.ToString());//true ``` 在代码中改变箭头样式需要先使用系统变量加入箭头自定块→设置尺寸`TableRecord`的`DimBlk`属性,若需要分别设置箭头,将`Dimsah`为true→设置`Dimblk1/2`的属性→`DIMBLK`系统全局变量重置为默认值。 ### 旋转标注`RotatedDimesion` ![image-20250402215416418](assets/image-20250402215416418.png) | 属性 | | | --------------------- | -------------------------------------------- | | XLine1Point{set;get} | 尺寸界限1起点 | | XLine2Point{set;get} | 尺寸界限2起点 | | DimLinePoint{set;get} | 用来确定尺寸线位置(尺寸线或其延长线经过该点) | | Rotation{set;get} | 尺寸旋转弧度 | ```c# public void Demo() { Database db = HostApplicationServices.WorkingDatabase; Line line = new Line(Point3d.Origin, (Point3d.Origin - Vector3d.XAxis * 100-Vector3d.YAxis * 200)); //旋转标注 RotatedDimension rotated = new RotatedDimension(); rotated.XLine1Point = line.StartPoint; rotated.XLine2Point = line.EndPoint; Point3d mid = MathTool.GetMidPoint(line.StartPoint, line.EndPoint); rotated.DimLinePoint = mid +Vector3d.YAxis*30; rotated.Rotation = line.Angle;//弧度设置 rotated.ColorIndex = 3; //文本旋转 rotated.TextRotation = line.Angle-Math.PI; //三者可同时存在 rotated.Prefix = "L=";//前缀 rotated.Suffix = "m";//后缀 rotated.DimensionText = "exit?<>"; db.AddElementToEntity(rotated,line); } ``` :red_circle:`DimesionText`属性可以完全替换主标记值,或者给标记值加前缀或后缀(需要使用`<>`,主标记值连同`PreFix`以及`Suffix`属性值会替换`<>`). ```c# rotate.DimensionText = "~<>";//给标注文字加前缀 rotate.DimensionText = "XXX";//完全替换 ``` ### 对齐标注AlignedDimension ![image-20250402215356693](assets/image-20250402215356693.png) 对齐标注与尺寸界限两点连成的线段平行。 没有有`Rotation`属性,其余属性与`RotatedDimesion`类相同. ```c# public void Demo() { Database db = HostApplicationServices.WorkingDatabase; Line line = new Line(Point3d.Origin, (Point3d.Origin - Vector3d.XAxis * 100-Vector3d.YAxis * 200)); //对齐标注 //没有Rotation属性,自动与两点形成的线段平行 AlignedDimension aligned = new AlignedDimension(); aligned.XLine1Point = line.StartPoint; aligned.XLine2Point = line.EndPoint; aligned.DimLinePoint = new Point3d(-50, -80, 0); aligned.ColorIndex = 3;//设置颜色 db.AddElementToEntity(line, aligned); } ``` ### 角度标注LineAngularDimesion2 ![image-20250402215337784](assets/image-20250402215337784.png) 属性: :one:`Xline1Start`,`XLine1End`,尺寸界限1起点,端点(两点确定一条线) :two:`XLine2Start`,`XLine2End`,尺寸界限2起点,端点 :three:`ArcPoint`,尺寸线位置(:red_circle:尺寸线会经过该点)。 ```c# public void Demo() { Database db = HostApplicationServices.WorkingDatabase; Line line1 = new Line(new Point3d(0, 0, 0), new Point3d(200, 0, 0)); Line line2 = new Line(new Point3d(50, 20, 0), new Point3d(200, 300, 0)); LineAngularDimension2 angular = new LineAngularDimension2(); line1.ColorIndex = 2; line2.ColorIndex = 2; //确定尺寸界限位置 angular.XLine1Start = line1.StartPoint; angular.XLine1End = line1.EndPoint; angular.XLine2Start = line2.StartPoint; angular.XLine2End = line2.EndPoint; angular.ColorIndex = 3; //尺寸线位置 angular.ArcPoint = new Point3d(200, 530, 0); db.AddElementToEntity(line1,line2,angular); } ``` ### 角度标注Point3AngularDimesion > 适合标注两半径之间的夹角或已知两线段的交点 ![image-20250402215317191](assets/image-20250402215317191.png) | 属性 | 解释 | | ------------- | ---------------------------------------------- | | `ArcPoint` | 确定尺寸线位置(尺寸线经过该点) | | `CenterPoint` | 中心点,由中线点和尺寸界限起点确定尺寸界限方向 | | `XLine1Point` | 尺寸界限1起点 | | `XLine2Point` | 尺寸界限2起点 | ```c# public void Demo() { Database db = HostApplicationServices.WorkingDatabase; Point3d center = Point3d.Origin;//圆心点 Point3d p1 = new Point3d(200, 0, 0);//尺寸界限1起点 Point3d p2 = new Point3d(200, 200, 0);//尺寸界限2起点 //尺寸线点 Point3d arcPoint = MathTool.GetMidPoint(p1, p2) - Vector3d.XAxis * 50; Point3AngularDimension angular = new Point3AngularDimension(); angular.XLine1Point = p1; angular.XLine2Point = p2; angular.ArcPoint = arcPoint; angular.ColorIndex = 3;//设置为绿色 db.AddElementToEntity(angular); } ``` ### 弧长标注 ArcDimension ![image-20250402215253925](assets/image-20250402215253925.png) | 属性 | 解释 | | ------------- | ---------------------------------------------- | | `ArcPoint` | 确定尺寸线位置(尺寸线经过该点) | | `CenterPoint` | 中心点,由中线点和尺寸界限起点确定尺寸界限方向 | | `XLine1Point` | 尺寸界限1起点 | | `XLine2Point` | 尺寸界限2起点 | ```c# public void Demo() { Database db = HostApplicationServices.WorkingDatabase; Arc arc = new Arc(Point3d.Origin, 200, 0, Math.PI * 0.3); //求终点 Point3d cen = MathTool.GetMidPoint(arc.StartPoint, arc.EndPoint); Vector3d temp = arc.StartPoint.GetVectorTo(arc.EndPoint); //求两点与X轴的弧度 double radian = temp.Y > 0 ? temp.GetAngleTo(Vector3d.XAxis) : -temp.GetAngleTo(Vector3d.XAxis); ArcDimension arcDim = new ArcDimension(arc.Center, arc.StartPoint, arc.EndPoint, cen+Vector3d.XAxis*50+Vector3d.YAxis*50 , "<>", db.Dimstyle); arcDim.ColorIndex = 3; arcDim.TextRotation = radian + Math.PI;//设置文本弧度 db.AddElementToEntity(arc,arcDim);//加入图形空间 } ``` ### 半径标注RadialDimension | 属性 | 释义 | | --------------------- | -------- | | `Center{set;get}` | 圆心 | | `ChordPoint`{set;get} | 弦上一点 | | `Double LeaderLength` | 引线长度 | ```c# public void Demo() { Database db = HostApplicationServices.WorkingDatabase; Arc arc = new Arc(Point3d.Origin, 200, 0, Math.PI * 0.45); RadialDimension radial = new RadialDimension(); //弦上一点 radial.ChordPoint = new Point3d(100, Math.Sqrt(200*200-100*100), 0); radial.Center = arc.Center; radial.LeaderLength = 5;//引线长度 radial.ColorIndex = 6; db.AddElementToEntity(radial, arc); } ``` ### 直径标注 DiametricDimension | 属性 | 释义 | | ------------------------------ | ------------ | | `ChordPoint{set;get}` | 弦上一点 | | `FarChordPoint{set;get}` | 弦上的远端点 | | `Double LeaderLength{set;get}` | 引线长度 | ```c# public void Demo() { Database db = HostApplicationServices.WorkingDatabase; Arc arc = new Arc(Point3d.Origin, 200, 0, Math.PI * 0.45); DiametricDimension d = new DiametricDimension(); //弦上一点 d.ChordPoint = new Point3d(100, Math.Sqrt(200*200-100*100), 0); d.FarChordPoint = new Point3d(-100, -Math.Sqrt(200 * 200 - 100 * 100), 0); d.LeaderLength = 5;//引线长度 d.ColorIndex = 6; db.AddElementToEntity(d, arc); } ``` ### 标注样式箭头相关 ```c# //设置系统变量,加载块定义 //小点 Application.SetSystemVariable("DIMBLK", "_DOTSMALl"); //无箭头 Application.SetSystemVariable("DIMBLK","_NONE"); //重置系统变量 Application.SetSystemVariable("DIMBLK", "."); ObjectId id1,id2 = ObjectId.Null; using(Transaction trans = s.db.TransactionManager.StartTransaction()) { //获取块表 BlockTable bt = trans.GetObject(s.db.BlockTableId, OpenMode.ForRead) as BlockTable; //获取箭头id id1= bt["_DOTSMAll"]; id2 = bt["_NONE"]; } RotatedDimension ro = new RotatedDimension(); ro.XLine1Point = Point3d.Origin; ro.XLine2Point = Point3d.Origin + Vector3d.XAxis * 200; ro.DimLinePoint = Point3d.Origin + Vector3d.XAxis * 50 + Vector3d.YAxis * 15; //开启单独标注 ro.Dimsah = true; //图层 ro.Layer = "AM_尺寸线"; //相关箭头id ro.Dimblk1 = id1; ro.Dimblk2 = id2; //隐藏第一个尺寸线和尺寸界限 ro.Dimsd2 = true; ro.Dimse2 = true; //文字位置 ro.TextPosition = Point3d.Origin + Vector3d.XAxis * 50 + Vector3d.YAxis * 15; s.db.AddLineToEntity(ro); ``` # 编辑图元 > 主要是针对图元的复制,移动,旋转,删除,缩放,以及阵列。 :bookmark:步骤 :one:获取矩阵 1. 移动矩阵:`Matrix3d mt = Matrix3d.Displacement(moveVec)`,参数:移动矢量 2. 旋转矩阵:`Matrix3d.Rotation(radian, Vector3d.ZAxis, center)`,参数旋转弧度,平面法向量,旋转中心 3. 镜像矩阵:`Matrix3d.Mirroring(new Line3d(p1, p2))`,参数为镜像线 4. 等比缩放矩阵:` Matrix3d.Scaling(scale, basePoint)`,参数为缩放比例与缩放基点 :two:变换图形 1. 在原对象基础上进行变换:`ent.TransformBy(mt)` 2. 保留原对象进行变换:`ent.GetTransformedCopy(mt)` :red_circle:当使用`GetObject`函数打开对象后可使用`ent.UpgradeOpen()_只读升级为可写`或`ent.DowngradeOpen()_可写降级为只读`来切换打开状态,应当注意的是提交事务处理后,对象的打开状态都会被清除。 矩阵描述了当前的变换信息,左乘矩阵*源点坐标 = 目标位置。 image-20250503181742448 ## 添加颜色 先将封装好的图元文档编译成 dll 文件,在新建项目中引用该 dll 文档,并加入到命名空间中,注意程序集所有类必须为 `Public`。 ```csharp public void Editor() { Database db = HostApplicationServices.WorkingDatabase; Circle c1 = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 100); Circle c2 = new Circle(new Point3d(0,0,0),Vector3d.ZAxis, 200); c1.ColorIndex = 1;//红色 c2.Color = Color.FromRgb(0, 225, 0); db.AddLineToEntity(c1, c2); } ``` 图元颜色属性继承于 `Entity`: :one: `ColorIndex `属性,1 代表红色 :two:`Color`属性,可通过`Color.Fromrgb `方法,设置 颜色。 设置颜色须在事务更新前设置,因此时对象的`IsNewObject`属性会由` True` 改成` False`。 若对象创建后更新,需重新发起事务处理,根据对象的`ObjectId`打开图元对象来进行设置。 :red_circle:`GetObject`方法用来打开对象,必须在事务处理中进行。 ```csharp public static class EditEntityTool { /// /// 更改颜色 /// /// 图形ID /// 颜色索引 public static void ChangeEntityColor(this ObjectId id,short colorIndex) { //声明一个图形数据库 Database db = HostApplicationServices.WorkingDatabase; using (Transaction trans = db.TransactionManager.StartTransaction()) { //获取图形对象,只写 Entity entity = id.GetObject(OpenMode.ForWrite) as Entity; entity.ColorIndex = colorIndex; //提交事务 trans.Commit(); } } /// /// 更改图元颜色 /// /// 实体对象 /// 颜色索引 public static void ChangeEntityColor(this Entity ent,short colorIndex) { //先判断实体是否创建 if(ent.IsNewObject) { //未创建,则 ent.ColorIndex = colorIndex; //此处未将内存中的实体对象添加到ModelSpace中,可自行处理 } else {//若被创建,则直接获取图形Id ent.ObjectId.ChangeEntityColor(colorIndex); } } } ``` ## 移动与复制 ```csharp public void MoveCircle() { Database db = HostApplicationServices.WorkingDatabase; Circle c1 = new Circle(new Point3d(100, 100, 0), Vector3d.ZAxis, 300); Circle c2 = new Circle(new Point3d(200, 200, 0), Vector3d.ZAxis, 50); c2.Center = new Point3d(c2.Center.X + 100, c2.Center.Y + 100, 0); db.AddLineToEntity(c2); } ``` 使用 `Matrix` 进行移动 ```csharp public void MoveCircle() { Database db = HostApplicationServices.WorkingDatabase; Circle c1 = new Circle(new Point3d(100, 100, 0), Vector3d.ZAxis,50); Circle c2 = new Circle(new Point3d(100, 100, 0), Vector3d.ZAxis, 50); //要移动的方位 Point3d p = new Point3d(200, 200, 0); //获取向量 Vector3d v = c1.Center.GetVectorTo(p); //变换矩阵 Matrix3d mt = Matrix3d.Displacement(v); c1.TransformBy(mt);//移动 //使用 ent.GetTransformedCopy(mt)即复制一个新图元 db.AddLineToEntity(c1,c2); } ``` 移动封装 ```csharp /// /// 移动图元 /// /// 图形数据库 /// 源点 /// 目标点 /// 是否拷贝 /// 目标图元id /// 返回ObjectId,仅移动返回null public static ObjectId MoveEntity(this Database db,Point3d sourcePoint,Point3d endPoint,bool isCopy,ObjectId id) { ObjectId newId;//未定义 //移动矢量 Vector3d moveVector = sourcePoint.GetVectorTo(endPoint); //变换矩阵 Matrix3d mt = Matrix3d.Displacement(moveVector); using (Transaction trans = db.TransactionManager.StartTransaction()) { //获取图元对象 Entity ent = id.GetObject(OpenMode.ForRead) as Entity; if (isCopy) { //需要复制 Entity copyEnt = ent.GetTransformedCopy(mt); newId = db.AddElementToEntity(copyEnt); } else { //不需要复制,仅移动 ent.UpgradeOpen();//只读提升为只写 ent.TransformBy(mt); newId = ObjectId.Null; } trans.Commit();//提交 return newId; } } /// /// 移动图元 /// /// 图形数据库 /// 源点 /// 目标点 /// 是否复制 /// 源对象 public static void MoveEntity(this Database db,Point3d sourcePoint,Point3d targetPoint,bool isCopy, Entity ent) { //判断实体是否加入到空间 if (ent.IsNewObject) { //未加入到空间 //移动矢量 Vector3d moveVector = sourcePoint.GetVectorTo(targetPoint); //移动矩阵 Matrix3d mt = Matrix3d.Displacement(moveVector); if (isCopy) { Entity copyEnt = ent.GetTransformedCopy(mt); db.AddElementToEntity(ent, copyEnt); return;//返回 } ent.TransformBy(mt);//直接移动 db.AddElementToEntity(ent); } else { db.MoveEntity(sourcePoint, targetPoint, isCopy, ent.ObjectId); } } ``` ## 旋转 :package:封装(此处直接在原对象基础上进行旋转,也可以保留原对象,旋转复制的新对象) ```csharp /// /// 旋转 /// /// 图元ID /// 旋转角度 /// 旋转中心 public static void RotateEntity(this ObjectId id,double degree,Point3d center) { Database db = HostApplicationServices.WorkingDatabase; using(Transaction trans = db.TransactionManager.StartTransaction()) { //只写打开图元 Entity ent = id.GetObject(OpenMode.ForWrite) as Entity; //变换矩阵 Matrix3d mt = Matrix3d.Rotation(degree.GetDegreeToRadian(), Vector3d.ZAxis, center); ent.TransformBy(mt); //提交 trans.Commit(); } } /// /// 旋转图元 /// /// 图元对象 /// 旋转角度 /// 旋转中心 public static void RotateEntity(this Entity ent,double degree,Point3d center) { if(ent.IsNewObject) { //未在模型空间,直接旋转 //变换矩阵 Matrix3d mt = Matrix3d.Rotation(degree.GetDegreeToRadian(), Vector3d.ZAxis, center); //旋转 ent.TransformBy(mt); } else { //须在开启事务处理进行旋转 ent.ObjectId.RotateEntity(degree, center); } } ``` ## 镜像 ```csharp /// /// 镜像 /// /// ID /// 镜像点1 /// 镜像点2 /// 是否删除源 /// 返回镜像ObjectId public static ObjectId MirrorEntity(this ObjectId id, Point3d p1, Point3d p2, bool clearSource) { //镜像矩阵 Matrix3d mt = Matrix3d.Mirroring(new Line3d(p1, p2)); //OpenCloseTransaction读取实体效率最高 OpenCloseTransaction newTrans = new OpenCloseTransaction(); //获取实体 Entity ent = newTrans.GetObject(id, OpenMode.ForRead) as Entity; if (clearSource) { ent.UpgradeOpen();//只写 ent.Erase();//删除 } //镜像 Entity mirrorEnt = ent.GetTransformedCopy(mt); //在模型空间中添加镜像实体 return id.Database.AddLineToEntity(mirrorEnt); } /// /// 镜像 /// /// 实体对象 /// 镜像点1 /// 镜像点2 /// 是否删除源 /// 返回镜像ObjectId public static ObjectId MirrorEntity(this Entity ent, Point3d p1, Point3d p2, bool clearSource) { Database db = HostApplicationServices.WorkingDatabase; if (ent.IsNewObject) { //未加入到模型空间 Matrix3d mt = Matrix3d.Mirroring(new Line3d(p1, p2)); //获取镜像对象 Entity mirrorEnt = ent.GetTransformedCopy(mt); return clearSource ? db.AddLineToEntity(mirrorEnt) : db.AddLineToEntity(ent, mirrorEnt)[1]; } else { return ent.ObjectId.MirrorEntity(p1, p2, clearSource); } } ``` ## 删除 ```csharp /// /// 删除 /// /// 图元Id public static void EraseEntity(this ObjectId entId) { //获取图元对象 Entity ent = entId.GetObject(OpenMode.ForWrite) as Entity; ent.Erase();//删除 } /// /// 删除图元 /// /// 实体对象 public static void EraseEntity(this Entity ent) { //判断实体是否添加进模型空间 if(!ent.IsNewObject) { ent.Erase();//删除 } } ``` ## 缩放 ```csharp /// /// 缩放 /// /// 图元Id /// 缩放基点 /// 缩放比例 public static void ScaleEntity(this ObjectId entId,Point3d basePoint,double scale) { //获取对象 Entity ent = entId.GetObject(OpenMode.ForWrite) as Entity; //缩放矩阵 Matrix3d mt = Matrix3d.Scaling(scale, basePoint); //缩放 ent.TransformBy(mt); } /// /// 缩放 /// /// 实体 /// 缩放基点 /// 缩放比例 /// 图形数据库 /// 返回图元ObjectId public static ObjectId ScaleEntity(this Entity ent,Point3d basePoint,double scale,Database db) { //判断实体是否加入模型空间 if(ent.IsNewObject) { //缩放矩阵 Matrix3d mt = Matrix3d.Scaling(scale, basePoint); //缩放 ent.TransformBy(mt); db.AddLineToEntity(ent);//加入到模型空间 } else { ent.ObjectId.ScaleEntity(basePoint, scale); } return ent.ObjectId; } ``` ## 矩形阵列 ```csharp /// /// 矩形阵列 /// /// 源ObjectId /// 行 /// 列 /// 行间距 /// 列间距 /// 图形数据库 /// 返回ObjectId集合 public static ObjectIdCollection ArrayRecTangle(this ObjectId id,int row,int column,double disRow,double disCol,Database db) { ObjectIdCollection ids = new ObjectIdCollection(); //获取对象 Entity ent = id.GetObject(OpenMode.ForWrite) as Entity; for(int i = 0; i < row; i++) { //列 for(int j = 0; j < column; j++) { //移动矩阵 Matrix3d mt = Matrix3d.Displacement(new Vector3d(j * disCol, i * disRow, 0)); //复制 Entity copyEnt = ent.GetTransformedCopy(mt); db.AddLineToEntity(copyEnt);//添加实体 ids.Add(copyEnt.ObjectId); } } //删除源 ent.Erase(); return ids; } /// /// 矩形阵列 /// /// 源ObjectId /// 行 /// 列 /// 行间距 /// 列间距 /// 图形数据库 /// 返回ObjectId集合 public static ObjectIdCollection ArrayRecTangle(this Entity ent,int row,int column,double disRow,double disCol,Database db) { ObjectIdCollection ids = new ObjectIdCollection(); if (ent.IsNewObject) { //未加入到模型空间 for (int i = 0; i < row; i++) { //列 for (int j = 0; j < column; j++) { //矩形阵列 Matrix3d mt = Matrix3d.Displacement(new Vector3d(j * disCol, i * disRow, 0)); //复制 Entity copyEnt = ent.GetTransformedCopy(mt); db.AddLineToEntity(copyEnt);//添加实体到模型空间 ids.Add(copyEnt.ObjectId);//加入id } } } else { ids= ent.ObjectId.ArrayRecTangle(row,column, disRow, disCol, db); } return ids; } ``` ## 环形阵列 > 环形阵列涉及数学中的植树问题。 > > 满 360°,间距数 = 阵列数量;不足 360°:间距数 = 阵列数量-1 ```ceylon /// /// 环形阵列 /// /// 源ObjectId /// 旋转中心 /// 阵列角度 /// 阵列数量 /// 图形数据库 /// 返回ObjectID集合 public static ObjectIdCollection ArrayLoop(this ObjectId id, Point3d center, double degree, int num, Database db) { //声明集合对象 ObjectIdCollection collection = new ObjectIdCollection(); //获取图形对象 Entity ent = id.GetObject(OpenMode.ForWrite) as Entity; //矩阵弧度 double radian; //去除非法角度以及计算弧度(植树问题) if ((degree >= 360)) { degree = 360; radian = MathTool.GetDegreeToRadian(degree / num); } else if (degree <= -360) { degree = -360; radian = MathTool.GetDegreeToRadian(degree / num); } else { radian = MathTool.GetDegreeToRadian(degree / (num - 1)); } for (int i = 0; i < num; i++) { //计算旋转矩阵 Matrix3d mt = Matrix3d.Rotation(radian * i, Vector3d.ZAxis, center); //复制 Entity copyEnt = ent.GetTransformedCopy(mt); //添加到模型空间,并将ID存放到集合中 collection.Add(db.AddLineToEntity(copyEnt)); } ent.Erase();//删除源 return collection; } /// /// 环形阵列 /// /// 图形数据库 /// 旋转中心 /// 阵列角度 /// 数量 /// 图形数据库 /// 返回图元ObjectId集合 public static ObjectIdCollection ArrayLoop(this Entity ent,Point3d center,double degree, int num,Database db) { //声明Collection对象 ObjectIdCollection collection = new ObjectIdCollection(); //矩阵弧度 double radian; //去除非法角度以及计算弧度(植树问题) if ((degree >= 360)) { degree = 360; radian = MathTool.GetDegreeToRadian(degree / num); } else if (degree <= -360) { degree = -360; radian = MathTool.GetDegreeToRadian(degree / num); } else { radian = MathTool.GetDegreeToRadian(degree / (num - 1)); } //判断实体是否加入模型空间 if (ent.IsNewObject) { for(int i = 0;i 获取用户输入信息的函数为`GetXXX`,提示选项类为`PromptXXXOptions`,与获取用户输入信息一致;提示结果类为`PromptXXXResult`。 ![image-20250320203211450](assets/image-20250320203211450.png) ### `PromptOptions` 获取用户输入信息的函数为`GetXXX`均可传入一个`PromptXXXOptions`实例作为参数。 重载:one:`​PromptXXXOptions(string message)`,`message`提示信息 ```c# var options = new PromptDoubleOptions("请输入数值:[E退出/U闭合]"); options.Keywords.Add("E"); options.Keywords.Add("U"); //默认值用Option.DefaultValue指示,便可在提示框内看见默认值 options.DefaultValue = 5; ``` 使用该构造函数会出现多余关键字列表,使用`options.AppendKeywordsToMessage = false`关闭关键字列表 ![image-20250511163706054](assets/image-20250511163706054.png) 重载:two: `PrompXXXOptions(string messageAndKeywords,string globalKeywords)`,参数1提示信息和关键字列表,参数2为关键字列表字符串(AppendKeywordsToMessage属性关闭的即是这个) ```c# var options = new PromptDoubleOptions("请输入数值:[E退出/U调整]", "E U") 关键字列表用/间隔,全局关键字用空格间隔, //无需关闭关键字列表 ``` :red_circle:注意关键字须在中文前面 ![image-20250511163325698](assets/image-20250511163325698.png) `Prompt`类继承关系如下 ```mermaid flowchart TD A(PromptOptions) --> B(PromptEditorOptions) B--> Angle B --> C(Corner) C --> Point B --> Entity B --> Keyword B --> D(Numerical) D -->Distance D --> Double D -->Integer B --> String ``` :bookmark:`PromptOptions`属性与方法 | 属性 | 解释 | | --------------------------------------- | -------------------------------- | | `bool AppendKeywordsToMessage{set;get}` | 是否将关键字列表添加到提示信息中 | | `KeywordCollection Keywords{get;}` | 关键字集合 | 方法 ```c# //类似于构造函数 public void SetMessageAndKeywords(string messageAndKeywords, string globalKeywords); ``` ### `PromptResult` 提示结果类均继承于`PromptResult`,如下: ```mermaid classDiagram class PromptResult { <> +Status_状态 +StringResult_关键字 +ToString() } class PromptIntegerResult { <> +Value: int +ToString() } class PromptDoubleResult { <> +Value: double +ToString() } class PromptPointResult { +Value: Point3d +ToString() } class PromptEntityResult { <> +ObjectId +PickedPoint: Point3d +ToString() } class PromptNestedEntityResult { <> +Transform +GetContainers() +ToString() } PromptResult <|-- PromptIntegerResult PromptResult <|-- PromptDoubleResult PromptResult <|-- PromptPointResult PromptResult <|-- PromptEntityResult PromptEntityResult <|-- PromptNestedEntityResult ``` :one: `Status`属性为`PromptStatus`枚举值,详情如下 | 输入 | 状态(status) | | -------------- | :----------- | | 成功输入 | `Ok` | | 输入`ESC` | `Cancel` | | 回车,空格 | `None` | | 用户输入关键字 | `Keyword` | | 未知错误 | `Error` | | 其他状态 | `Other` | ### GetInteger/Double/Distance 属性如下: ```mermaid classDiagram class PromptOptions { <> } class PromptNumericalOptions{ bool AllowZero_是否可以输入零 bool AllowNegative_是否可以输入负数 bool AllowNone_是否可以空输入(设置默认值后此项无效,互相冲突,只能存其一) } class PromptIntegerOptions { int LowerLimit_最小值:read/write int UpperLimit_最大值:read/write int DefaultValue_默认值:read/write } class PromptDoubleOptions { double DefaultValue_默认值:read/write } class PromptDistanceOptions { double DefaultValue_默认值:read/write bool UseBasePoint_是否使用基点作为参照点:red/write Point3d BasePoint_基点:read/write } PromptOptions <|-- PromptNumericalOptions PromptNumericalOptions <|-- PromptIntegerOptions PromptNumericalOptions <|-- PromptDoubleOptions PromptNumericalOptions <|-- PromptDistanceOptions ``` :bookmark:`GetInteger`:用于获取用户输入的整数,返回值是`PromptIntegerResult`类型。 ```c# Editor ed = doc.Editor; PromptIntegerOptions intOption = new PromptIntegerOptions("请输入一个整数"); //范围限制 intOption.UpperLimit = 360; intOption.LowerLimit = 0; intOption.DefaultValue = 90;//默认值 intOption.AllowNegative = false;//不允许输入负数 PromptIntegerResult intRes = ed.GetInteger(intOption); if(intRes.Status == PromptStatus.OK) { Application.ShowAlertDialog($"value:{intRes.Value}"); } ``` :bookmark:`GetDouble`,获取用户输入的实数,返回`PromptDoubleResult`类型。 ```c# var editor = doc.Editor; var options = new PromptDoubleOptions("请输入数值:默认<5>[PI/2PI/3PI]", "PI 2PI 3PI"); //默认值 options.DefaultValue = 5; PromptDoubleResult result = editor.GetDouble(options); if (result.Status == PromptStatus.OK) { editor.WriteMessage($"\n输入值: {result.Value}"); } else if (result.Status == PromptStatus.Keyword) { Application.ShowAlertDialog($"value:{result.StringResult}"); } ``` :bookmark:GetDistance,获得用户输入的一段距离,返回`PrompDoubleResult`类型 > :one: 可直接输入距离,:two: 也可指定两点获得距离,:three: 或者先设置`basePoint`再指定一个点来获得距离。 > > :red_circle:使用`BasePoint`须设置`UseBasePoint=true`才生效。 ```c# public void CircleDemo() { Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; //圆心 Point3d center; PromptPointResult ppr= ed.GetPoint("请选择圆心"); if(ppr.Status == PromptStatus.OK) { center = ppr.Value;//将用户选择的点赋值给圆心 double radius = 0;//半径 PromptDistanceOptions pdo = new PromptDistanceOptions("请属于圆半径"); pdo.BasePoint = center;//指定圆心为参照点 pdo.AllowNegative = false;//不允许输入负数 pdo.UseBasePoint = true;//允许将基点作为参照点。 PromptDoubleResult pdr = ed.GetDistance(pdo); if (pdr.Status == PromptStatus.OK) { radius = pdr.Value; } db.DrawCircle(center, radius);//将圆添加进模型空间 } } ``` ### `GetString` `GetString`获取用户输入的字符串,返回值是`PromptResult`类型,没有`Value`属性。 `PromptStringOptions`属性如下; ```mermaid classDiagram class PromptStringOptions{ bool AllowSpaces_是否允许空格,设置默认值后此项无效,互相冲突,只能存其一 string DefaultValue_默认值 bool UseDefaultValue_是否使用默认值,默认为true } ``` ```c# PromptStringOptions op = new PromptStringOptions("请输入文本"); op.DefaultValue = "默认值"; PromptResult result = editor.GetString(op); if(result.Status == PromptStatus.OK) { //stringResult此时代表的是文本值或关键字 Application.ShowAlertDialog(result.StringResult); } ``` ### GetCorner/Point > `GetCorner`获取矩形角点,`GetPoint()`用于获取点坐标,返回值类型为`PromptPointResult`类型 > > 1. `Value`属性为输入点的坐标; > 2. `Status`为当前的状态,属于`PromptStatus`枚举类型; > 3. `stringResult`为当前输入的关键字。 > [^GetPoint正确输入返回值]: ![getPoint](assets/getPoint.png) `PromptPointOptions`类继承关系: ```mermaid classDiagram class PromptPointOptions { +bool UseBasePoint;是否使用基点作为参考点 } class PromptCornerOptions { +bool AllowNone_允许回车或空格 +Point3d BasePoint_基点 } PromptOptions <|-- PromptCornerOptions PromptCornerOptions <|-- PromptPointOptions ``` `GetCorner`简单使用 ```c# PromptCornerOptions opt = new PromptCornerOptions("请输入一个角点",Point3d.Origin); PromptPointResult result = editor.GetCorner(opt); if(result.Status == PromptStatus.OK) { //返回值为Point3d类型,另一个角点 Application.ShowAlertDialog(result.Value.ToString()); } ``` 使用`GetPoint`方法画线段 ```ceylon PromptPointOptions opt = new PromptPointOptions("选择一个点"); PromptPointResult result = editor.GetPoint(opt); if(result.Status == PromptStatus.OK) { opt.Message = "请选择下一个点"; //设置基点 opt.BasePoint = result.Value; //基点在UseBasePoint为true时有用 opt.UseBasePoint = true; result = editor.GetPoint(opt); if (result.Status == PromptStatus.OK) { Line l = new Line(opt.BasePoint, result.Value); l.ColorIndex = 1; db.AddLineToEntity(l); } } ``` :traffic_light:仿多段线命令 :one:第一个点使用一种方法 :two:后面的点使用同一个方法,状态为OK或关键字时进入循环,后结尾继续让用户选择点坐标。 :three:此处宽度为整体宽度,可以在设置宽度时获取起始宽度与终止宽度。 ```c# public void PolyLineDemo() { Database db = HostApplicationServices.WorkingDatabase; //命令行对象 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; ObjectId id = ObjectId.Null; //初始化宽度与颜色 double width = 0; double colorIndex = 0; Polyline polyline = new Polyline(); //获取第一个点 Point3d startPoint = GetPolyLineFirPoint(out bool b); if (b) return;//用户若取消则退出程序 //循环调用下一个点 GetNextPoint(startPoint, id, polyline, width, colorIndex); } public Point3d GetPolyLineFirPoint(out bool b) { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptPointOptions ppo = new PromptPointOptions("\n请选择第一个点"); ppo.AllowNone = true;//允许空格 PromptPointResult ppr = ed.GetPoint(ppo);//获取提示结果类 b = ppr.Status == PromptStatus.Cancel ? true : false;//给b赋值 if (ppr.Status == PromptStatus.OK) { return ppr.Value;//返回用户选择的点或默认点 } else { b = true; return new Point3d(100, 200, 0);//键入空格返回默认点 } } public void GetNextPoint(Point3d basePoint, ObjectId id, Polyline pl, double width, double colorIndex) { Database db = HostApplicationServices.WorkingDatabase; //命令行对象 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptPointOptions ppo = new PromptPointOptions("\n请选择下一个点,[W线宽][O完成][I设置颜色0-256]"); ppo.BasePoint = basePoint;//重置参照点 ppo.UseBasePoint = true;//允许使用参照点 //添加关键字 ppo.Keywords.Add("W"); ppo.Keywords.Add("O"); ppo.Keywords.Add("I"); ppo.AppendKeywordsToMessage = false;//不显示关键字列表信息 PromptPointResult ppr = ed.GetPoint(ppo); while (ppr.Status == PromptStatus.OK || ppr.Status == PromptStatus.Keyword) { //OK 状态 if (ppr.Status == PromptStatus.OK) { if (id == ObjectId.Null) { //未添加实体 //设置二维点 Point2d startPoint = new Point2d(basePoint.X, basePoint.Y); Point2d endPoint = new Point2d(ppr.Value.X, ppr.Value.Y); pl.AddVertexAt(0, startPoint, 0, 0, 0); pl.AddVertexAt(1, endPoint, 0, 0, 0); pl.ColorIndex = (int)colorIndex;//颜色设置 pl.ConstantWidth = width;//整体宽度设置 id = db.AddLineToEntity(pl);//添加进模型空间 ppo.BasePoint = ppr.Value;//重置参照点 } else { //已添加进实体 using (Transaction trans = db.TransactionManager.StartTransaction()) { //当前选择点二维坐标 Point2d nextPoint = new Point2d(ppr.Value.X, ppr.Value.Y); Polyline polyline = id.GetObject(OpenMode.ForWrite) as Polyline; polyline.AddVertexAt(polyline.NumberOfVertices, nextPoint, 0, 0, 0); polyline.ColorIndex = (int)colorIndex; polyline.ConstantWidth = width; ppo.BasePoint = ppr.Value; trans.Commit(); } } } else if(ppr.Status == PromptStatus.Keyword) { //关键字 switch(ppr.StringResult) { case "W": width = GetWidth(); break; //其他一致。不在此处重复 } } if (ppr.Status == PromptStatus.Cancel) return; ppr = ed.GetPoint(ppo);//继续调用 } } public double GetWidth() { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptDoubleOptions pdo = new PromptDoubleOptions("请输入一个整数"); PromptDoubleResult pdr = ed.GetDouble(pdo); if (pdr.Status == PromptStatus.OK) { return pdr.Value; } else return 0; } ``` :traffic_light:仿系统直线命令 1. 绘制第一个点回车会指定默认点 2. 在用户选择第二个点时,将当前线段添加到图形空间,`U`退回第一个选点 3. 绘制第三个点时,按U退回第二个点 4. 绘制第四个点时需要添加闭合关键字`C` :key:用`i`来记录点的数量,针对不同的点提供不同的信息,比如选择第四个点时添加闭合关键字`C`;同时声明一个`List ids`用来记录空间中线条的数量,在第二条线往后退回时,清空关键字集合,增加U命令。 ```ceylon internal class ImitateLine { public ImitateLine() { _ids = new List(); } Point3d _startPoint; Point3d _endPoint; int _num;//计数器,储存当前模型空间中该线点的数量 List _ids; PromptPointOptions _options; public void DrawLine() { //当前图形数据库 Database db = HostApplicationServices.WorkingDatabase; //命令行对象 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; while (true) { switch (_num) { //当前空间不存在点,用户选择第一个点 case 0: if(!GetFirPoint(ed)) { return;//如果用户取消,则退出该方法 } else { _options.BasePoint = _startPoint;//重置参照点 _options.UseBasePoint = true;//允许使用参照点 break; } //让用户选择第二个点,第三个点 case 1: case 2: if (!GetSecThrPoint(ed, db)) return;//false则返回 _options.BasePoint = _startPoint;//重置参照点 break; default: if (!GetNextPoint(ed, db)) return; _options.BasePoint = _startPoint;//重置参照点 break; } } } /// /// 获取用户起点 /// /// 命令行对象 /// 提示选项 /// 返回当前的状态,True则用户继续向下选择 public bool GetFirPoint(Editor ed) { _options = new PromptPointOptions("\n请选择第一个点"); _options.AllowNone = true;//允许空格 bool b = true; PromptPointResult ppr = ed.GetPoint(_options); if (ppr.Status == PromptStatus.OK || ppr.Status == PromptStatus.None) { //状态为OK或状态为空格 //给起点赋值 _startPoint = ppr.Value; _num++;//计数器累加器 } else { //用户取消则,将falae返回 b = false; } return b; } /// /// 获取第二个,第三个点 /// /// 命令行对象 /// 图形数据库 /// 返回结果状态 public bool GetSecThrPoint(Editor ed,Database db) { bool b = true; _options.Message = "\n选择下一个点[U退回]"; _options.AllowNone = false;//禁用空格 _options.Keywords.Add("U"); _options.AppendKeywordsToMessage = false;//不将关键字添加进关键字列表 PromptPointResult ppr = ed.GetPoint(_options);//获取用户选择的点 if(ppr.Status == PromptStatus.OK) { //声明直线 Line line = new Line(_startPoint,ppr.Value); _ids.Add(db.AddLineToEntity(line));//添加直线到空间 _startPoint = ppr.Value; _num++;//自增 } else if(ppr.Status == PromptStatus.Keyword) { //退回 BackPoint(db); } else b = false; return b; } /// /// 获取剩余的点 /// /// 命令行对象 /// 图形数据库 /// 返回获取点的状态 public bool GetNextPoint(Editor ed,Database db) { _options.Message = "\n选择下一个点[U退回][C闭合]"; _options.Keywords.Add("C"); PromptPointResult ppr = ed.GetPoint(_options); bool b = true; if(ppr.Status == PromptStatus.OK) { //声明直线 Line line = new Line(_startPoint, ppr.Value); _ids.Add(db.AddLineToEntity(line));//添加直线到模型空间 _num++;//加1 _startPoint= ppr.Value;//重置起点 } else if(ppr.Status == PromptStatus.Keyword) { if(ppr.StringResult == "U") { BackPoint(db);//退回 } else if(ppr.StringResult == "C") { //闭合 using (Transaction trans = db.TransactionManager.StartTransaction()) { //拿到线段的起点 Line line = _ids[0].GetObject(OpenMode.ForRead) as Line; Line addLine = new Line(line.StartPoint, _startPoint); db.AddLineToEntity(addLine);//添加直线到模型空间 b = false; trans.Commit();//提交 } } } else b =false;//ESC或其他 return b; } /// /// 退回点 /// /// 图形数据库 public void BackPoint(Database db) { //判断集合数量是否==2,若等于则需要清空关键字集合 if(_ids.Count == 2) { _options.Keywords.Clear();//清空关键字集合 _options.Keywords.Add("U"); } //判断ObjectId集合里面的数据 if (_ids.Count == 0) { //此时用户在选择第二个时退回 _num--;//计数器退一 } else { //此时已有直线在ModelSpace中 //开启事务处理 using(Transaction trans = db.TransactionManager.StartTransaction()) { //获取对象 Line line = _ids[_ids.Count - 1].GetObject(OpenMode.ForWrite) as Line; _startPoint = line.StartPoint;//给起点赋值 _num--;//计数器减1 line.Erase();//删除直线 _ids.RemoveAt(_ids.Count - 1);//移除ObjectId trans.Commit();//提交事务 } } } } ``` ## EntityJig拖拽类 > 实现拖拽类,本质是实现`Entity`必要的属性,如`Line`实例,要给予一个起点与终点,拖拽时会根据直线的属性值会自动更新直线。 实现拖拽效果基本步骤: :one:实例化一个`EntityJig`的派生类 :two:调用`Editor`的`Drag`方法(返回`PromptResult`) 1. 程序会先执行`Sampler`方法,更新属性 2. 之后执行`Update`方法,返回`true`,动态更新图形。 :three:根据`PromptRsult`返回的状态判断下一步操作。 :fu: *注意事项*: ![image-20250322165844497](assets/image-20250322165844497.png) :one: `Sampler`函数中需要声明结果类(`PromptXXXResult`)与提示类`JigPromptXXXOptions`,所用函数为`prompts.AcquireXXX(options)`。 * :red_circle:与获取用户输入信息的`GetXXX`方法返回的状态不同(正确获取一个点状态为`OK`),拖拽时`PromptXXXResult`状态为`0K`,其他一致 * :red_circle:返回值`SamplerStatus`状态有三个`SamplerStatus.OK`(正在拖拽),`SamplerStatus.NoChange`(不发生改变),以及`SamplerStatus.Cancel`(取消拖拽),前两个状态无区别,都会进入`Update`函数中;当返回`Cancel`状态时会立即跳出`Drag`方法。 :two:`Update`方法用于更新实体,建议在该函数中更新实体关键属性(不推荐在`SamPler`函数中更新) 使用拖拽类绘制圆案例 ```c# internal class CircleJig : EntityJig { private Point3d _center;//圆心 private double _radius;//半径 //参数Circle为推拽对象Entity public CircleJig(Point3d center) : base(new Circle()) { _center = center; } //拖拽时调用该方法 protected override SamplerStatus Sampler(JigPrompts prompts) { //拖拽提示类 JigPromptPointOptions options = new JigPromptPointOptions("\n请选择圆上一点"); options.UseBasePoint = true;//允许使用参照点 options.BasePoint = _center;//使用圆心作为参照点 options.Cursor = CursorType.RubberBand;//光标设置为橡皮筋 options.UserInputControls = UserInputControls.Accept3dCoordinates;//接受三维坐标 //添加关键字 options.Keywords.Add("100"); options.Keywords.Add("200"); options.Keywords.Add("300"); //拖拽结果类 PromptPointResult ppr = prompts.AcquirePoint(options); if(ppr.Status == PromptStatus.Cancel) { //ESC return SamplerStatus.Cancel; } else if(ppr.Status == PromptStatus.Keyword) { switch(ppr.StringResult) { case "100": _radius = 100; break; case "200": _radius = 200; break; case "300": _radius = 300; break; } //关键字 return SamplerStatus.NoChange; } if(ppr.Status == PromptStatus.OK) { //重置半径 _radius = ppr.Value.DistanceTo(_center); return SamplerStatus.OK; } else return SamplerStatus.NoChange;//暂停 } //更新拖拽对象 protected override bool Update() { if (_radius > 0) { //和原点不重复。重复会报错 ((Circle)Entity).Center = _center; ((Circle)Entity).Radius = _radius; } return true; //返回true时,图形对象才会更新 } //返回实体对象 public Entity GetEntity() { return Entity; } } ``` 在主函数中调用: ```c# public void CircleDemo() { Database db = HostApplicationServices.WorkingDatabase; //命令行对象 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; //声明CircleJig拖拽对象 PromptPointResult ppr = ed.GetPoint("\n请选择圆心"); CircleJig circleJig = new CircleJig(ppr.Value); PromptResult pr = ed.Drag(circleJig);//拖拽 if (pr.Status == PromptStatus.OK || pr.Status == PromptStatus.Keyword) { db.AddLineToEntity(circleJig.GetEntity()); } } ``` 使用拖拽类模仿直线命令: :red_circle:主程序 :one:在函数中循环调用获取点的方法 :two:第一个点获取是通过`GetPoint`方法获得,并在方法体内部重置拖拽实例的起点属性 :three:其余的点是通过`ed.Drag(jig)`方法获得。 ```ceylon public void PolyLineDemo() { //当前图形数据库 Database db = Application.DocumentManager.MdiActiveDocument.Database; //命令行对象 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; LineJig lineJig = new LineJig();//实例化直线拖拽类 while(true) { switch(lineJig.Num) { case 0://获得第一个点 //ESC或其他错误直接返回 if (GetFirPoint(ed, lineJig)) return; lineJig.Num++;//成功选择点自增1 break; default://获取更多的点 if(GetNextPoint(ed, lineJig)) return; break; } } } public bool GetFirPoint(Editor ed,LineJig lineJig) { bool b = false; PromptPointResult ppr = ed.GetPoint("选择第一个点"); if(ppr.Status == PromptStatus.OK) { //给源点赋值 lineJig.SourcePoint = ppr.Value; //给起点赋值 lineJig.StartPoint = ppr.Value; } else b = true; return b; } public bool GetNextPoint(Editor ed,LineJig lineJig) { Database db = HostApplicationServices.WorkingDatabase; bool b = false; PromptResult pr = ed.Drag(lineJig);//拖拽 if (pr.Status == PromptStatus.Cancel) { b = true; } else if (pr.Status == PromptStatus.Keyword) { if (pr.StringResult == "U") { //退回 //lineJig.Num --在方法中执行 BackPoint(lineJig, db); } else if(pr.StringResult == "C") { b = true; Line tempLine = new Line(lineJig.SourcePoint,lineJig.EndPoint); lineJig.LineIds.Add(db.AddLineToEntity(tempLine));//添加进模型空间 } } else { Line tempLine = new Line(lineJig.StartPoint,lineJig.EndPoint); lineJig.StartPoint = lineJig.EndPoint;//重置起点 lineJig.LineIds.Add(db.AddLineToEntity(tempLine));//添加进模型空间 lineJig.Num++;//自增1 } return b; } public void BackPoint(LineJig lineJig,Database db) { if (lineJig.LineIds.Count == 0) { //未添加进模型空间 lineJig.Num--; } else { using(Transaction trans = db.TransactionManager.StartTransaction()) { //获取直线对象 Line line = lineJig.LineIds[lineJig.LineIds.Count-1].GetObject(OpenMode.ForWrite) as Line; lineJig.StartPoint = line.StartPoint;//重置起点 line.Erase();//删除直线 lineJig.LineIds.RemoveAt(lineJig.LineIds.Count - 1);//移除最后一位 lineJig.Num --; trans.Commit();//提交事务 } } } ``` :red_circle:拖拽类 通过判断类中的计数器属性`Num`的值,给`JigPromptPointOptions`提示选项实例赋值,添加关键字。在拖拽中去修改`EndPoint`属性,以及在`Update`函数中更新当前直线的起点与端点坐标。 ```ceylon public class LineJig : EntityJig { public LineJig() : base(new Line()) { //实例化直线,默认起点终点均为0,0,0 LineIds = new List();//初始化集合 } //源点 public Point3d SourcePoint { get; set; } //直线起点 public Point3d StartPoint { get; set; } //直线终点 public Point3d EndPoint { get; set; } //计数器 public short Num { set; get; } public List LineIds { set; get; } protected override SamplerStatus Sampler(JigPrompts prompts) { string message = null; string[] keywords = null; switch (Num) { case 1: case 2: message = "\n选择下一个点[U退回]"; keywords = new string[] { "U"}; break; default: message = "\n选择下一个点[U退回][C闭合]"; keywords = new string[] { "U","C"}; break; } return GetPoint(prompts,message,keywords); } protected override bool Update() { //更新直线的起点与终点 ((Line)Entity).StartPoint = StartPoint; ((Line)Entity).EndPoint = EndPoint; return true; } /// /// 获取直线实体对象 /// /// 返回直线实体对象 public Line GetLine() { return (Line)Entity; } /// /// 拖拽点 /// /// /// 提示信息 /// 关键字数组 /// 返回拖拽状态 public SamplerStatus GetPoint(JigPrompts prompts, string message,params string[] keywords) { //jig提示选项类 JigPromptPointOptions jigOptions = new JigPromptPointOptions(message); foreach (string keyword in keywords) { //添加关键字 jigOptions.Keywords.Add(keyword); } jigOptions.UseBasePoint = false; //接受三维坐标输入 jigOptions.UserInputControls = UserInputControls.Accept3dCoordinates; jigOptions.Cursor = CursorType.NoSpecialCursor;//橡皮筋样式 jigOptions.AppendKeywordsToMessage = false;//不显示关键字列表 PromptPointResult ppr = prompts.AcquirePoint(jigOptions);//获取结果类 //用户取消则取消拖拽,退出drag方法 if(ppr.Status == PromptStatus.Cancel) return SamplerStatus.Cancel; //给端点赋值 EndPoint = ppr.Value; return SamplerStatus.OK;//拖拽中,程序正常执行 } } ``` ## 选择集 选择集通过调用`Editor`类的`GetSelection`和`SelectXXX`函数来实现 | 方法 | 释义 | | ------------- | ------------------------ | | GetSelection | 用户在图形窗口选择实体 | | SeclectAll | 选择所有实体 | | SelectImplied | 选择当前窗口已选择的实体 | | | | 使用`SeclectAll`函数时若图形窗口没有图形,则状态为`Error`,`Value`值为`null`,`GetSelection`同理。 ![image-20250316221311021](assets/image-20250316221311021.png) 结果类中的`Value`属性是`SelectionSet`类型(程序运行时实现该抽象类),代表用户选择实体的集合。其`Count`属性代表选择的数量,通过`public abstract ObjectId[] GetObjectIds()`方法可获得选择对象的`id`数组。 ```c# public void SelectTest() { Database db = HostApplicationServices.WorkingDatabase;//图形数据库 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;//命令行对象 PromptSelectionOptions pso =new PromptSelectionOptions();//提示类 PromptSelectionResult psr = ed.GetSelection(pso); if(psr.Status == PromptStatus.OK)//正常状态 { ObjectId[] ids = psr.Value.GetObjectIds(); using (Transaction trans = db.TransactionManager.StartTransaction()) { for (int i = 0; i < ids.Length; i++) { Entity ent = ids[i].GetObject(OpenMode.ForWrite) as Entity;//获取实体对象 ent.ColorIndex = 2;//更改颜色 } trans.Commit();//提交 } } } ``` 使用`SelectImplied`时,需要在命令中添加`CommandFlags.UsePickSet`参数,先选择后操作。 ```c# [CommandMethod("1",CommandFlags.UsePickSet)] ``` 如果未选择,返回`error`状态。 ### 过滤器 过滤器用来限定用户选择的对象,用`SecletionFilter(TypedValue[] values)`来实现;过滤器参数列表由`TypedValue`结构组成。 ```c# TypedValue(int typeCode, object value) //第一个参数指过滤器类型,使用哪种DXF组码 //第二个参数指过滤器的值 ``` :bookmark:常用`DXF`组码: ![image-20250408210442036](assets/image-20250408210442036.png) 完整`DXF组码`参照[官方文档CAD2018版](https://help.autodesk.com/view/ACD/2018/CHS/?guid=GUID-3F0380A5-1C15-464D-BC66-2C5F094BCFB9). ```c# public void SelectTest() { Database db = HostApplicationServices.WorkingDatabase;//图形数据库 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;//命令行对象 PromptSelectionOptions pso =new PromptSelectionOptions();//提示类 //TypeValue数组,单选择圆形。 TypedValue[] typeValues = new TypedValue[] { new TypedValue((int)DxfCode.Start, "Circle") }; //声明一个过滤器对象 SelectionFilter filter = new SelectionFilter(typeValues); PromptSelectionResult psr = ed.GetSelection(pso,filter); if(psr.Status == PromptStatus.OK)//正常状态 { ObjectId[] ids = psr.Value.GetObjectIds(); using (Transaction trans = db.TransactionManager.StartTransaction()) { for (int i = 0; i < ids.Length; i++) { Entity ent = ids[i].GetObject(OpenMode.ForWrite) as Entity;//获取实体对象 ent.ColorIndex = 2;//更改颜色 } trans.Commit();//提交 } } ``` # 符号表 图形数据库是由符号表和命名对象字典组成,每个符号表中包含`n`条表记录. ```mermaid flowchart TD A(SymbolTable) --> LayerTable -->LayerTableRecord_1,_2... A --> BlockTable --> BlockTableRecord_1,_2... A --> TextStyleTable --> TextStyleTableRecord_1,_2... A -->LineStyleTable -->LineStyleTableRecord_1,_2... A -->DimStyleTable -->DimStyleTableRecord_1,_2... ``` CAD中各式表都继承于`SymbolTable`,各式表记录继承于`SymbolTableRecord`。 :one:符号表`SymbolTable`常用方法: | 方法 | 释义 | | ----------------------------------------------- | ------------------------------------------ | | public ObjectId Add( SymbolTableRecord value ); | 向表中增加一条记录 | | public bool Has(ObjectId id ); | 判断表中是否包含此表记录的ObjectId | | public bool Has(string key ); | 判断表中是否包含此表记录的名称(名称唯一) | :two:符号表记录`SymbolTableRecord`中常用`Name`属性:表记录名称 :three:`SymbolUtilityServices`类 :bookmark:常用方法: | 方法 | 释义 | | ------------------------------------------------------------ | ---------------------- | | `public static void ValidateSymbolName(string name,bool allowVerticalBar );` | 验证对象名称 | | `public static ObjectId GetLinetypeByBlockId()` | 获得`ByBlock`线型Id | | `public static ObjectId GetLinetypeByLayerId()` | 获得`ByLayer`线型Id | | `public static ObjectId GetLinetypeContinuousId` | 获得`Continuous`线型Id | ## 文字样式表TextStyleTable > 在当前文字样式下,CAD中使用命令增加文字,样式的`高度,注释性,宽度银子,倾斜角度,颠倒等属性`会作用到文字中,但是在代码中文字只会继承样式的字体,还要需将样式表中的属性分别赋值给文字属性。 :bookmark:常用属性: | 属性 | 解释 | | ---------------------------------------- | -------------- | | `public string FileName{set;get}` | 字体文件名 | | `public string BigFontFileName{set;get}` | 大字体文件名 | | `FontDescriptor Font` | 系统字体描述符 | | `public double TextSize{set;get}` | 样式文字高度 | | `XScal` | 宽度缩放因子 | :bookmark:大字体:用于非西方语言的字符,有的字体不支持中文,则还需要另行设置大字体。 ```c# /// /// 添加文本样式 /// /// 样式名 /// 高度 /// 宽度系数 /// 倾斜度 /// 返回样式的ObjectId public static ObjectId AddTextStyle(string styleName, double height, double xScale=1,double oblique = 0) { ObjectId id = ObjectId.Null; //当前数据库 Database db = HostApplicationServices.WorkingDatabase; using (OpenCloseTransaction trans = new OpenCloseTransaction()) { //获取文字样式表 TextStyleTable textStyle = trans.GetObject(db.TextStyleTableId, OpenMode.ForRead) as TextStyleTable; if (textStyle.Has(styleName)) id = textStyle[styleName]; else { //没有则新增样式 TextStyleTableRecord textRecord = new TextStyleTableRecord(); textRecord.Name = styleName;//样式名称 textRecord.FileName = "isocp.shx";//字体文件名 textRecord.BigFontFileName = "gbcbig.shx";//大字体文件名 textRecord.ObliquingAngle = oblique;//倾斜度 textRecord.TextSize = height;//文字高度 textRecord.XScale = xScale;//宽度系数 //打开表 textStyle.UpgradeOpen(); id = textStyle.Add(textRecord);//添加表中 textStyle.DowngradeOpen(); //添加进事务处理 trans.AddNewlyCreatedDBObject(textRecord, true); trans.Commit();//提交 } } return id; } //字体与文字样式关联 public static void AddText(string value,ObjectId styleId,Point3d p) { DBText dBText = new DBText();//单行文字 //事务处理获取样式对象 using (OpenCloseTransaction trans = new OpenCloseTransaction()) { TextStyleTableRecord textRecord = trans.GetObject(styleId, OpenMode.ForRead)as TextStyleTableRecord; dBText.TextStyleId = styleId;//关联文字样式 dBText.Position = p;//插入位置 dBText.TextString = value;//文本赋值 dBText.Height = textRecord.TextSize;//文本高度 dBText.Oblique = textRecord.ObliquingAngle;//倾斜 dBText.WidthFactor = textRecord.XScale;//宽度系数 trans.Commit();//提交 } Database db = HostApplicationServices.WorkingDatabase; db.AddElementToEntity(dBText); } ``` 使用`Font`属性 ```c# //字体设置 textRecord.Font = new FontDescriptor("Arial",true,false,0 ,0); //参数 :系统字体,粗体,斜体,字符集,自宽与语义系数 ``` :bookmark:设置或获取当前文字样式`ObjectId db.TextStyle`. ## 层表LayerTable > 创建新的图层就是创建一条新的层表记录,并将其添加到层表中。 :bookmark:层表常用方法 | 方法 | 释义 | | --------------------- | -------------------------------------------- | | `GenerateUsageData()` | 对层表用户数据进行更新,常用`IsUsed`属性连用 | :bookmark:图层表记录常用属性 | 属性 | 释义 | | --------------------------- | ------------------------------------------------------------ | | `IsUsed{get;}` | 判断图层是否被使用,0图层,当前图层,外部参照图层,有实体对象的图层都为true | | `Color{set;get}` | 图层颜色 | | `LineWeight{set;get}` | 线宽,`LineWeight`枚举类型,可指定`LineWeight.ByLayer`,线宽随层 | | `ObjectId LinetypeObjectId` | 设置图层线型,接受线型`ObjectId`, | ### 创建图层 :red_circle:图层名称重复,有空格`" "`,空""或其他特殊字符会在给`Name`属性赋值时报错。 ``` c# public struct LayerStatus { public string Name; public string status; } public static class LayerTool { public static LayerStatus AddLayer(this Database db,Color color,string layerName) { LayerStatus layerStatus = new LayerStatus(); try { //校验名称 SymbolUtilityServices.ValidateSymbolName(layerName, false);//不允许 | } catch { layerStatus.status = "fail-非法"; return layerStatus;//校验不通过则直接返回 } //表操作需要在事务处理中进行 using(Transaction trans = db.TransactionManager.StartTransaction()) { //获取层表对象并以只读方式打开 LayerTable lt = trans.GetObject(db.LayerTableId,OpenMode.ForRead) as LayerTable; //判断层表中是否由此记录名称 if (!lt.Has(layerName)) { //无则向下进行 lt.UpgradeOpen();//提升为写 //创建层表记录对象,也就是图层 LayerTableRecord ltr = new LayerTableRecord(); ltr.Name = layerName;//名称,继承SymbolTableRecord ltr.Color = color;//颜色 //添加图层 lt.Add(ltr); trans.AddNewlyCreatedDBObject(ltr, true); trans.Commit();//提交 layerStatus.Name = layerName;//保存图层名称 layerStatus.status = "ok";//保存图层状态 } else { //有重复名 layerStatus.status = "fail-重复"; } } return layerStatus; } } ``` CAD中加载自定义图层(部分线型来自机械CAD中的自定义线型): ```c# public static void AddLayer(this Database db) { string[] layerNames = new string[] {"抛面线","文字","尺寸线","中心线","不可见轮廓线","虚线","轮廓线" }; byte[] indexs = new byte[] { 1, 2, 3, 4, 3, 6, 7 };//颜色索引 string[] lineTypes = new string[] { "CONTINUOUS", "CONTINUOUS", "CONTINUOUS", "AM_ISO08W050", "AM_ISO09W050", "AM_ISO02W050", "CONTINUOUS" }; using(Transaction trans = db.TransactionManager.StartTransaction()) { //未做名称处理 //打开层表 LayerTable lt = trans.GetObject(db.LayerTableId,OpenMode.ForWrite) as LayerTable; //线型表 LinetypeTable ltt = trans.GetObject(db.LinetypeTableId,OpenMode.ForRead)as LinetypeTable; for(int i = 0; i < layerNames.Length; i++) { if (!lt.Has(layerNames[i])) //没有该名称则添加 { LayerTableRecord ltr = new LayerTableRecord(); ltr.LinetypeObjectId = ltt[lineTypes[i]];//线型 ltr.Name = layerNames[i];//图层名称 ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, indexs[i]);//颜色索引 lt.Add(ltr);//添加图层到层表中 trans.AddNewlyCreatedDBObject(ltr, true);//确认添加 } } trans.Commit();//提交事务 } } ``` ### 编辑图层属性 基本步骤(需在事务处理中进行): :one::获取层表对象 :two::获取层表记录对象,进行属性编辑操作。 ```c# /// /// 编辑图层 /// /// 图形数据库 /// 图层名称 /// 颜色索引 /// 线宽 public static void EditorLayer(this Database db,string layerName,byte colorIndex, LineWeight weight = LineWeight.LineWeight000)//设置默认值 { //开启事务处理 using(Transaction trans = db.TransactionManager.StartTransaction()) { //获取层表,只读打开 LayerTable lt = trans.GetObject(db.LayerTableId,OpenMode.ForRead) as LayerTable; //判断是否有该记录 if (!lt.Has(layerName)) return; //获取层表记录 LayerTableRecord ltr = lt[layerName].GetObject(OpenMode.ForWrite) as LayerTableRecord; ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);//设置颜色 ltr.LineWeight = weight; trans.Commit();//提交事务 } } ``` ### 设置当前图层 使用数据库的属性`ObjectId Clayer`来设置当前图层。 ```c# /// /// 设置当前图层 /// /// 图形数据库 /// 图层名称 public static void SetCurrentLayer(this Database db,string LayerName) { //获取层表 using (Transaction trans = db.TransactionManager.StartTransaction()) { //获取层表 LayerTable lt = db.LayerTableId.GetObject(OpenMode.ForRead) as LayerTable; if(lt.Has(LayerName)) { //获取层表记录Id ObjectId layerId = lt[LayerName]; if (db.Clayer != layerId) { //设置当前图层 db.Clayer = layerId; } } trans.Commit();//提交 } } ``` ### 获取所有图层 `LayerTable`类是由`LayerTableRecord`组成的集合类,可以进行迭代。 ```c# //获取所有图层名称 public static List GetAllLayers(this Database db) { //开启事务处理 using(Transaction trans = db.TransactionManager.StartTransaction()) { //打开层表 LayerTable lt = db.LayerTableId.GetObject(OpenMode.ForRead) as LayerTable; //初始化泛型 List layerNames = new List(); foreach(var item in lt)// ObjectId item,图层表记录ID { //获取层表记录名称 string name = ((LayerTableRecord)trans.GetObject(item, OpenMode.ForRead)).Name; layerNames.Add(name);//添加名称 } return layerNames; } } ``` ### 删除图层 `IsUsed`属性用来判断图层是否被使用,0图层,当前图层,外部参照图层,有实体对象的图层都为true. :red_circle:使用之前需要使用`LayerTable.GenerateUsageData()`对层表的用户数据进行更新,否则可能会出现未包含对象的图层或未依赖外部参照的图层`IsUsed`属性为`true`. ```c# public static void EraseLayer(this Database db,string layerName) { using(Transaction trans = db.TransactionManager.StartTransaction()) { //打开层表 LayerTable lt = trans.GetObject(db.LayerTableId,OpenMode.ForRead) as LayerTable; if (!lt.Has(layerName)) return; //没有则退出 //打开层表记录 LayerTableRecord ltr = trans.GetObject(lt[layerName],OpenMode.ForRead) as LayerTableRecord; //更新图层数据 lt.GenerateUsageData(); if(!ltr.IsUsed) { //未被使用 ltr.UpgradeOpen();//提升为写 ltr.Erase();//继承于DBObject ltr.DowngradeOpen();//降为只读 } trans.Commit(); } } ``` ### 实体与图层 实体的属性如线型,颜色,宽度有以下3种设置方法。 :one:显示设置:显示指定实体的颜色,线型等。 :two:`ByLayer`:将实体关联到目标图层,而后设置实体相关属性为`ByLayer`,及继承于当前图层。 :three:`ByBlock`: 暂 实体属性优先级:显示设置>`ByBlock`>`ByLayer`。 ## 线型LineType `CAD`标准文件线型一共两个,一个是英制`acad.lin`,另一个是公制`acadiso.lin`。每次`CAD`图形会自动加载3种线型,`BYBLOCK`,`BYLAYER`,`CONTINUOUS`三种线性。 加载其他标准线型,使用数据库下的`public void LoadLineTypeFile(string lineTypeName,string fileName)`函数。 ```c# public static ObjectId AddLineType(this Database db,string lineName) { using (Transaction trans = db.TransactionManager.StartTransaction()) { //打开线型表 LinetypeTable lt= trans.GetObject(db.LinetypeTableId,OpenMode.ForRead)as LinetypeTable; if(!lt.Has(lineName)) { //没有指定线型则加载线型 lt.UpgradeOpen();//提升为写 db.LoadLineTypeFile(lineName, "acad.lin"); //AutoCAD Mechanical中加载amgdt.lin文件 trans.Commit();//提交 return lt[lineName]; } else return ObjectId.Null; } } ``` 加载线型后,可通过图层的`LineTypeObjectId`属性设置线型。 ## 尺寸样式表 :red_circle:样式模板 ```c# /// /// 标注样式模板 /// /// /// 样式名称 /// 返回标注样式ID public static ObjectId AddDimStyle(this Database db, string dimName) { ObjectId id = ObjectId.Null; using (OpenCloseTransaction trans = new OpenCloseTransaction()) { //获取标注样式表 DimStyleTable dimStyle = trans.GetObject(db.DimStyleTableId,OpenMode.ForRead) as DimStyleTable; if (!dimStyle.Has(dimName)) { //没有该名称则新增样式 DimStyleTableRecord dimRecord = new DimStyleTableRecord(); dimRecord.Name = dimName;//样式名称 //尺寸相关属性 //尺寸线颜色随层 dimRecord.Dimclrd = Color.FromColorIndex(ColorMethod.ByLayer, 256); //尺寸界限线颜色 dimRecord.Dimclre = Color.FromColorIndex(ColorMethod.ByLayer, 256); //尺寸界限超出尺寸线距离 dimRecord.Dimexe = 1; //尺寸界限据对象起点的距离 dimRecord.Dimexo = 1; //符号与箭头 dimRecord.Dimasz = 3;//箭头大小 //文字相关 //获取文字样式ID dimRecord.Dimtxsty = AddTextStyle(db, "GB_文字"); ;//文字样式 dimRecord.Dimtxt = 3.5;//文字高度 dimRecord.Dimclrt = Color.FromColorIndex(ColorMethod.ByAci, 2);//文字颜色 dimRecord.Dimtad = 1;//文字在尺寸线垂直方向的上方,0置中 dimRecord.Dimtih = false;// 文字与尺寸对齐方式,ISO标准 //标注文字放在尺寸界线外侧时,尺寸线是否绘制在尺寸界线之间 dimRecord.Dimtofl = true; //全局比例 dimRecord.Dimscale = 1; //主单位 //线型标注精度 dimRecord.Dimdec = 2;//保留两位小数 dimRecord.Dimzin = 8;//不输出后续0 //角度精度 dimRecord.Dimadec = 1; dimRecord.Dimazin = 2;//不输出后续零 //事务操作 dimStyle.UpgradeOpen();//写 id = dimStyle.Add(dimRecord); dimStyle.DowngradeOpen(); trans.AddNewlyCreatedDBObject(dimRecord, true); trans.Commit();//提交事务 db.Dimstyle = id;//设置当前样式 //防止出现样式替代问题 db.SetDimstyleData(dimRecord); } } return id; } ``` 更多内部变量参照:[《CAD用户手册》](https://help.autodesk.com/view/ACD/2018/CHS/?guid=GUID-8EA3CEFF-F842-4EFD-B7D8-3D9BA6AFBBCF) `DIMBLK`设置箭头形状: ```c# //设置系统变量,加载箭头自定义块 Application.SetSystemVariable("DIMBLK", "_DOTSMAll"); //恢复原来的系统变量 Application.SetSystemVariable("DIMBLK", "."); //设置箭头形状 dimRecord.Dimblk = bt["_DOTSMALL"]; ``` `DIMBLK1`设置箭头形状: ```c# //// 启用独立箭头设置 Application.SetSystemVariable("DIMSAH", 1); Application.SetSystemVariable("DIMBLK", "_DOTSMAll"); //恢复原来的系统变量 Application.SetSystemVariable("DIMBLK1", "."); ... // 确保标注样式同步DIMSAH状态 dimRecord.Dimsah = true; dimRecord.Dimblk1 = bt["_DOTSMAll"]; ``` # 块 :book:块表记录`BlockTableRecord`常用方法: :one:添加实体到块表记录中 ``` public ObjectId AppendEntity( Entity entity); ``` :book: 常用属性 | 属性 | 释义 | | ------------------------------------ | ---------------- | | `bool HasAttributeDefinitions{get;}` | 是否包含属性定义 | | `bool Explodable{set;get}` | 是否可以炸开 | | `BlockScaling BlockScaling{set;get}` | 缩放模式 | | `string Comments{set;get}` | 块注释 | ## 普通块 自定义块和模型空间为用同一等级,都是`BlockTableRecord`,该是在图形数据库中存储。 ```c# public void BlockTest() { //图形数据库 Database db = HostApplicationServices.WorkingDatabase; //Get using (OpenCloseTransaction trans = new OpenCloseTransaction()) { //只读打开块表 BlockTable bt = trans.GetObject(db.BlockTableId,OpenMode.ForRead) as BlockTable; //定义块 BlockTableRecord btr = new BlockTableRecord(); btr.Name = "New_Blcok"; List entities = entIds();//获取实体集合 bt.UpgradeOpen();//提升为写 for (int i = 0; i < entities.Count; i++) { //实体添加进块中 btr.AppendEntity(entities[i]); } bt.Add(btr);//添加块表记录 //添加进事务中 trans.AddNewlyCreatedDBObject(btr, true); trans.Commit(); } } ``` 插入块参照与插入实体方式相同。 ```c# //参照块,插入位置,自定义块ID public BlockReference( Point3d position, ObjectId blockTableRecord); ``` :bookmark:参照块常用属性 | 属性 | 作用 | | ---------------------------------------------- | -------------- | | `public double Rotation{set;get}` | 旋转弧度 | | `public Scale3d ScaleFactors{set;get}` | 缩放因子 | | `Matrix3d BlockTransform{set;get}` | 块参照变换矩阵 | | `AttributeCollection AttributeCollection{get}` | 块属性集合 | | `double UnitFactor{get}` | 等比缩放因子 | | `double Rotation{set;get}` | 旋转弧度 | `ScaleFactors`有两个有参的构造函数,:one:整体进行缩放,:two:基于块的原型在`X,Y,Z`三个方向上进行缩放,要求缩放因子不为0. ```c# public void AddBlockReference(ObjectId id, Point3d position,Scale3d scale ,double rotation =0) { //块所在的数据库 Database db = id.Database; //开启事务处理 using (OpenCloseTransaction trans = new OpenCloseTransaction()) { //获取块表 BlockTable bt = trans.GetObject(db.BlockTableId,OpenMode.ForRead) as BlockTable; //模型空间 BlockTableRecord btr = trans.GetObject(bt[BlockTableRecord.ModelSpace],OpenMode.ForRead) as BlockTableRecord; //定义参照块 BlockReference bref = new BlockReference(position, id); bref.Rotation = rotation;//旋转角度 bref.ScaleFactors = scale;//放大比例 //加入到模型空间 btr.UpgradeOpen(); btr.AppendEntity(bref); btr.DowngradeOpen();//读 //添加到事务处理 trans.AddNewlyCreatedDBObject(bref, true); trans.Commit();//提交 } } ``` ## 属性块 参照块给块添加额外属性。 ![image-20250415215732842](assets/image-20250415215732842.png) :bookmark:标记即属性名称,默认为属性默认值,提示为提示用户输入相关内容。 属性块定义: :one:添加块定义 :two:定义属性块`AttributeDefinition`,继承于`DBText`,将其添加进块定义中。 ```c# public static ObjectId AddBlcok(this Database db,string name,Entity ent, params AttributeDefinition[] attributes) { using (OpenCloseTransaction trans = new OpenCloseTransaction()) { ObjectId id = ObjectId.Null; //打开块表 BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; //块定义 BlockTableRecord btr = new BlockTableRecord { Name = name, //等比缩放 BlockScaling = BlockScaling.Uniform }; //添加实体 btr.AppendEntity(ent); foreach(AttributeDefinition att in attributes) { //添加属性 btr.AppendEntity(att); } bt.UpgradeOpen(); id = bt.Add(btr); trans.AddNewlyCreatedDBObject(btr, true); trans.Commit();//提交事务 return id; } } ``` 属性块插入: :one:添加块参照 :two:遍历块定义中的实体,将`AttributeDefinition`过滤出来 :three:根据属性定义中的属性新增`AttributeReference`属性参照。 :four:将属性参照添加进块参照的`AttributeCollection`集合中 ```c public static void InsertBlockRef(this Database db,ObjectId blockId,Point3d position, Scale3d scale,string des) { //新增块参照 BlockReference bRef = new BlockReference(position,blockId); bRef.ScaleFactors = scale;//比例 using (OpenCloseTransaction trans = new OpenCloseTransaction()) { //打开块表 BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; //打开模型空间 BlockTableRecord space = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord; //获取块定义对象 BlockTableRecord btr = trans.GetObject(blockId, OpenMode.ForRead) as BlockTableRecord; space.UpgradeOpen(); space.AppendEntity(bRef); trans.AddNewlyCreatedDBObject(bRef, true); //若存在属性定义 if (btr.HasAttributeDefinitions) { //遍历 foreach(ObjectId id in btr) { AttributeDefinition att = trans.GetObject(id, OpenMode.ForRead) as AttributeDefinition; if(att != null) { //新建属性参照 AttributeReference attRef = new AttributeReference(); //重要方法,用以继承属性块 attRef.SetAttributeFromBlock(att, bRef.BlockTransform); attRef.TextString = "sad"; attRef.AdjustAlignment(db);//强制调整 bRef.AttributeCollection.AppendAttribute(attRef); } } } else { } trans.Commit(); } } ``` # 几何类与几何计算 [线性代数基础参考地址](线性代数基础.md) ## CircularArc3d 构造函数 一 ```csharp CircularArc3d cArc = new CircularArc3d(Point3d startPoint, Point3d onArcPoint,Point3d endPoint) ``` cArc 对象中的属性: | 属性 | 解释 | | ------------------------------- | ---- | | `Double cArc.Radian{get;set;}` | 半径 | | `Point3d cArc.Center{set;get;}` | 圆心 | ## Point2d | 方法 | 解释 | | ---------------------- | ---------------------------------------- | | `p1.GetVectorTo(p2)` | 从 p1 点指向 p2 点的向量,要求点为二维点 | | `p1.GetDistanceTo(p2)` | 计算二维点的距离 | ## Point3d :one: 常用属性 | 属性 | 解释 | | -------------------------- | ---------------- | | `Point3d Origin{get;}` | 原点(0,0,0) | | `Double point.X/Y/Z{get;}` | 点在三轴上的分量 | :two: 常用方法 | 方法 | 解释 | | ------------------------------------------------------------ | ---------------------------------------- | | `Point3d Add(Vector3d value );` | 与向量相加,与`+`作用一致 | | `Point3d Subtract(Vector3d value );` | 与向量相减,与`-`作用一致 | | ` Vector3d GetVectorTo(Point3d p2)` | 从 p1 点指向 p2 点的向量,等同于两点相减 | | `Point3d MultiplyBy(double value );` | 点`X`标量 | | `Point3d DivideBy(double value );` | 除以一个标量 | | `Point3d TransformBy(Matrix3d leftSide );` | 用矩阵`leftSide`乘以当前点 | | `Double DistanceTo(point)` | 获取两点之间的距离 | | `Point3d RotateBy(double angle,Vector3d vector,Point3d centerPoint);` | 旋转 | | `Point3d ScaleBy(double scaleFactor, Point3d centerPoint);` | 缩放 | ```c# //创建一个平移矩阵,Y方向平移10 Matrix3d mt = Matrix3d.Displacement(Vector3d.YAxis * 10); Point3d o = Point3d.Origin; Application.ShowAlertDialog((o.TransformBy(mt)).ToString()); ``` 旋转使用的是右手法则,以`Z`轴为旋转轴,通过正负来控制旋转方向。 ```c# Point3d o = Point3d.Origin; Point3d p = new Point3d(2, 2, 0); //顺时针旋转(2,-2,0) Application.ShowAlertDialog(p.RotateBy(Math.PI / 2, -Vector3d.ZAxis, o).ToString()); ``` 缩放本质:计算目标点与源点的向量,再乘以标量应用到源点上,得到目标点。 ```c# Point3d p = new Point3d(2, 2, 0); //基于点本身缩放,依旧是本身 Application.ShowAlertDialog(p.ScaleBy(2,p).ToString()); ``` ## Vector2d | 属性 | 解释 | | -------------------- | ------------------------------------------------------------ | | `vector.Angle{get;}` | 获取二维向量沿逆时针与 X 正方向的夹角,与直线的 Angel 属性相同。 | | 方法 | 解释 | | --------------------- | ------------------------------------------ | | vector.GetAngleTo(v2) | 计算两个向量之间的弧度,无方向性,值为 0~Π | ## Vector3d :one: 常用属性 | 属性 | 释义 | | --------------------------------- | ---------------- | | `double X/Y/Z{get;}` | 三轴上的分量 | | `double Length{get;}` | 模长 | | `Vector3d XAxis/YAxis/ZAxis{get}` | 三轴上的单位向量 | :two: 常用方法 | 方法 | 解释 | | ------------------------------------------------- | ------------------------------------------ | | `Vector3d Add(Vector3d v );` | 与向量相加,与`+`作用一致 | | vector.GetAngleTo(v2) | 计算两个向量之间的弧度,无方向性,值为 0~Π | | `Vector3d Subtract(Vector3d v );` | 与向量相减,与`-`作用一致 | | `Vector3d MultiplyBy(double value );` | 点*标量 | | `Vector3d CrossProduct(Vector3d vector );` | 叉乘 | | `double DotProduct( Vector3d v );` | 点乘 | | `Vector3d TransformBy(Matrix3d leftSide );` | 用矩阵`leftSide`乘以当前向量 | | `Vector3d DivideBy(double value );` | 除以一个标量 | | `Vector3d RotateBy(double angle,Vector3d axis );` | 旋转 | ## Matrix3d ## `IntersectWith` 方法 ```c public void IntersectWith( Entity entity, // 要相交的另一个实体 Intersect intersectType, // 相交类型(如延伸、不延伸等) Point3dCollection points, // 存储交点的集合 IntPtr thisGraphicSystemMarker, // 保留参数(通常传 IntPtr.Zero) IntPtr otherGraphicSystemMarker // 保留参数(通常传 IntPtr.Zero) ); ``` :bookmark:关键参数说明 | 参数 | 类型 | 说明 | | :------------------------- | :------------------ | :------------------------------------- | | `entity` | `Entity` | 目标实体(如直线、圆、多段线等)。 | | `intersectType` | `Intersect` | 控制如何计算交点(见下文)。 | | `points` | `Point3dCollection` | 用于存储交点的集合(方法执行后填充)。 | | `thisGraphicSystemMarker` | `IntPtr` | 保留参数,始终传 `IntPtr.Zero`。 | | `otherGraphicSystemMarker` | `IntPtr` | 保留参数,始终传 `IntPtr.Zero`。 | :bookmark:`Intersect` 枚举选项 | 值 | 说明 | | :------------------------- | :-------------------------------------------- | | `Intersect.OnBothOperands` | 严格在实体边界内求交(不延伸)。 | | `Intersect.ExtendThis` | 延伸当前实体(调用方法的实体)以查找交点。 | | `Intersect.ExtendOther` | 延伸目标实体(参数中的 `entity`)以查找交点。 | | `Intersect.ExtendBoth` | 同时延伸两个实体以查找交点。 |