# 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 程序包。

:two: 搜索` autocad .net`,选择22.00版本。
:three: 调试选择 `CAD `外部程序。
测试开发环境
```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`)。

图形数据库由表组成,有块表,层表,样式表等;表的基本单位是记录。块表记录中存储的就是绘制的图形,我们要将直线添加到块表记录中。
: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:事务处理的嵌套:

在当前事务中新增一个事务处理,即事务的嵌套。
: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: 多段线常用属性与方法

: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:如果只有外边界,则会全部填充。

: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`

: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`属性,否则会报错.
对齐图例:

对齐常用枚举值:
* `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中的控制码表示或者直接使用输入法。

```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`会报错,可能不是有效的枚举值。
多行文字格式化代码:

```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`起到`}`结束),互不影响。
## 标注

```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`

| 属性 | |
| --------------------- | -------------------------------------------- |
| 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

对齐标注与尺寸界限两点连成的线段平行。
没有有`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

属性:
: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
> 适合标注两半径之间的夹角或已知两线段的交点

| 属性 | 解释 |
| ------------- | ---------------------------------------------- |
| `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

| 属性 | 解释 |
| ------------- | ---------------------------------------------- |
| `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()_可写降级为只读`来切换打开状态,应当注意的是提交事务处理后,对象的打开状态都会被清除。
矩阵描述了当前的变换信息,左乘矩阵*源点坐标 = 目标位置。
## 添加颜色
先将封装好的图元文档编译成 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`。

### `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`关闭关键字列表

重载:two: `PrompXXXOptions(string messageAndKeywords,string globalKeywords)`,参数1提示信息和关键字列表,参数2为关键字列表字符串(AppendKeywordsToMessage属性关闭的即是这个)
```c#
var options = new PromptDoubleOptions("请输入数值:[E退出/U调整]", "E U")
关键字列表用/间隔,全局关键字用空格间隔,
//无需关闭关键字列表
```
:red_circle:注意关键字须在中文前面

`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正确输入返回值]: 
`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: *注意事项*:

: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`同理。

结果类中的`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`组码:

完整`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();//提交
}
}
```
## 属性块
参照块给块添加额外属性。

: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` | 同时延伸两个实体以查找交点。 |