云原生CAD 如何在web端实现AutoCAD的绘圆弧功能
前言
在 Web 端实现 CAD 绘图,核心难点不仅在于图形的渲染,更在于如何复刻桌面端严谨的几何逻辑与交互体验。MxCAD 作为专业的 Web CAD 解决方案,提供了完整的绘图能力体系。
对于开发者而言,MxCAD 的能力分为两个层次:
1.开箱即用的内置命令: 系统预置了符合 CAD 标准的一系列绘图指令,可直接调用,满足基础需求。
2.精细化的 API 二次开发: 针对复杂的业务场景,MxCAD 提供了底层的几何类库与交互接口,允许开发者像搭建积木一样,实现如”多模式圆弧绘制”等高度定制化的专业功能。
本文将首先介绍如何快速调用内置命令,随后以工程制图中最为复杂的“圆弧绘制”为例,深入解析如何利用 MxCAD API 实现精细化的图形开发。
一、内置绘图命令的快速调用
MxCAD 为开发者预置了一套完善的基础图形绘制命令体系。这些命令经过高度封装,用户无需关注底层的几何计算与渲染逻辑,即可直接调用以执行标准的绘图操作。
MxCAD 内置的命令覆盖了 CAD 绘图中最常用的几大类图元:
- 线性与点类: 包含
Mx_Line(直线)、Mx_Point(点)、Mx_Xline(构造线)以及Mx_Ray(射线),满足基础定位与连线需求。 - 多边形与矩形类: 提供
Mx_Polygon(正多边形)和Mx_Rectang(矩形),支持快速生成规则几何闭合区域。 - 基础曲线类: 核心包含
Mx_Arc(圆弧)和Mx_Circle(圆),这是工程制图中不可或缺的元素。 - 复合曲线类: 集成了
Mx_Pline(多段线)和Mx_Spline(样条曲线),用于处理更复杂的路径与拟合线条。
调用方式说明:
内置命令采用了标准化的命名规范,支持直接调用。例如需要绘制一段基础圆弧时,只需在代码中或命令行中直接调用 Mx_Arc 命令,系统便会自动激活圆弧绘制交互流程,极大地简化了基础功能的开发流程。
二、”圆弧”绘制的精细化讲解
在 CAD 绘图中,圆弧(Arc)的绘制逻辑远比直线复杂。AutoCAD 等桌面软件之所以强大,是因为它提供了”三点法”、”起点-圆心-端点”、”起点-角度-弦长”等多种几何约束模式。
MxCAD 的 API 开发核心,就在于如何通过代码精准复刻这些几何逻辑。本节将拆解完整圆弧绘制命令的实现步骤,展示如何利用 MxCAD 提供的几何类库与交互接口,实现精细化的多模式圆弧绘制。
1. 圆弧绘制的核心数据结构
在深入具体绘制方式之前,需要了解圆弧在 MxCAD 中的核心参数。无论采用哪种绘制模式(三点绘圆弧除外),圆弧最终都由以下四个参数唯一确定:
- 圆心(center): 圆弧所在圆的圆心坐标
- 半径(radius): 圆弧的半径大小
- 起始角(startAngle): 圆弧的起始角度(弧度制)
- 终止角(endAngle): 圆弧的终止角度(弧度制)
MxCAD 中圆弧实体通过McDbArc类表示,所有绘制模式的最终目标都是计算出这四个参数,然后赋值给圆弧对象并添加到数据库。
import { McDbArc, MxCpp } from "mxcad";
const arc = new McDbArc();
arc.startAngle = startAngle;
arc.endAngle = endAngle;
arc.radius = radius;
arc.center = center;
MxCpp.mxcad.drawEntity(arc);
2. 圆弧的多种绘制方式
在实际 CAD 绘图中,用户可能掌握的信息各不相同——有时知道圆弧经过的三个点,有时知道圆心和起止角度,有时则需要相切连接。MxCAD 的 API 为每种场景都提供了对应的构造方法。下面逐一解析源码中实现的七种核心绘制模式。
2.1 三点画弧(3P)
知道圆弧起点、弧上任意一点(第二点)和圆弧端点,系统自动计算过这三点的唯一圆,生成圆弧。
import { McDbArc, MxCpp } from "mxcad";
/**
* 三点计算圆弧
* startPoint 圆弧起始点
* anyPoint 圆弧任意点
* endPoint 圆弧结束点
*/
const arc = new McDbArc();
arc.computeArc(startPoint.x, startPoint.y, anyPoint.x, anyPoint.y, endPoint.x, endPoint.y);
MxCpp.mxcad.drawEntity(arc);
2.2 起点、圆心、端点(SCE)
知道圆弧起点,圆心,圆弧端点,计算并生成圆弧。
适用于已知圆心和半径范围的场景,如机械零件的倒角绘制。
/** 辅助函数:计算两个坐标的角度 */
function angleTo(x1: number, y1: number, x2: number, y2: number): number {
const x = x1 - x2;
const y = y1 - y2;
let angle_temp = 0;
if (x == 0) {
angle_temp = Math.PI / 2;
} else {
angle_temp = Math.atan(Math.abs(y / x));
}
if (x < 0 && y >= 0) {
angle_temp = Math.PI - angle_temp;
} else if (x < 0 && y < 0) {
angle_temp = Math.PI + angle_temp;
} else if (x >= 0 && y < 0) {
angle_temp = Math.PI * 2.0 - angle_temp;
}
return angle_temp;
}
import { McDbArc, MxCpp } from "mxcad";
/**
* startPoint:圆弧起始点
* pt:圆弧圆心
* endPoint:圆弧结束点
*/
// 用户指定圆心
const center = pt;
// 半径由圆心到起点的距离自动计算
const radius = center.distanceTo(startPoint);
// 起始角:起点相对于圆心的极角(减去PI,统一坐标系)
const startAngle = angleTo(center.x, center.y, startPoint.x, startPoint.y) - Math.PI;
// 终止角:当前鼠标位置相对于圆心的极角
const endAngle = angleTo(center.x, center.y, endPoint.x, endPoint.y) - Math.PI;
// 赋值给圆弧对象并绘制
// center、radius、startAngle、endAngle
2.3 起点、圆心、角度(CSA)
知道圆弧起点、圆弧圆心和圆弧夹角(弧度值),系统自动生成指定角度的圆弧。常用于机械制图中已知旋转角度的场景,如螺栓孔的均布排列。
// 辅助函数:由圆心、起始角和圆弧夹角反推终点的直角坐标
function findEndPoint(C: McGePoint3d, startAngle: number, centralAngle: number, radius: number): McGePoint3d {
const endAngle = startAngle + centralAngle;
return new McGePoint3d(
C.x + radius * Math.cos(endAngle),
C.y + radius * Math.sin(endAngle)
);
}
// 辅助函数:由圆心、开始点、结束点和圆弧起始角度反推圆弧结束角度
function findEndAngle(C: McGePoint3d, P1: McGePoint3d, P2: McGePoint3d, startAngle: number) {
// 计算起始向量和结束向量
const V1 = { x: P1.x - C.x, y: P1.y - C.y };
const V2 = { x: P2.x - C.x, y: P2.y - C.y }
// 计算起始角和结束角
const theta1 = Math.atan2(V1.y, V1.x);
let theta2 = Math.atan2(V2.y, V2.x);
// 计算结束角相对于起始角的夹角差
let deltaTheta = theta2 - theta1;
// 如果夹角差为负值,则加上 2*pi
if (deltaTheta < 0) {
deltaTheta += 2 * Math.PI;
}
// 计算结束角
const endAngle = startAngle + deltaTheta;
return endAngle;
}
/**
* centerAngle:圆弧夹角
* center:圆弧圆心
* startPoint:圆弧开始点
*/
// 半径由圆心到起点的距离自动计算
const radius = center.distanceTo(startPoint);
// 起始角固定为起点相对于圆心的极角(圆弧开始角)
const startAngle = Math.PI + angleTo(center.x, center.y, startPoint.x, startPoint.y);
// 根据起始角和圆弧夹角计算终点
const endPoint = findEndPoint(center, startPoint, centerAngle, radius);
// 计算结束角度
const endAngle = findEndAngle(center, startPoint, endPoint, startAngle);
// 赋值给圆弧对象并绘制
// center、radius、startAngle、endAngle
2.4 起点、圆心、弦长(CSL)
知道圆弧起点、圆弧圆心和圆弧弦长(弧两端点之间的直线距离),系统自动计算对应的圆弧。适用于已知弧跨度的工程场景。
// 辅助函数:由圆弧圆心、圆弧开始点、弦长、圆弧半径反推圆弧结束点
function calculateEndPoint(centerX, centerY, startX, startY, chordLength, radius): McGePoint3d {
let vectorX = startX - centerX;
let vectorY = startY - centerY;
// 弦长对应的圆心角
let theta = chordLength / radius;
// 旋转向量得到终点坐标
let endX = vectorX * Math.cos(theta) - vectorY * Math.sin(theta);
let endY = vectorX * Math.sin(theta) + vectorY * Math.cos(theta);
endX += centerX;
endY += centerY;
return new McGePoint3d(endX, endY);
}
/**
* length:弦长
* center:圆弧圆心
* startPoint:圆弧开始点
*/
// 半径由圆心到起点的距离自动计算
const radius = center.distanceTo(startPoint);
// 起始角固定为起点相对于圆心的极角(圆弧开始角)
const startAngle = Math.PI + angleTo(center.x, center.y, startPoint.x, startPoint.y);
// 用户输入弦长后,调用辅助函数计算终点
const endPoint = calculateEndPoint(center.x, center.y, startPoint.x, startPoint.y, length, arc.radius);
// 计算终点相对于圆心的极角作为终止角
const endAngle = angleTo(center.x, center.y, endPoint.x, endPoint.y) - Math.PI;
// 赋值给圆弧对象并绘制
// center、radius、startAngle、endAngle
2.5 起点、端点、角度(SEA)
知道圆弧起点、圆弧端点和圆弧夹角,系统自动反算出圆心位置并生成圆弧。这是一种”先定跨度、再定弧度”的绘制方式。
/** 根据开始点和节点和圆心夹角 计算除圆心位置 */
function findArcCenter(P1: McGePoint3d, P2: McGePoint3d, angle: number): McGePoint3d {
// 计算圆弧的中点
const M = {
x: (P1.x + P2.x) / 2,
y: (P1.y + P2.y) / 2
};
// 计算圆弧的半径
const r = Math.sqrt((P1.x - M.x) ** 2 + (P1.y - M.y) ** 2) / Math.sin(angle / 2);
// 计算圆心位置
const V = { x: P2.x - P1.x, y: P2.y - P1.y };
const N = { x: V.x / Math.sqrt(V.x ** 2 + V.y ** 2), y: V.y / Math.sqrt(V.x ** 2 + V.y ** 2) };
const R = { x: -N.y, y: N.x };
const x = M.x + r * R.x
const y = M.y + r * R.y
return new McGePoint3d(x, y)
}
/**
* centerAngle:圆弧夹角
* endPoint:圆弧结束点
* startPoint:圆弧开始点
*/
// 由起点、端点和圆弧夹角反算圆心
const center = findArcCenter(startPoint, endPoint, centerAngle);
// 半径即圆心到起点的距离
const radius = center.distanceTo(startPoint);
// 开始角度
const startAngle = angleTo(center.x, center.y, startPoint.x, startPoint.y) - Math.PI
// 结束角度
const endAngle = angleTo(center.x, center.y, endPoint.x, endPoint.y) - Math.PI
// 赋值给圆弧对象并绘制
// center、radius、startAngle、endAngle

