云原生CAD 如何在web端实现AutoCAD的绘圆弧功能

AI摘要
【知识分享】本文系统介绍MxCAD在Web端实现CAD绘图的技术方案,分为内置命令调用与API二次开发两个层次。内容以圆弧绘制为核心案例,详细解析了七种绘制模式(三点法、圆心优先、端点优先等)的几何计算原理与代码实现,并给出整合多种模式的统一交互流程设计。文章属于技术教程类知识分享,内容专业、表述客观,无违规风险。

前言

在 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 层) 这是最终的执行阶段。将解算出的几何参数(如圆心坐标、半径值、起始角)注入到对应的数据库实体类(如 McDbCircleMcDbLine)中,并通过事务处理将其添加至模型空间。此阶段需严格遵循 CAD 的数据库规范,确保图层、线型等属性的正确挂载。

2. 总结

MxCAD 的 API 开发本质上是对 CAD 绘图行为的代码化重构。从简单的直线绘制到复杂的圆弧状态机,其核心都在于对“参数约束”的精准控制。掌握这一套从 UI 交互到几何解算,再到实体生成的完整链路,能让我们轻松驾驭基础图形的开发,更为后续处理块参照、属性文字乃至复杂参数化设计奠定了逻辑基石。以“交互与计算分离、状态与逻辑解耦”的设计原则,构建出符合工程规范的 CAD 应用。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
48
粉丝
1
喜欢
8
收藏
0
排名:1902
访问:1248
私信
所有博文
社区赞助商