Skip to content
On this page

Arrow comment

Below we will introduce how to use mxcad plug-in to achieve the function of arrow annotation in CAD drawings, in which the user clicks the canvas to determine the starting point of the arrow, and then continuously clicks the canvas to determine the vertex and end position of the arrow lead. In the arrow guide function, users can customize the arrow shape, as well as the content of superscript text and subscript text, and modify the text position according to the drawing needs. The arrow quote function can help users quickly mark the content of the drawing, and increase the integrity and readability of the drawing content.

Function implementation

  1. Implement a custom arrow reference class

In order to facilitate later management and annotation modification, We can through inheritance McDbCustomEntity Customize the entity class to extend the implementation of the custom arrow reference class. Then, We can use McDbMText or McDbText measurement information text object, will mark information on the page.

In the following example, we provide arrow, point, cross, half arrow and other arrow styles in the arrow reference class, as well as the alignment of the upper and lower script text on the line end, the line in the middle, the line end, etc. Users can refer to the following example code for secondary development according to their own project needs.

ts
// Custom arrow reference classes
class McDbTestArrowCitation extends McDbCustomEntity {
    // Defines a point object inside McDbTestConMeasurement 
    // Arrow line points group
    private points: McGePoint3d[] = [];
    // Text point location set
    private positionArr: McGePoint3d[] = [];
    // Text height
    private height: number = 0;
    // Superscript text content
    private _textUp: string = "";
    // Subscript text content
    private _textDown: string = "";
    // Arrow style
    private _arrowType: string = "";
    // Alignment mode
    private _alginType: string = "";
    // Initial record length
    private arrowLength: number = 0;
    // Text rotation Angle
    private angle: number = 0;

    // constructor
    constructor(imp?: any) {
        super(imp);
    }
    // Create function
    public create(imp: any) {
        return new McDbTestArrowCitation(imp)
    }
    // Get class name
    public getTypeName(): string {
        return "McDbTestArrowCitation";
    }
    //Sets or gets the text word height
    public set textHeight(val: number) {
        this.height = val;
    }
    public get textHeight(): number {
        return this.height;
    }
    //Sets or gets superscript text
    public set textUp(val: string) {
        this._textUp = val;
    }
    public get textUp(): string {
        return this._textUp;
    }
    //Sets or gets the subscript text
    public set textDown(val: string) {
        this._textDown = val;
    }
    public get textDown(): string {
        return this._textDown;
    }
    //Sets or gets the arrow style
    public set arrowType(val: string) {
        this._arrowType = val;
    }
    public get arrowType(): string {
        return this._arrowType;
    }
    //Sets or gets the alignment style
    public set alginType(val: string) {
        this._alginType = val;
    }
    public get alginType(): string {
        return this._alginType;
    }
    // Read from defined entity data
    public dwgInFields(filter: IMcDbDwgFiler): boolean {
        this.points = filter.readPoints("points").val;
        this.positionArr = filter.readPoints("positionArr").val;
        this._textDown = filter.readString("textDown").val;
        this._textUp = filter.readString("textUp").val;
        this._arrowType = filter.readString("arrowType").val;
        this._alginType = filter.readString("alginType").val;
        this.arrowLength = filter.readLong("arrowLength").val;
        this.angle = filter.readDouble("angle").val;
        this.height = filter.readDouble("height").val;
        return true;
    }
    // Writes custom entity data
    public dwgOutFields(filter: IMcDbDwgFiler): boolean {
        filter.writePoints("points", this.points);
        filter.writePoints("positionArr", this.positionArr);
        filter.writeString("textDown", this._textDown);
        filter.writeString("textUp", this._textUp);
        filter.writeString("arrowType", this._arrowType);
        filter.writeString("alginType", this._alginType);
        filter.writeLong("arrowLength", this.arrowLength);
        filter.writeDouble("angle", this.angle);
        filter.writeDouble("height", this.height);
        return true;
    }

    // Moves the pinch point of a custom object
    public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
        this.assertWrite();
        const length = this.points.length
        if (iIndex <= length - 1) {
            this.points[iIndex].x += dXOffset;
            this.points[iIndex].y += dYOffset;
            this.points[iIndex].z += dZOffset;
        }
        if (iIndex === length - 1) {
            this.positionArr.forEach(position => {
                position.x += dXOffset;
                position.y += dYOffset;
                position.z += dZOffset;
            });
            this.reCountData();
        };
        if (iIndex > length - 1) {
            this.positionArr.forEach((position, index) => {
                if (iIndex - length === index) {
                    position.x += dXOffset;
                    position.y += dYOffset;
                    position.z += dZOffset;
                }
            });
        }
    };
    // Gets the pinch point of a custom object
    public getGripPoints(): McGePoint3dArray {
        let ret = new McGePoint3dArray()
        this.points.forEach(pt => {
            ret.append(pt)
        });
        this.positionArr.forEach(pt => {
            ret.append(pt);
        })
        return ret;
    };
    /**
     * Draw arrow style
     * A : arrow
     * HA : half arrow
     * P : point
     * C : corss
     */
    private drawArrow(): McDbEntity[] {
        const pt1 = this.points[0];
        const pt2 = this.points[1];
        if (this._arrowType === 'A' || this._arrowType === 'HA') {
            const vec = pt2.sub(pt1).normalize().mult(this.arrowLength);
            const pt = pt1.clone().addvec(vec);
            const _vec = vec.clone().rotateBy(Math.PI / 2).normalize().mult(this.arrowLength / 8);
            const pt3 = pt.clone().addvec(_vec);
            const pt4 = pt.clone().subvec(_vec);
            const solid = new McDbHatch();
            this._arrowType === 'A' ? solid.appendLoop(new McGePoint3dArray([pt1, pt3, pt4])) : solid.appendLoop(new McGePoint3dArray([pt1, pt3, pt]));
            return [solid]
        }else if(this._arrowType === 'P'){
            const solid = new McDbHatch();
            solid.appendCircleLoop(pt1.x,pt1.y,this.arrowLength/3);
            return [solid]
        }else if(this._arrowType === 'C'){
            const point1 = pt1.clone().addvec(McGeVector3d.kXAxis.normalize().mult(this.arrowLength/2));
            const point2 = pt1.clone().subvec(McGeVector3d.kXAxis.normalize().mult(this.arrowLength/2));
            const point3 = pt1.clone().addvec(McGeVector3d.kYAxis.normalize().mult(this.arrowLength/2));
            const point4 = pt1.clone().subvec(McGeVector3d.kYAxis.normalize().mult(this.arrowLength/2));
            const line1 = new McDbLine(point1,point2);
            const line2 = new McDbLine(point3, point4);
            return [line1,line2]
        }
    }
    /**
    * Drawing text
    * L: online end
    * M: In line
    * R: line end
    */
    private drawText(): McDbEntity[] {
        const textArr = [];
        const textUp = new McDbText();
        textUp.height = this.height;
        textUp.textString = this._textUp;
        textUp.position = textUp.alignmentPoint = this.positionArr[0];
        textUp.horizontalMode = McDb.TextHorzMode.kTextLeft;
        textUp.rotate(this.positionArr[0], this.angle);
        if (this._alginType === 'M' || this._alginType === 'R') {
            const textDown = new McDbMText()
            textDown.contents = this._textDown;
            textDown.location = this.positionArr[1];
            textDown.textHeight = this.height;
            textDown.attachment = McDb.AttachmentPoint.kTopCenter;
            textDown.rotate(this.positionArr[1], this.angle);
            if (this._alginType === 'M') {
                textUp.horizontalMode = McDb.TextHorzMode.kTextMid;
            }
            if (this._alginType === 'R') {
                textDown.attachment = McDb.AttachmentPoint.kTopLeft;
            }
            textArr.push(textDown);
        }
        textArr.push(textUp);
        return textArr
    }
    // Draw entity 
    public worldDraw(draw: MxCADWorldDraw): void {
        // Draw multiple lines
        const pl = new McDbPolyline();
        this.points.forEach((pt) => {
            pl.addVertexAt(pt);
        });
        draw.drawEntity(pl);

        // Draw an arrow
        if(this._arrowType !== 'N' && this.points.length > 1){
            const arrowArr = this.drawArrow();
            arrowArr.forEach( arrow =>{
                draw.drawEntity(arrow)
            })
        }
       
        if (this.points.length > 1) {
            // Draw subscript text
            const textArr = this.drawText();
            textArr.forEach(text => {
                draw.drawEntity(text)
            })
        }
    }
    // Recalculate the text position and rotation direction
    private reCountData() {
        const length = this.points.length;
        // Gets the direction and rotation Angle of the last line
        if (length > 1) {
            const pt1 = this.points[length - 2];
            const pt2 = this.points[length - 1];
            if (!this.arrowLength) {
                this.arrowLength = MxFun.viewCoordLong2Cad(20);
            };
            if (!this.height) {
                this.height = this.arrowLength*(2/3);
            };
            const vec = pt2.sub(pt1).normalize().mult(this.height / 2);
            const _vec = vec.clone().rotateBy(Math.PI / 2).normalize().mult(this.height / 2);
            this.angle = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
            if (Math.PI * (3 / 2) > this.angle && this.angle > Math.PI / 2) {
                this.angle += Math.PI;
                _vec.negate();
            }
            if (this._alginType === 'L') {
                // On the online end, only superscript text
                const position = pt2.clone().addvec(vec).subvec(_vec);
                this.positionArr[0] = position;
            } else if (this._alginType === 'M') {
                // In the middle of the line, the upper and lower scripts are centered
                const distance = pt1.distanceTo(pt2);
                const midPt = pt1.clone().addvec(vec.normalize().mult(distance / 2))
                this.positionArr[1] = midPt.clone().subvec(_vec);
                this.positionArr[0] = midPt.clone().addvec(_vec);
            } else if (this._alginType === 'R') {
                // Line end, upper and lower script at the end
                this.positionArr[1] = pt2.clone().addvec(vec).subvec(_vec);
                this.positionArr[0] = pt2.clone().addvec(vec).addvec(_vec);
            }
        }

    }
    // Add vertex
    public addVertex(pt: McGePoint3d) {
        this.assertWrite();
        this.points.push(pt);
        this.reCountData();
    }
    // Get vertex array
    public getPoints() {
        return this.points;
    }
}
  1. Register custom class information