2.6 起点、端点、切向(SED)
知道圆弧起点、圆弧端点和圆弧起点的切线方向,系统自动生成与该方向相切的圆弧。这是高级绘图功能,常用于机械制图中光滑过渡的连接弧。
// 辅助函数:由圆弧开始点、圆弧结束点和切向量反推圆弧凸度
function CMxDrawPolylineDragArcDraw_CalcArcBulge(firstPoint: McGePoint3d, nextPoint: McGePoint3d, vecArcTangent: McGeVector3d): number {
if (firstPoint.isEqualTo(nextPoint))
return 0.0;
let midPt = firstPoint.c().addvec(nextPoint.c().sub(firstPoint).mult(0.5));
let vecMid = nextPoint.c().sub(firstPoint);
vecMid.rotateBy(Math.PI / 2.0, McGeVector3d.kZAxis);
let tmpMidLine = new McDbLine(midPt, midPt.c().addvec(vecMid));
let vecVertical: McGeVector3d = vecArcTangent.c();
vecVertical.rotateBy(Math.PI / 2.0, McGeVector3d.kZAxis);
let tmpVerticalLine = new McDbLine(firstPoint, firstPoint.c().addvec(vecVertical));
let aryPoint: McGePoint3dArray = tmpMidLine.IntersectWith(tmpVerticalLine, McDb.Intersect.kExtendBoth);
if (aryPoint.isEmpty())
return 0.0;
let arcCenPoint = aryPoint.at(0);
let dR = arcCenPoint.distanceTo(firstPoint);
vecMid.normalize();
vecMid.mult(dR);
let arcMidPt1 = arcCenPoint.c().addvec(vecMid);
let arcMidPt2 = arcCenPoint.c().subvec(vecMid);
let vecArcDir1 = arcMidPt1.c().sub(firstPoint);
let vecArcDir2 = arcMidPt2.c().sub(firstPoint);
let arcMidPt = arcMidPt1;
if (vecArcDir1.angleTo1(vecArcTangent) > vecArcDir2.angleTo1(vecArcTangent)) {
arcMidPt = arcMidPt2;
}
return MxCADUtility.calcBulge(firstPoint, arcMidPt, nextPoint).val;
}
import { McDbArc, MxCpp, McDbPolyline } from "mxcad";
/**
* vecArcTangent:圆弧起点切线向量
* endPoint:圆弧结束点
* startPoint:圆弧开始点
*/
// 利用 MxCAD 几何库计算圆弧的 bulge(拱高比)
const bulge = CMxDrawPolylineDragArcDraw_CalcArcBulge(startPoint,endPoint,vecArcTangent);
// 通过多段线(Polyline)的 bulge 属性绘制圆弧
pl = new McDbPolyline();
pl.addVertexAt(startPoint, bulge);
pl.addVertexAt(endPoint);
// 根据多段线得到圆弧上的任意点位,再根据三点画圆弧绘制
const length = pl.getLength().val;
const midPt = pl.getPointAtDist(length / 2).val;
const arc = new McDbArc();
arc.computeArc(startPoint.x, startPoint.y, midPt.x, midPt.y, endPoint.x, endPoint.y);
MxCpp.mxcad.drawEntity(arc);
2.7 起点、端点、半径(SER)
知道圆弧起点、圆弧端点和圆弧半径,系统自动求解满足条件的圆心位置(通常有两个解),并生成圆弧。适用于已知弧跨度和拱高的场景。
// 辅助函数:由圆弧开始点、圆弧结束点、圆弧半径反推圆弧圆心
function findArcCenterWithRadius(P1, P2, radius):McGePoint3d {
// 圆心到弦中点的距离
const d = Math.sqrt(
radius ** 2 - ((P1.x - P2.x) ** 2 + (P1.y - P2.y) ** 2) / 4
);
// 弦的中点
const M = {
x: (P1.x + P2.x) / 2,
y: (P1.y + P2.y) / 2
};
// 从中点沿垂直方向偏移 d 得到圆心
return new McGePoint3d(
M.x - d * (P2.y - P1.y) / Math.sqrt((P1.x - P2.x) ** 2 + (P1.y - P2.y) ** 2),
M.y - d * (P1.x - P2.x) / Math.sqrt((P1.x - P2.x) ** 2 + (P1.y - P2.y) ** 2)
);
}
/**
* radius:圆弧半径
* endPoint:圆弧结束点
* startPoint:圆弧开始点
*/
// 由起点、端点和半径反算圆心
const center = findArcCenterWithRadius(startPoint, endPoint, radius);
// 圆弧开始角度
const startAngle = angleTo(center.x, center.y, startPoint.x, startPoint.y) - Math.PI
// 圆弧结束角度
const endAngle = angleTo(center.x, center.y, endPoint.x, endPoint.y) - Math.PI
// 赋值给圆弧对象并绘制
// center、radius、startAngle、endAngle
3. 整合多种绘制方式
开始
|
v
指定圆弧起点(startPoint)
|
v
是否输入关键词 [C/E]?
/
C(是) E(是)
| |
v v
指定圆心(center) 指定端点(endPoint)
| |
v v
指定端点/角度/弦长 指定圆心/角度/切向/半径
[A/L/R] 子选项 [A/D/R] 子选项
| |
+--------+---------+
|
v
绘制圆弧并返回
在实际开发中,我们不需要为每种绘制模式编写独立的函数,而是可以将所有模式整合到一个统一的交互流程中。下面展示实现的完整 drawArc 函数,它能有效管理用户的交互输入,支持在绘制过程中动态切换绘制模式。
该绘制函数采用“起点锚定 + 关键字分流”的交互逻辑,通过动态分支适配不同的绘图习惯:
1.确立基准:首先强制获取圆弧起点,作为所有后续计算的绝对坐标原点。
2.模式决策:在第二步交互中,根据用户输入的关键字将逻辑一分为二:
- 输入 C (圆心模式):优先锁定圆心位置,随后通过角度或弦长确定终点。适用于已知中心点的场景。
- 输入 E (端点模式):优先锁定终点位置,随后通过半径或切向反推圆弧曲率。适用于已知连接目标的场景。
3.闭环绘制:无论走哪条分支,最终都会补全几何约束(如半径、角度),从而生成精确的圆弧实体。
/**
- `startPoint`:已输入的起点(`undefined` 表示尚未输入)
- `anyPoint`:三点法中的第二点(弧上点)
- `endPoint`:已输入的端点
- `center`:已输入的圆心
- `isCenterArc`:是否处于"圆心优先"模式(SCE/CSA/CSL)
- `currentBranch`:当前分支标识(`"E"` 端点优先、`"C"` 圆心优先、`"L"` 弦长模式)
*/
async function drawArc() {
let mxcad = MxCpp.getCurrentMxCAD();
const getPoint = new MxCADUiPrPoint();
const arc = new McDbArc();
let startPoint: McGePoint3d;
let anyPoint: McGePoint3d;
let endPoint: McGePoint3d;
let center: McGePoint3d;
let radius: number;
// 动态预览回调
let draw: (currentPoint: McGePoint3d, pWorldDraw: McEdGetPointWorldDrawObject) => void = () => { };
let msg = "\n指定圆弧的起点:";
let key = "[圆心(C)]";
let isCenterArc = false;
let currentBranch: "E" | "C" | "L";
// 主交互循环
while (true) {
getPoint.setUserDraw(draw);
getPoint.setMessage(msg);
getPoint.setKeyWords(key);
const point = await getPoint.go() as McGePoint3d;
// --- 处理命令行关键词 ---
if (getPoint.isKeyWordPicked("C")) {
// 切换到"圆心优先"模式:起点 → 圆心 → ...
currentBranch = "C";
isCenterArc = true;
if (startPoint) {
getPoint.setMessage("\n指定圆弧的圆心");
const pt = await getPoint.go();
if (!pt) return;
center = pt;
msg = "\n指定圆弧的端点(按住 ctrl 键取相反方向)";
key = "[角度(A)/弦长(L)/修改半径(R)]";
getPoint.setLastInputPoint(center);
guideLine.startPoint = center;
radius = center.distanceTo(startPoint);
// 设置动态预览回调
draw = (currentPoint, pWorldDraw) => {
angle = angleTo(center.x, center.y, startPoint.x, startPoint.y) - Math.PI;
angle1 = angleTo(center.x, center.y, currentPoint.x, currentPoint.y) - Math.PI;
guideLine.endPoint = currentPoint;
drawCenterArc(currentPoint, pWorldDraw);
};
}
continue;
}
if (isCenterArc) {
// --- 圆心优先模式下的子分支 ---
// 关键词 R:修改半径
if (getPoint.isKeyWordPicked("R")) {
getPoint.setMessage("\n指定圆弧的半径");
const pt = await getPoint.go();
if (!pt) return;
radius = pt.distanceTo(center);
msg = "\n指定圆弧的端点(按住 ctrl 键取相反方向)";
key = "[角度(A)/弦长(L)/修改半径(R)]";
continue;
}
// 关键词 A:指定角度
if (getPoint.isKeyWordPicked("A")) {
msg = "指定圆弧夹角(按住 ctrl 键取相反方向)";
angle = Math.PI + angleTo(center.x, center.y, startPoint.x, startPoint.y);
if (!radius) radius = center.distanceTo(startPoint);
draw = (currentPoint, pWorldDraw) => {
const centerAngle = angleTo(center.x, center.y, currentPoint.x, currentPoint.y) - Math.PI;
endPoint = findEndPoint(center, angle, centerAngle, radius);
angle1 = findEndAngle(center, startPoint, endPoint, angle);
drawCenterArc(currentPoint, pWorldDraw);
};
continue;
}
// 关键词 L:指定弦长
if (getPoint.isKeyWordPicked("L")) {
currentBranch = "L";
msg = "\n指定弦长(按住 ctrl 键取相反方向)";
getPoint.setLastInputPoint(startPoint);
draw = (currentPoint, pWorldDraw) => {
const length = currentPoint.distanceTo(startPoint);
if (length > radius * 2) {
guideLine.endPoint = currentPoint;
pWorldDraw.drawMcDbEntity(guideLine);
return;
}
endPoint = calculateEndPoint(center.x, center.y, startPoint.x, startPoint.y, length, arc.radius);
angle1 = angleTo(center.x, center.y, endPoint.x, endPoint.y) - Math.PI;
drawCenterArc(currentPoint, pWorldDraw);
};
continue;
}
// 关键词 D:切向模式
if (getPoint.isKeyWordPicked("D")) {
getPoint.setMessage("\n指定圆弧起点的切向");
getPoint.setLastInputPoint(startPoint);
let pl = new McDbPolyline();
getPoint.setUserDraw((currentPoint, pWorldDraw) => {
const vecArcTangent = currentPoint.sub(startPoint);
const bulge = CMxDrawPolylineDragArcDraw_CalcArcBulge(
isOpposite ? startPoint : endPoint,
isOpposite ? endPoint : startPoint,
vecArcTangent
);
pl = new McDbPolyline();
pl.addVertexAt(startPoint, bulge);
pl.addVertexAt(endPoint);
guideLine.endPoint = currentPoint;
pWorldDraw.drawMcDbEntity(pl, true);
pWorldDraw.drawMcDbEntity(guideLine);
});
const pt = await getPoint.go();
if (!pt) return;
mxcad.drawEntity(pl);
return;
}
// 直接点击端点
if (!center) {
center = point;
continue;
}
if (!startPoint) {
startPoint = point;
radius = startPoint.distanceTo(center);
msg = "\n指定圆弧的端点";
key = "[角度(A)/弦长(L)/修改半径(R)]";
continue;
}
// 绘制完成
mxcad.drawEntity(arc);
return;
}
// --- 非圆心优先模式(三点法 / 端点优先) ---
if (getPoint.isKeyWordPicked("E")) {
// 切换到"端点优先"模式:起点 → 端点 → ...
currentBranch = "E";
getPoint.setMessage("\n指定圆弧的端点");
getPoint.setKeyWords("");
startPoint = startPoint;
const pt = await getPoint.go();
if (!pt) return;
endPoint = pt;
isCenterArc = true;
msg = "\n指定圆弧的圆心(按住 ctrl 键取相反方向)";
key = "[角度(A)/方向(D)/半径(R)]";
guideLine.startPoint = endPoint;
draw = (currentPoint, pWorldDraw) => {
center = currentPoint;
radius = center.distanceTo(startPoint);
angle = angleTo(center.x, center.y, startPoint.x, startPoint.y) - Math.PI;
angle1 = angleTo(center.x, center.y, endPoint.x, endPoint.y) - Math.PI;
drawCenterArc(currentPoint, pWorldDraw);
};
continue;
}
// 三点法主流程
if (!startPoint) {
startPoint = point;
msg = "\n指定圆弧第二个点";
key = "[圆心(C)/端点(E)]";
draw = (currentPoint, pWorldDraw) => {
pWorldDraw.drawLine(startPoint.toVector3(), currentPoint.toVector3());
};
continue;
}
if (!anyPoint) {
anyPoint = point;
msg = "\n指定圆弧的端点";
draw = (currentPoint, pWorldDraw) => {
endPoint = currentPoint;
arc.computeArc(startPoint.x, startPoint.y, anyPoint.x, anyPoint.y, endPoint.x, endPoint.y);
pWorldDraw.drawMcDbEntity(arc, true);
};
continue;
}
// 绘制完成
mxcad.drawEntity(arc);
return;
}
}
MxFun.addCommand("Mx_Arc", drawArc);
三、基础图形开发的通用范式与总结
MxCAD 的基础图形开发并非孤立的知识堆砌,而是一套逻辑严密、高度复用的通用范式。无论是直线、圆、多段线还是文字标注,其底层逻辑都遵循着“交互定义参数 -> 几何计算约束 -> API 实例化实体”的标准化流程。
1. 通用开发思路:三步走策略
第一步:交互参数收集(UI 层) 这是所有图形绘制的起点。利用
MxOcxUiPrPoint(点选)、MxOcxUiPrDist(距离)、MxOcxUiPrAngle(角度)等交互类,精准捕获用户的输入意图。核心在于利用“关键字(Keywords)”机制,在单一的交互流中通过addKeyword动态切换子命令(如圆绘制的“三点/两点/切点”模式),实现复杂功能的轻量化交互。第二步:几何逻辑解算(Math 层) 这是连接用户意图与图形实体的桥梁。在获取基础点位后,需通过几何算法反推图形的核心属性。例如,由三点反求圆心与半径,或由起点、端点及切向推算控制点。这一层要求开发者具备扎实的解析几何基础,确保在极端数据下(如三点共线)程序的健壮性。
第三步:实体构建与上屏(API 层) 这是最终的执行阶段。将解算出的几何参数(如圆心坐标、半径值、起始角)注入到对应的数据库实体类(如
McDbCircle、McDbLine)中,并通过事务处理将其添加至模型空间。此阶段需严格遵循 CAD 的数据库规范,确保图层、线型等属性的正确挂载。
2. 总结
MxCAD 的 API 开发本质上是对 CAD 绘图行为的代码化重构。从简单的直线绘制到复杂的圆弧状态机,其核心都在于对“参数约束”的精准控制。掌握这一套从 UI 交互到几何解算,再到实体生成的完整链路,能让我们轻松驾驭基础图形的开发,更为后续处理块参照、属性文字乃至复杂参数化设计奠定了逻辑基石。以“交互与计算分离、状态与逻辑解耦”的设计原则,构建出符合工程规范的 CAD 应用。
本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu
推荐文章: