Eliciting annotation
Below we will introduce how to use the mxcad plug-in to achieve the function of leading out the annotation in the CAD drawing, the function of the user clicks the canvas to determine the starting point of the content, click the canvas again to determine the lead position and text annotation bit, one of the annotations can have multiple leading arrows. In the extraction annotation 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 extraction annotation function can help users quickly annotate the contents of drawings, and increase the completeness and readability of the contents of drawings.
Function implementation
- Implement custom eliciting annotation classes
In order to facilitate later management and annotation modification, We can through inheritance McDbCustomEntity Custom entity classes to extend the implementation of custom extraction annotation classes. 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 annotation class, which can fix the lead Angle, and align the beginning, middle and end of the upper and lower script text. Users can refer to the following example code for secondary development according to their own project needs.
// Elicited tag class
class McDbTestExportAnnotation extends McDbCustomEntity {
// Define point objects inside McDbTestExportAnnotation
// Mark point set
private points: McGePoint3d[] = [];
// Lead position
private exportLinePos: McGePoint3d = new McGePoint3d();
// Text base position
private textBasePos: McGePoint3d = new 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 = "";
// 对齐方式
private _alginType: string = "";
// Initial record length
private arrowLength: number = 0;
// Fixed Angle
private _alignAngle: number = 0;
// constructor
constructor(imp?: any) {
super(imp);
}
// Create function
public create(imp: any) {
return new McDbTestExportAnnotation(imp)
}
// Get class name
public getTypeName(): string {
return "McDbTestExportAnnotation";
}
//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;
}
//Set or get a fixed Angle
public set alignAngle(val: number) {
this._alignAngle = val;
}
public get alignAngle(): number {
return this._alignAngle;
}
// Read from defined entity data
public dwgInFields(filter: IMcDbDwgFiler): boolean {
this.points = filter.readPoints("points").val;
this.positionArr = filter.readPoints("positionArr").val;
this.exportLinePos = filter.readPoint("exportLinePos").val;
this.textBasePos = filter.readPoint("textBasePos").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._alignAngle = filter.readLong("alignAngle").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.writePoint("textBasePos", this.textBasePos);
filter.writePoint("exportLinePos", this.exportLinePos);
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.writeLong("alignAngle", this._alignAngle);
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) {
this.points[iIndex].x += dXOffset;
this.points[iIndex].y += dYOffset;
this.points[iIndex].z += dZOffset;
}
if (iIndex === length) {
this.exportLinePos.x += dXOffset;
this.exportLinePos.y += dYOffset;
this.exportLinePos.z += dZOffset;
this.textBasePos.x += dXOffset;
this.textBasePos.y += dYOffset;
this.textBasePos.z += dZOffset;
};
if (iIndex > length) {
this.textBasePos.x += dXOffset;
}
};
// Gets the pinch point of a custom object
public getGripPoints(): McGePoint3dArray {
let ret = new McGePoint3dArray()
this.points.forEach(pt => {
ret.append(pt)
});
ret.append(this.exportLinePos);
ret.append(this.textBasePos);
return ret;
};
// Draw an arrow
private drawArrow(point: McGePoint3d): McDbEntity[] {
const pt1 = point;
const pt2 = this.exportLinePos;
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]
}
}
// Draw words
private drawText(): McDbEntity[] {
if (!this.height) {
this.height = this.arrowLength * (2 / 3);
};
const textArr = [];
const textUp = new McDbText();
textUp.height = this.height;
textUp.textString = this._textUp;
textUp.horizontalMode = McDb.TextHorzMode.kTextLeft;
const textDown = new McDbMText()
textDown.contents = this._textDown;
textDown.textHeight = this.height;
textDown.attachment = McDb.AttachmentPoint.kTopLeft;
let pt1, pt2;
if (this.exportLinePos.x < this.textBasePos.x) {
pt1 = this.exportLinePos.clone();
pt2 = this.textBasePos.clone();
} else {
pt2 = this.exportLinePos.clone();
pt1 = this.textBasePos.clone();
}
const vec = pt2.sub(pt1).normalize().mult(this.height / 2);
const _vec = vec.clone().rotateBy(Math.PI / 2).normalize().mult(this.height / 2);
if (this._alginType === 'S') {
// Beginning end
this.positionArr[1] = pt1.clone().addvec(vec).subvec(_vec);
this.positionArr[0] = pt1.clone().addvec(vec).addvec(_vec);
} else if (this._alginType === 'M') {
// 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 === 'E') {
// The upper and lower scripts are at the end
this.positionArr[1] = pt2.clone().subvec(_vec);
this.positionArr[0] = pt2.clone().addvec(_vec);
textUp.horizontalMode = McDb.TextHorzMode.kTextRight;
textDown.attachment = McDb.AttachmentPoint.kTopRight;
}
textUp.position = textUp.alignmentPoint = this.positionArr[0];
textDown.location = this.positionArr[1];
textArr.push(textUp);
textArr.push(textDown);
return textArr
}
// Take point after calculating fixed Angle
private getFixedAnglePt(fixedPt: McGePoint3d, pt: McGePoint3d): McGePoint3d {
const vec = pt.sub(fixedPt);
const vecAngle = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
const num = 360 / this._alignAngle;
let angleSub = null;
let angle = 0;
for (let i = 0; i < num; i++) {
const a = Math.abs(this._alignAngle * i - vecAngle * 180 / Math.PI);
if (!angleSub) {
angleSub = a;
angle = this._alignAngle * i;
} else if (angleSub > a) {
angleSub = a;
angle = this._alignAngle * i;
}
}
const _pt = fixedPt.clone().addvec(McGeVector3d.kXAxis.normalize().mult(100).rotateBy(angle * Math.PI / 180))
const _line = new McDbLine(fixedPt, _pt);
const newPoint = _line.getClosestPointTo(pt, true).val;
return newPoint;
}
// Draw entity
public worldDraw(draw: MxCADWorldDraw): void {
if (!this.arrowLength) {
this.arrowLength = MxFun.viewCoordLong2Cad(20);
}
// Calculate exportLinePos
if (this._alignAngle && this.points.length === 1) {
this.exportLinePos = this.getFixedAnglePt(this.points[0], this.exportLinePos);
}
// Draw an arrow
this.points.forEach(pt => {
if (this._arrowType !== 'N') {
const arrowArr = this.drawArrow(pt);
arrowArr.forEach(arrow => {
draw.drawEntity(arrow)
})
};
// Draw arrow path
const line = new McDbLine(this.exportLinePos, pt);
draw.drawEntity(line);
});
this.textBasePos = new McGePoint3d(this.textBasePos.x, this.exportLinePos.y);
const line = new McDbLine(this.exportLinePos, this.textBasePos);
draw.drawEntity(line);
// Draw words
this.drawText().forEach(text => {
draw.drawEntity(text);
})
}
// Add vertex
public addVertex(pt: McGePoint3d) {
this.assertWrite();
this.points.push(pt);
if (this.points.length > 1 && this.exportLinePos && this.alignAngle) {
const point = this.getFixedAnglePt(this.exportLinePos, pt);
this.points[this.points.length - 1] = point;
}
}
// Get vertex array
public getPoints() {
return this.points;
}
// Set the lead position
public setExportLinePos(pt: McGePoint3d) {
this.exportLinePos = pt.clone()
}
// Get the lead position
public getExportLinePos() {
return this.exportLinePos;
}
// Set the text baseline position
public setTextBasePos(pt: McGePoint3d) {
this.textBasePos = new McGePoint3d(pt.x, this.exportLinePos.y);
}
// Gets the text baseline position
public getTextBasePos() {
return this.textBasePos;
}
}
- Register custom class information
new McDbTestExportAnnotation().rxInit();
- Write methods and call McDbTestExportAnnotation to customize the eliciting annotation class to achieve the eliciting annotation function
- Set arrow style, subscript 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.
// 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("[Beginning(S)/Middle(M)/End(E)]")
let alignType = await getAlignType.go();
/**
* Set the subscript text
*/
const getStrUp = new MxCADUiPrString();
getStrUp.setMessage('Please set the superscript text content:');
let strUp = await getStrUp.go();
if (!strUp) strUp = "上";
const getStrDown = new MxCADUiPrString();
getStrDown.setMessage('Please set the subscript text content:');
let strDown = await getStrDown.go();
if (!strDown) strDown = "下";
// Set a fixed Angle
const getAlignAngle = new MxCADUiPrKeyWord()
getAlignAngle.setMessage("Please select a fixed Angle:")
getAlignAngle.setKeyWords("[0°(0)/30°(30)/45°(45)/60°(60)/90°(90)/]")
let alignAngle = await getAlignAngle.go();
- Gets lead position, tag position, and text position
We can use take object MxCADUiPrPoint Take points consecutively to get the lead endpoint, the label position, and the text position. Combined with the information obtained in the above steps, a new extraction annotation object is constructed and dynamically drawn for user observation.
const exportAnn = new McDbTestExportAnnotation();
exportAnn.textUp = strUp;
exportAnn.textDown = strDown;
exportAnn.arrowType = arrowStyle;
exportAnn.alginType = alignType;
exportAnn.alignAngle = Number(alignAngle);
const getPoint = new MxCADUiPrPoint();
getPoint.setMessage('Please specify the first point of the annotation:');
const point = await getPoint.go();
if (!point) return;
exportAnn.addVertex(point);
const getEtpoint = new MxCADUiPrPoint();
getEtpoint.setMessage('Please specify the lead position:');
getEtpoint.setUserDraw((pt, pw) => {
const _clone = exportAnn.clone() as McDbTestExportAnnotation;
_clone.setExportLinePos(pt);
_clone.setTextBasePos(new McGePoint3d(pt.x + MxFun.viewCoordLong2Cad(50), pt.y))
pw.drawMcDbEntity(_clone)
})
const etPoint = await getEtpoint.go();
if (!etPoint) return;
exportAnn.setExportLinePos(etPoint);
const getTpoint = new MxCADUiPrPoint();
getTpoint.setMessage('Please specify the location of the text baseline:');
getTpoint.setUserDraw((pt, pw) => {
const _clone = exportAnn.clone() as McDbTestExportAnnotation;
_clone.setTextBasePos(pt);
pw.drawMcDbEntity(_clone)
});
const tPoint = await getTpoint.go();
if (!tPoint) return;
exportAnn.setTextBasePos(tPoint);
while (true) {
const getPt = new MxCADUiPrPoint();
getPt.setMessage('Specify other points, right-click to complete:');
getPt.setUserDraw((pt, pw) => {
const _clone = exportAnn.clone() as McDbTestExportAnnotation;
_clone.addVertex(pt);
pw.drawMcDbEntity(_clone)
})
const pt = await getPt.go();
if (!pt) break;
exportAnn.addVertex(pt);
}
const mxcad = MxCpp.getCurrentMxCAD();
mxcad.drawEntity(exportAnn);
Functional practice
Practical effects are as follows:
- Click the Lead annotation button to execute the lead annotation method
- Follow the command line prompts to set arrow style, upper and lower text content, and alignment
- enter an option or content in the input box and click Enter to confirm
- Click the left mouse button to determine the end point of the first lead
- Move the mouse and click the left mouse button to determine the lead position
- Move the mouse and click the left mouse button to determine the location of the text annotation
- Click the left mouse button to continuously draw the label lead
- Click the right mouse button to end the fetching point and successfully draw out the annotation