箭头引注
下面我们将介绍如何利用 mxcad 插件实现在CAD图纸中箭头引注的功能,该功能中用户点击画布确定箭头起点,再次连续点击画布确定箭头引线顶点及终点位置。在箭头引注功能中用户可自定义选择箭头形状,以及上标文字和下标文字内容,还可以根据绘制需求修改文字位置等。箭头引注功能能够帮助用户快速标注图纸内容,增加图纸内容的完整性和可读性。
功能实现
- 实现自定义箭头引注类
为了方便后期管理与修改标注,我们可以通过继承 McDbCustomEntity 自定义实体类来扩展实现自定义箭头引注类。然后,我们可以利用 McDbMText 或 McDbText 构造测量信息多文本对象,将标注信息绘制在页面中。
下面示例的箭头引注类中我们提供了箭头、点、十字、半箭头等箭头样式,以及上下标文字在线端、齐线中、齐线端等对齐方式,用户可参考下面的示例代码根据自身项目需求进行二次开发。
ts
// 自定义箭头引注类
class McDbTestArrowCitation extends McDbCustomEntity {
// 定义McDbTestConMeasurement内部的点对象
// 箭头线点数组
private points: McGePoint3d[] = [];
// 文字点位置集合
private positionArr: McGePoint3d[] = [];
// 文字高度
private height: number = 0;
// 上标文字内容
private _textUp: string = "";
// 下标文字内容
private _textDown: string = "";
// 箭头样式
private _arrowType: string = "";
// 对齐方式
private _alginType: string = "";
// 记录初始长度
private arrowLength: number = 0;
// 文字旋转角度
private angle: number = 0;
// 构造函数
constructor(imp?: any) {
super(imp);
}
// 创建函数
public create(imp: any) {
return new McDbTestArrowCitation(imp)
}
// 获取类名
public getTypeName(): string {
return "McDbTestArrowCitation";
}
//设置或获取文本字高
public set textHeight(val: number) {
this.height = val;
}
public get textHeight(): number {
return this.height;
}
//设置或获取上标文本
public set textUp(val: string) {
this._textUp = val;
}
public get textUp(): string {
return this._textUp;
}
//设置或获取下标文本
public set textDown(val: string) {
this._textDown = val;
}
public get textDown(): string {
return this._textDown;
}
//设置或获取箭头样式
public set arrowType(val: string) {
this._arrowType = val;
}
public get arrowType(): string {
return this._arrowType;
}
//设置或获取对齐样式
public set alginType(val: string) {
this._alginType = val;
}
public get alginType(): string {
return this._alginType;
}
// 读取自定义实体数据
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;
}
// 写入自定义实体数据
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;
}
// 移动自定义对象的夹点
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;
}
});
}
};
// 获取自定义对象的夹点
public getGripPoints(): McGePoint3dArray {
let ret = new McGePoint3dArray()
this.points.forEach(pt => {
ret.append(pt)
});
this.positionArr.forEach(pt => {
ret.append(pt);
})
return ret;
};
/**
* 画箭头样式
* A : 箭头
* HA : 半箭头
* P :点
* C :十字
*/
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]
}
}
/**
* 画文字
* L:在线端
* M:齐线中
* R:齐线端
*/
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
}
// 绘制实体
public worldDraw(draw: MxCADWorldDraw): void {
// 画多段线
const pl = new McDbPolyline();
this.points.forEach((pt) => {
pl.addVertexAt(pt);
});
draw.drawEntity(pl);
// 画箭头
if(this._arrowType !== 'N' && this.points.length > 1){
const arrowArr = this.drawArrow();
arrowArr.forEach( arrow =>{
draw.drawEntity(arrow)
})
}
if (this.points.length > 1) {
// 画上下标文字
const textArr = this.drawText();
textArr.forEach(text => {
draw.drawEntity(text)
})
}
}
// 重新计算文字位置和旋转方向
private reCountData() {
const length = this.points.length;
// 获取最后一段直线的方向与旋转角度
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') {
// 在线端,只有上标文字
const position = pt2.clone().addvec(vec).subvec(_vec);
this.positionArr[0] = position;
} else if (this._alginType === 'M') {
// 齐线中,上下标文字居中
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') {
// 齐线端,上下标文字在末尾
this.positionArr[1] = pt2.clone().addvec(vec).subvec(_vec);
this.positionArr[0] = pt2.clone().addvec(vec).addvec(_vec);
}
}
}
// 添加顶点
public addVertex(pt: McGePoint3d) {
this.assertWrite();
this.points.push(pt);
this.reCountData();
}
// 获取顶点数组
public getPoints() {
return this.points;
}
}
- 注册自定义类信息
ts
new McDbTestArrowCitation().rxInit();
- 编写方法,调用 McDbTestArrowCitation 自定义箭头引注类实现箭头引注功能
- 设置箭头样式,上下标文字内容及对齐方式
我们可以利用 MxCADUiPrString() 根据根据用户输入得到上下标文字内容,或者通过其他方式直接赋值。选择箭头样式或对齐方式时,我们可以通过 MxCADUiPrKeyWord() 根据用户选择的关键词来设置相应操作。
ts
// 设置箭头样式
const getArrowStyle = new MxCADUiPrKeyWord()
getArrowStyle.setMessage("请选着箭头样式:")
getArrowStyle.setKeyWords("[箭头(A)/半箭头(HA)/点(P)/十字(C)/无(N)]")
let arrowStyle = await getArrowStyle.go();
// 设置对齐方式
const getAlignType = new MxCADUiPrKeyWord()
getAlignType.setMessage("请选择上下标文字对齐方式:")
getAlignType.setKeyWords("[在线端(L)/齐线中(M)/齐线端(R)]")
let alignType = await getAlignType.go();
/**
* 设置上下标文字
* 在线端只能设置上标文字
*/
const getStrUp = new MxCADUiPrString();
getStrUp.setMessage('请设置上标文字内容:');
let strUp = await getStrUp.go();
if (!strUp) strUp = "上";
let strDown = "";
if(alignType === "M" || alignType === "R"){
const getStrDown = new MxCADUiPrString();
getStrDown.setMessage('请设置下标文字内容:');
strDown = await getStrDown.go();
if (!strDown) strDown = "下";
}
- 获取箭头起点,及引线顶点
我们可以利用取点对象 MxCADUiPrPoint 连续取点来获取箭头起点和引线的各个顶点。结合上述步骤中获取的箭头引注的信息,构造新的箭头引注对象,并动态绘制方便用户观察。
ts
const arrowCiatat = new McDbTestArrowCitation();
arrowCiatat.textUp = strUp;
arrowCiatat.textDown = strDown;
arrowCiatat.arrowType = arrowStyle;
arrowCiatat.alginType = alginType;
const getPoint = new MxCADUiPrPoint();
getPoint.setMessage('指定箭头起点:');
const point = await getPoint.go();
if (!point) return;
arrowCiatat.addVertex(point);
while (true) {
const getPt = new MxCADUiPrPoint();
getPt.setMessage('指定下一点或终点,右键完成');
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);
功能实践
实践效果如下:
- 点击箭头引注按钮,执行箭头引注方法
- 根据命令行提示设置箭头引注的箭头样式、上下标文字内容及对齐方式
- 在输入框中输入选项或内容,点击enter键确定
- 点击图纸确定箭头起点,移动鼠标确定引线顶点
- 点击鼠标右键结束取点,成功绘制箭头引注
- 完成绘制后,鼠标左键点击箭头引注对象可通过夹点调整对象位置