HarmonyOS 鸿蒙Next中(在线CAD插件)国产网页CAD二开实现粗糙度标注

HarmonyOS 鸿蒙Next中(在线CAD插件)国产网页CAD二开实现粗糙度标注

前言

表面粗糙度符号是机械制图中的重要标注符号,用于表示零件表面的微观不平度。它的基本形式是一个三角形,尖端从材料外垂直指向被标注的表面。符号的尖端必须从材料外垂直指向被标注的表面,标注可以注在尺寸界线上、轮廓线上、延长线上或代号中‌。在本篇文章中我们将通过解析表面粗糙度符号,调用mxcad二次开发实现粗糙度标注功能。

表面粗糙度符号解析

image-20250516105056600.png

  1. 基本符号:这些符号代表了表面可以用任何方法获得。它们简洁而直观,是表达设计意图的基础。
  2. 加工方法符号:在基本符号的长边上加上一横线,可以标注相关的加工方法。无论是车削、铣削还是其他任何去除材料的方法,都能通过这些符号清晰地表达出来。
  3. 去除材料方法符号:在基本符号上加上一小圆,表示表面是通过去除材料的方法获得的。这不仅可以是车、铣等传统加工方法,也可以是磨削、抛光等更精细的处理方式。
  4. 相同去除方法符号:如果多个表面具有相同的去除材料方法,可以在基本符号上加上一个小圆,表示这些表面有相同的粗糙度要求。
  5. 保持原供应状况符号:在某些情况下,表面需要保持其原始供应状况,这时可以在基本符号上加上一个小圆,表示这些表面不需要进行任何额外的处理。
  6. 符号与代号的组合:在实际设计中,我们可能会遇到多种符号的组合使用。例如,基本符号加一短线和说明划线,表示表面是通过特定的去除材料方法获得的。

自定义实体实现

根据上述内容中对粗糙度符号的分析,我们可以得到粗糙度标注的核心数据,并根据这些数据通过mxcad里的自定义实体McDbCustomEntity实现粗糙度标注实体。

1. 基本结构设置

 export class McDbTestRoughness extends McDbCustomEntity {
     // 基本属性定义
     private position: McGePoint3d = new McGePoint3d();  // 标注位置
     private textDownString: string[] = ['1.6'];         // 下方文本
     private textUpString: string[] = [];                // 上方文本
     private textLeftString: string = '';                // 左侧文本
     private CornerType: string = '';                    // 角标类型
     private markType: number = 0;                       // 标注类型
     private dimSize: number = 5;                        // 标注尺寸
     private rotation: number = 0;                       // 旋转角度
     private dimHeight: number = 10;                     // 标注高度
     private isSameRequire: boolean = false;             // 是否是相同需求
     private isAddLongLine: boolean = false;             // 是否加长处理
     private isMostSymbols: boolean = false;             // 多数符号
     // 包围盒
     private minPt: McGePoint3d = new McGePoint3d();
     private maxPt: McGePoint3d = new McGePoint3d();
 }

2. 构造函数和创建方法

   constructor(imp?: any) {
       super(imp);
   }
   public create(imp: any) {
       return new McDbTestRoughness(imp);
   }
   public getTypeName(): string {
       return "McDbTestRoughness";
   }

3. 数据持久化

// 读取自定义实体数据
     public dwgInFields(filter: IMcDbDwgFiler): boolean {
         this.position = filter.readPoint("position").val;
         this.minPt = filter.readPoint("minPt").val;
         this.maxPt = filter.readPoint("maxPt").val;
         const textDownStr = filter.readString("textDownStr").val;
         this.textDownString = textDownStr.split(',').filter((item) => item != "");;
         const textUpStr = filter.readString("textUpStr").val;
         this.textUpString = textUpStr.split(',').filter((item) => item != "");;
         this.textLeftString = filter.readString("textLeftStr").val;
         this.CornerType = filter.readString('CornerType').val;
         this.markType = filter.readLong('markType').val;
         this.dimSize = filter.readDouble('dimSize').val;
         this.rotation = filter.readDouble('rotation').val;
         this.dimHeight = filter.readDouble('dimHeight').val;
         this.isSameRequire = filter.readLong('isSameRequire').val == 1 ? true : false;
         this.isAddLongLine = filter.readLong('isAddLongLine').val == 1 ? true : false;
         this.isMostSymbols = filter.readLong('isMostSymbols').val == 1 ? true : false;
         return true;
     }
     // 写入自定义实体数据
     public dwgOutFields(filter: IMcDbDwgFiler): boolean {
         filter.writePoint("position", this.position);
         filter.writePoint("maxPt", this.maxPt);
         filter.writePoint("minPt", this.minPt);
         const textDownStr = this.textDownString.join(',');
         const textUpStr = this.textUpString.join(',');
         filter.writeString("textDownStr", textDownStr);
         filter.writeString("textUpStr", textUpStr);
         filter.writeString("textLeftStr", this.textLeftString);
         filter.writeString('CornerType', this.CornerType);
         filter.writeLong('markType', this.markType);
         filter.writeDouble('dimSize', this.dimSize);
         filter.writeDouble('rotation', this.rotation);
         filter.writeDouble('dimHeight', this.dimHeight);
         filter.writeLong('isSameRequire', this.isSameRequire ? 1 : 0);
         filter.writeLong('isAddLongLine', this.isAddLongLine ? 1 : 0);
         filter.writeLong('isMostSymbols', this.isMostSymbols ? 1 : 0);
         return true;
     }