ts
new McDbTestArrowCitation().rxInit();
  1. Write method, call McDbTestArrowCitation custom arrow reference class to implement arrow reference function
  • Set arrow style, upper and lower script text content and alignment

We can use MxCADUiPrString() according to the according to user input by the subscript text content, Or assign values directly by some other means. When you select an arrow style or alignment, We can use MxCADUiPrKeyWord() set up corresponding operation according to user's choice of key words.

ts
// Set arrow style
const getArrowStyle = new MxCADUiPrKeyWord()
getArrowStyle.setMessage("Please select the arrow style:")
getArrowStyle.setKeyWords("[Arrow(A)/Half arrow(HA)/Dot(P)/Cross(C)/None(N)]")
let arrowStyle = await getArrowStyle.go();
// Set alignment
const getAlignType = new MxCADUiPrKeyWord()
getAlignType.setMessage("Please select the text alignment above and below:")
getAlignType.setKeyWords("[Line start(L)/Line center(M)/Line end(R)]")
let alignType = await getAlignType.go();
/**
  * Set the subscript text
  * Only superscript text can be set on the online end
  */
const getStrUp = new MxCADUiPrString();
getStrUp.setMessage('Please set the superscript text content:');
let strUp = await getStrUp.go();
if (!strUp) strUp = "";
let strDown = "";
if(alignType === "M" || alignType === "R"){
    const getStrDown = new MxCADUiPrString();
    getStrDown.setMessage('Please set the subscript text content:');
    strDown = await getStrDown.go();
    if (!strDown) strDown = "";
}
  • Get the starting point of the arrow and the vertex of the lead

We can use take object MxCADUiPrPoint continuous take to get the arrow and lead the starting point of each vertex. According to the information obtained in the above steps, a new arrow reference object is constructed and dynamically drawn for user observation.

ts
const arrowCiatat = new McDbTestArrowCitation();
arrowCiatat.textUp = strUp;
arrowCiatat.textDown = strDown;
arrowCiatat.arrowType = arrowStyle;
arrowCiatat.alginType = alginType;
const getPoint = new MxCADUiPrPoint();
getPoint.setMessage('Specify arrow start:');
const point = await getPoint.go();
if (!point) return;
arrowCiatat.addVertex(point);
while (true) {
    const getPt = new MxCADUiPrPoint();
    getPt.setMessage('Specify the next point or end point and right-click to finish');
    getPt.setUserDraw((pt, pw) => {
        const _clone = arrowCiatat.clone() as McDbTestArrowCitation;
        _clone.addVertex(pt);
        pw.drawMcDbEntity(_clone)
    })
    const pt = await getPt.go();
    if (!pt) break;
    arrowCiatat.addVertex(pt);
}
const mxcad = MxCpp.getCurrentMxCAD();
mxcad.drawEntity(arrowCiatat);

Functional practice

Practical effects are as follows:

  • Click the arrow quote button to execute the arrow quote method
  • Follow the command line prompts to set the arrow style, subscript text content, and alignment of the arrow annotations
  • enter an option or content in the input box and click Enter to confirm
  • Click on the drawing to determine the starting point of the arrow and move the mouse to determine the vertex of the lead
  • Click the right mouse button to end the point, successfully draw the arrow guide
  • After the drawing is completed, the left mouse button can click the arrow guide object to adjust the position of the object through the pinch point