前言

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

表面粗糙度符号解析

  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);
               const b = position.distanceTo(point2);
               const c = point1.distanceTo(point2);
               const s = (a + b + c) / 2;
               const h = position.distanceTo(midPt);
               const A = (c * h) / 2;
               const circle = new McDbCircle();
               circle.radius = A / s;
               circle.center = midPt.clone().addvec(McGeVector3d.kYAxis.clone().negate().mult(circle.radius));
               entityArr.push(circle)
           }
   
           return entityArr
       }
       // 绘制实体
       public worldDraw(draw: MxCADWorldDraw): void {
           const entityArr = this.getAllEntity();
           this.getBox(entityArr);
           entityArr.forEach(item => {
               const _clone = item.clone() as McDbEntity;
               if (this.rotation) _clone.rotate(this.position, this.rotation);
               draw.drawEntity(_clone);
           });
       }

6. 暴露获取、设置实体内部数据的属性或方法

  // 获取或设置下标文本内容
       public set textDownStr(val: string[]) {
           this.textDownString = val;
       }
       public get textDownStr(): string[] {
           return this.textDownString;
       }
       // 获取或设置上标文本内容
       public set textUpStr(val: string[]) {
           this.textUpString = val;
       }
       public get textUpStr(): string[] {
           return this.textUpString;
       }
       // 获取或设置左边文本内容
       public set textLeftStr(val: string) {
           this.textLeftString = val;
       }
       public get textLeftStr(): string {
           return this.textLeftString;
       }
       // 获取或设置角标类型
       public set rougCornerType(val: string) {
           this.CornerType = val;
       }
       public get rougCornerType(): string {
           return this.CornerType;
       }
       // 获取或设置标注类型
       public set rougMarkType(val: number) {
           this.markType = val;
       }
       public get rougMarkType(): number {
           return this.markType;
       }
       // 获取或设置相同要求
       public set isRoungSameRequire(val: boolean) {
           this.isSameRequire = val;
           if (val) this.isAddLongLine = true;
       }
       public get isRoungSameRequire(): boolean {
           return this.isSameRequire;
       }
       // 获取或设置加长
       public set isAddRougLongLine(val: boolean) {
           this.isAddLongLine = val;
       }
       public get isAddRougLongLine(): boolean {
           return this.isAddLongLine;
       }
       // 获取或设置多数符号
       public set isShowMostSymbols(val: boolean) {
           this.isMostSymbols = val;
       }
       public get isShowMostSymbols(): boolean {
           return this.isMostSymbols;
       }
       // 获取或设置粗糙度标注高度
       public set rouDimHeight(val: number) {
           this.dimHeight = val;
       }
       public get rouDimHeight(): number {
           return this.dimHeight;
       }
       // 设置粗糙度标注位置
       public setPos(pt: McGePoint3d) {
           this.position = pt.clone();
       }
       // 获取粗糙度标注位置
       public getPos() {
           return this.position;
       }
       // 旋转角度
       public setRotation(angle: number) {
           this.rotation = angle
       }
       // 旋转角度
       public getRotation(): number {
           return this.rotation
       }
       // 获取包围盒
       public getBoundingBox(): { minPt: McGePoint3d; maxPt: McGePoint3d; ret: boolean; } {
           const entityArr = this.getAllEntity();
           this.getBox(entityArr);
           return { minPt: this.minPt, maxPt: this.maxPt, ret: true }
       }

使用粗糙度标注

根据粗糙度标注于直线、圆弧或圆时标注将垂直于曲线的切线上的位置特点,我们可以通过识别标注点所在的实体类型获取该实体在标注点的切线方向和位置,并以此来确定标注的旋转角度和方向。

// 粗糙度
async function Mx_Roughness() {
    const mxcad = MxCpp.getCurrentMxCAD();
    let rotation = 0;
    const roughness = new McDbTestRoughness();
    roughness.rougMarkType = 0;
    roughness.rougCornerType = '';
    const getPos = new MxCADUiPrPoint();
    getPos.setMessage(t('请设置定位点或直线或圆弧或圆'));
    getPos.setUserDraw((pt, pw) => {
        roughness.setPos(pt);
        pw.drawMcDbEntity(roughness)
    });
    const pos = await getPos.go();
    let filter = new MxCADResbuf([DxfCode.kEntityType, "LINE,ARC,CIRCLE,LWPOLYLINE"]);
    let objId = MxCADUtility.findEntAtPoint(pos.x, pos.y, pos.z, -1, filter);
    if (objId.isValid()) {
        const ent = objId.getMcDbEntity();
        // 拖动确定标注位置
        const getDirect = new MxCADUiPrPoint();
        getDirect.setMessage(t('拖动确定标注位置'));
        getDirect.setUserDraw((pt, pw) => {
            const line = ent.clone() as McDbLine;
            const closePt = line.getClosestPointTo(pt, true).val;
            const v = pt.sub(closePt);
            rotation = v.angleTo2(McGeVector3d.kYAxis, McGeVector3d.kNegateZAxis);
            roughness.setPos(closePt);
            roughness.setRotation(rotation);
            pw.drawMcDbEntity(roughness)
        });
        getDirect.setDisableDynInput(true);
        getDirect.disableAllTrace(true);
        const pt = await getDirect.go();
        if (!pt) return;
        mxcad.drawEntity(roughness)
    } else {
        roughness.setPos(pos);
        // 指定旋转方向
        const getAngle = new MxCADUiPrAngle();
        getAngle.setBasePt(pos);
        getAngle.setMessage(t('请指定或输入角度'));
        getAngle.setUserDraw((pt, pw) => {
            const line = new McDbLine(pt, pos);
            pw.drawMcDbEntity(line);
            const v = pt.sub(pos);
            rotation = v.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
            roughness.setRotation(rotation);
            pw.drawMcDbEntity(roughness)
        });
        let val = await getAngle.go();
        if (!val) return;
        const angle = getAngle.value();
        roughness.setRotation(angle);
        mxcad.drawEntity(roughness);
    }
}

效果演示

在线demo查看地址:https://demo2.mxdraw3d.com:3000/mxcad/
基础效果演示:

根据上述内容可做扩展开发,设置粗糙度弹框,其示例效果如下:


梦想云图网页CAD
5 声望3 粉丝