4. 设置标注夹点及夹点移动规则

// 移动夹点:标注点即为夹点,若夹点移动则标注点随之移动
   public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
       this.assertWrite();
       this.position.x += dXOffset;
       this.position.y += dYOffset;
       this.position.z += dZOffset;
   }
   // 获取夹点
   public getGripPoints(): McGePoint3dArray {
       let ret = new McGePoint3dArray();
       ret.append(this.position);
       return ret;
   }

5. 绘制标注实体

// 获取所有实体
     public getAllEntity(): McDbEntity[] {
         //  根据粗糙度绘制粗糙度形状
         const mxcad = MxCpp.getCurrentMxCAD();
         const entityArr = this.drawShape(this.markType, this.position, this.dimSize, true);
         const pl = entityArr[0] as McDbPolyline;
         const lastPoint = pl.getPointAt(pl.numVerts() - 1).val;
         // 添加左侧文字
         if (this.textLeftString) {
             const textLeft = new McDbText();
             textLeft.textString = this.textLeftString;
             textLeft.height = this.dimSize * (9 / 10);
             textLeft.alignmentPoint = textLeft.position = this.position.clone().addvec(McGeVector3d.kYAxis.clone().mult(this.dimSize * (1 / 10))).addvec(McGeVector3d.kXAxis.clone().negate().mult(this.dimSize))
             textLeft.horizontalMode = McDb.TextHorzMode.kTextRight;
             entityArr.push(textLeft)
         }
         // 添加角标
         if (this.CornerType) {
             const textCorner = new McDbText();
             textCorner.textString = this.CornerType;
             textCorner.height = this.dimSize * (7 / 10);
             textCorner.alignmentPoint = textCorner.position = this.position.clone().addvec(McGeVector3d.kYAxis.clone().mult(this.dimSize * (3 / 10))).addvec(McGeVector3d.kXAxis.clone().mult(this.dimSize * (9 / 10)))
             textCorner.horizontalMode = McDb.TextHorzMode.kTextLeft;
             entityArr.push(textCorner)
         }
         // 相同要求
         if (this.isSameRequire) {
             const cirlce = new McDbCircle();
             cirlce.center = lastPoint.clone();
             cirlce.radius = this.dimSize * (3 / 10);
             entityArr.push(cirlce);
         }
         // 加长横线
         let endX = lastPoint.x;
         // 绘制上标文字
         const height = (7 / 10) * this.dimSize;
         const basePos = lastPoint.clone().addvec(McGeVector3d.kXAxis.clone().mult(this.dimSize * (1 / 2))).addvec(McGeVector3d.kYAxis.clone().mult(this.dimSize * (1 / 5)))
         let basePos_x: number = basePos.x;

         let lineArr: McDbLine[] = []
         if (this.textUpString.length === 1) {
             const text = new McDbText();
             text.textString = this.textUpString[0];
             text.height = height;
             text.alignmentPoint = text.position = basePos;

             entityArr.push(text)

             const { maxPt } = this.getTextBox(text)

             if (maxPt.x > endX) endX = maxPt.x;
             basePos_x = text.position.x;

         } else if (this.textUpString.length === 2) {
             const text1 = new McDbText();
             text1.textString = this.textUpString[0];
             text1.height = height;

             const pos1 = basePos.clone();

             const text2 = new McDbText();
             text2.height = height;
             text2.textString = this.textUpString[1];

             const v = lastPoint.sub(this.position).mult(2 / 3)
             const pos2 = pos1.clone().addvec(v);
             const lastPoint2 = lastPoint.clone().addvec(v);

             text1.alignmentPoint = text1.position = new McGePoint3d(pos2.x, pos1.y);
             text2.alignmentPoint = text2.position = pos2;
             basePos_x = pos2.x;

             const res1 = this.getTextBox(text1)
             const res2 = this.getTextBox(text2)

             const endPt_x = res1.maxPt.x > res2.maxPt.x ? res1.maxPt.x : res2.maxPt.x;
             if (endX < endPt_x) endX = endPt_x + height * 0.3;

             const endPoint2 = new McGePoint3d(endX, lastPoint2.y);
             const line2 = new McDbLine(lastPoint2.x, lastPoint2.y, lastPoint2.z, endPoint2.x, endPoint2.y, endPoint2.y);
             const line3 = new McDbLine(lastPoint.x, lastPoint.y, lastPoint.z, lastPoint2.x, lastPoint2.y, lastPoint2.z);
             lineArr.push(line2);
             entityArr.push(text2);
             entityArr.push(text1);
             entityArr.push(line3);
         }

         // 绘制下标文字
         if (this.textDownString.length) {
             const pos = new McGePoint3d(basePos_x, lastPoint.y);
             this.textDownString.forEach((str, index) => {
                 const text = new McDbText();
                 text.textString = str;
                 text.height = height;
                 let v: McGeVector3d = new McGeVector3d()
                 v = McGeVector3d.kYAxis.clone().negate().mult(height * (index + 1 + (1 / 6)));
                 text.alignmentPoint = text.position = pos.clone().addvec(v);
                 entityArr.push(text)

                 const res = this.getTextBox(text)

                 endX = endX < res.maxPt.x ? res.maxPt.x : endX;
             });
         };

         if (this.isAddLongLine) {
             const endPoint = lastPoint.clone().addvec(McGeVector3d.kXAxis.clone().mult(this.dimSize * 2))
             const line = new McDbLine(lastPoint.x, lastPoint.y, lastPoint.z, endPoint.x, endPoint.y, endPoint.y);
             if (endX < endPoint.x) {
                 endX = endPoint.x;
                 entityArr.push(line);
             }
         }

         const endPoint = new McGePoint3d(endX, lastPoint.y)
         const line = new McDbLine(lastPoint.x, lastPoint.y, lastPoint.z, endPoint.x, endPoint.y, endPoint.y);
         entityArr.push(line)
         if (lineArr.length) lineArr.forEach(line => {
             line.endPoint.x = endX;
             entityArr.push(line);
         })

         // 多数符号
         const drawArc = (params: McGePoint3d[]) => {
             const arc = new McDbArc();
             arc.computeArc(params[0].x, params[0].y, params[1].x, params[1].y, params[2].x, params[2].y);
             entityArr.push(arc)
         }
         if (this.isMostSymbols) {
             // 绘制多数符号(两个圆弧+两条直线)
             // 两个圆弧
             const pt = this.position.clone().addvec(McGeVector3d.kYAxis.clone().mult(this.dimSize));
             const basePt = new McGePoint3d(endX, pt.y);

             const radius = this.dimSize * (7 / 5);
             const center1 = basePt.clone().addvec(McGeVector3d.kXAxis.clone().mult(this.dimSize * (7 / 5)));
             const v1 = McGeVector3d.kXAxis.clone().mult(radius);
             const cirlce1_pt1 = center1.clone().addvec(v1.clone().rotateBy(40 * (Math.PI / 180)));
             const cirlce1_pt2 = center1.clone().addvec(v1.clone());
             const cirlce1_pt3 = center1.clone().addvec(v1.clone().rotateBy(320 * (Math.PI / 180)));
             drawArc([cirlce1_pt1, cirlce1_pt2, cirlce1_pt3]);

             const center2 = basePt.clone().addvec(McGeVector3d.kXAxis.clone().mult(this.dimSize * (12 / 5)));
             const cirlce2_pt1 = center2.clone().addvec(v1.clone().rotateBy(140 * (Math.PI / 180)));
             const cirlce2_pt2 = center2.clone().addvec(v1.clone().negate());
             const cirlce2_pt3 = center2.clone().addvec(v1.clone().rotateBy(220 * (Math.PI / 180)));
             drawArc([cirlce2_pt1, cirlce2_pt2, cirlce2_pt3]);

             // 绘制两条直线
             const point = center1.clone().addvec(center2.sub(center1).mult(1 / 3)).addvec(McGeVector3d.kYAxis.clone().negate().mult(this.dimSize * (4 / 5)));
             this.drawShape(2, point, this.dimSize * (4 / 5), false).forEach(ent => {
                 entityArr.push(ent)
             });
         };

         this.getBox(entityArr);
         const mat = new McGeMatrix3d();
         const _height = this.maxPt.y - this.minPt.y;
         if (this.dimHeight) {
             const scale = this.dimHeight / _height;
             mat.setToScaling(scale, this.position);
         } else {
             mat.setToScaling(1, this.position);
         };
         entityArr.forEach(ent => {
             ent.transformBy(mat);
         })
         return entityArr
     }
     // 根据粗糙度type类型绘制粗糙度形状
     private drawShape(markType, position, dimSize, flag): McDbEntity[] {
         const entityArr: McDbEntity[] = [];
         const v = McGeVector3d.kYAxis.clone().mult(dimSize);
         const vec = McGeVector3d.kXAxis.clone().mult(dimSize * (3 / 5));
         const midPt = position.clone().addvec(v);
         const point1 = midPt.clone().addvec(vec);
         const point2 = midPt.clone().addvec(vec.clone().negate());

         let len = dimSize * (2 / 5);
         if (this.textDownStr.length > 2 && flag) len = dimSize * (1 / 2);
         const point3 = position.clone().addvec(point1.sub(position).mult(len));
         if (markType === 0) {
             //  原始样式(倒三角)
             const pl = new McDbPolyline();
             pl.addVertexAt(point1);
             pl.addVertexAt(point2);
             pl.addVertexAt(position);
             pl.addVertexAt(point3);
             entityArr.push(pl)
         } else {
             // 不封口三角
             const pl = new McDbPolyline();
             pl.addVertexAt(point2);
             pl.addVertexAt(position);
             pl.addVertexAt(point3);
             entityArr.push(pl)
         }

         if (markType === 1) {
             //  带圆:三角形内切圆
             const a = position.distanceTo(point1

更多关于HarmonyOS 鸿蒙Next中(在线CAD插件)国产网页CAD二开实现粗糙度标注的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

在鸿蒙Next中实现在线CAD插件的粗糙度标注,可通过调用ArkUI的Canvas组件进行图形绘制。利用鸿蒙的图形引擎能力,结合国产CAD插件的API接口,实现标注符号的矢量绘制与交互。关键步骤包括:解析CAD数据、定义粗糙度符号的绘制逻辑、处理用户交互事件。需注意鸿蒙的分布式能力,确保标注数据在多设备间的同步。

更多关于HarmonyOS 鸿蒙Next中(在线CAD插件)国产网页CAD二开实现粗糙度标注的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


这是一个非常专业且完整的基于MxCAD在HarmonyOS Next环境中实现粗糙度标注自定义实体的方案。帖子内容清晰地展示了从理论分析到代码实现的全过程。

核心实现评价:

  1. 架构设计合理:通过继承 McDbCustomEntity 创建 McDbTestRoughness 类,封装了粗糙度标注的所有属性和行为,符合CAD二次开发中自定义实体的最佳实践。
  2. 数据模型完整:定义的私有属性(positiontextDownStringmarkTypeisSameRequire等)全面覆盖了GB/T 131-2006标准中粗糙度符号的各类参数,为功能扩展奠定了基础。
  3. 序列化实现正确dwgInFieldsdwgOutFields 方法确保了自定义实体数据能够正确写入DWG文件和从中读取,这是实现数据持久化的关键。
  4. 交互功能完备
    • getGripPointsmoveGripPointsAt 实现了夹点编辑功能,用户可以通过拖动夹点移动标注。
    • worldDrawgetAllEntity 方法负责将内部数据状态实时、准确地渲染为可见的图形(多段线、文字、圆、圆弧等),并支持旋转。
  5. 业务逻辑封装良好drawShape 等方法将复杂的几何计算和图形组合逻辑封装在内部,对外提供简洁的属性和方法(如 setPos, setRotation, 各类 setter/getter),便于上层调用和属性修改。

关于HarmonyOS Next的适配要点:

帖子中展示的代码是标准的TypeScript,使用了MxCAD提供的API。在HarmonyOS Next应用中使用此代码时,需关注以下层面:

  • 运行时环境:确保MxCAD的库或插件已针对HarmonyOS Next的ArkTS运行时完成适配,能够正常调用其提供的Native能力。
  • UI集成:帖子末尾提到的“设置粗糙度弹框”属于应用层UI。在HarmonyOS Next中,此部分应使用ArkUI声明式范式(如@Component@State)重新实现,替代原有的Web或传统JS UI方案,以获得最佳性能和原生体验。
  • 工程配置:将这部分业务逻辑代码正确地集成到HarmonyOS Next的工程模块中,并管理好对MxCAD模块的依赖。

总结:

该实现方案本身技术细节扎实,结构清晰,具有很好的可读性和可维护性。它成功地将机械制图中的粗糙度标注业务逻辑转化为一个可交互、可持久化的CAD自定义实体。在HarmonyOS Next平台上集成的关键,在于确保MxCAD基础库的兼容性,并使用ArkUI重构与之交互的前端界面。

回到顶部