前言
CAD图形识别功能可帮助用户快速识别和提取CAD图纸中的各种图形,从而加速设计过程。可应用在识别与分类阶段,自动识别图纸中的不同元素,通过特定特征进行区分,减少了手动分类的工作量;也可应用在数量统计阶段,统计图纸中各种构件的数量用于预算;还可运用在图纸定位和应用阶段,快速定位图纸上的特定元素,便于快速查找和修改。
mxcad 为用户提供了图形识别功能和API,用户可根据自身需求对该功能进行拓展或二次开发,更多开发文档关注公众号:梦想云图网页CAD。
图形识别步骤
打开mxcad在线示例demo:demo.mxdraw3d.com:3000/mxcad/。
点击【工具(A)】菜单栏,找到图形识别功能,选择【图形识别】按钮,如下图:
3.根据命令行提示,点击鼠标左键点选图元或拉框选择需要识别的图形(图形识别功能目前支持识别直线类、多段线类、弧线类、圆类,以及块这五类图元),如下图:
4.点击右键结束选择后,在弹出的图形识别框内设置查找图形详情,设置图形名称可方便后续查找已识别图形;设置重新选择图形时,会覆盖当前选中图形;设置区域查找时,需要用户框选查找范围,若未框选范围则会默认为全图纸查找图形,如下图:
在图形识别框内点击【开始识别】按钮,开始在目标范围内查找图形,并将查找结果展示在图形识别列表中。点击图形识别列表下的坐标对应的【查看】按钮会自动定位到对应的图形位置,并圈选出目标图形,如下图:
当识别多个图形后,可点击目标图形统计表格对应的操作列中的【详情】按钮查看识别详情,图形坐标列表将转换为该目标图形的识别结果,如下图:
识别注意事项:
如果需要识别的图形较为复杂,为保证识别速度与精度,我们需尽可能查找图形中的特殊部分而不是选取整个图形(图形对象过多时可能会导致卡顿,影响用户使用效率)。
图形识别支持区域选择,用户可根据自身需求精确定位图形筛选区域。
功能开发
mxcad 图形识别功能中运用的核心思想是通过[McDbEntity]实体中每个实体的特征在图纸中进行查找,查找的实体中包括了[直线类]、[多段线类] 、[弧线类]、[圆类],以及[块]。
下列为基于mxcad 封装的图形识别类 SearchSamePattern 用户可根据该类的使用方法利用 mxcad 进行二次开发,代码如下:
登录后复制
import{
DxfCode, McCmColor, McDbArc, McDbBlockReference, McDbCircle, McDbLine, McDbPolyline, McDbWipeout, McGePoint3d,
McGePoint3dArray, McGeVector3d, MxCADResbuf, MxCADSelectionSet, MxCADUtility, MxCpp
}from"mxcad";import{ MxFun }from"mxdraw";exportclassSearchSamePattern{/** 识别图元数 */privateobjectCount: number =0;/** 选择图形集合 Map<className,[Ent]>*/privateselectObjects: any;private dTol =0.000001;/**
* pt1:左下角
* pt2:右上角
* pt3:左上角
* pt4:右下角
*/privatept1: McGePoint3d;privatept2: McGePoint3d;privatept3: McGePoint3d;privatept4: McGePoint3d;//包围盒大小privatepl: McDbPolyline;/** 识别图形类名集合 [string] */privateclassNames: any
/** 识别图形图片地址 */privatepatternImgUrl: String
/** 识别对象详情 Map<ClassName,[{}]>*/privateobjectsDetails: any ={};// 下列为识别结果数据/** 识别后的图形位置数组集合 */;privategraphBoxsPos: any[]=[];privateformPt: McGePoint3d;privatetoPt: McGePoint3d;/** 点精度值 */privatedTolPt: any;/** 识别后图形列表 */privatepatternList: any ={};/** 选择的图形集合 */publicsetSelectset(ss: MxCADSelectionSet): Promise<String>{if(!ss?.count())return;const mxcad = MxCpp.getCurrentMxCAD();// 获取当前图纸的空间块表记录包围盒const{ maxPt, minPt }= mxcad.getDatabase().currentSpace.getBoundingBox();const currentDrawOrder = mxcad.getDatabase().currentSpace.getMinMaxDrawOrder()const n = MxFun.screenCoordLong2Doc(Math.abs(maxPt.x - minPt.x));
minPt.x -= n; minPt.y -= n; maxPt.x += n; maxPt.y += n;let points =newMcGePoint3dArray();
points.append(minPt);
points.append(newMcGePoint3d(minPt.x, maxPt.y,0));
points.append(newMcGePoint3d(maxPt.x, maxPt.y,0));
points.append(newMcGePoint3d(maxPt.x, minPt.y,0));this.objectCount = ss.count();// 获取遮罩层drawOrderlet wipeout =newMcDbWipeout
wipeout.setVertices(points);const wipeoutId = mxcad.drawEntity(wipeout);const e = wipeoutId.getMcDbEntity();
e.drawOrder = currentDrawOrder.maxDrawOrder +1;this.selectObjects ={};const oldDrawOder =[];
ss.forEach(id=>{const ent = id.getMcDbEntity();/** 记录识别图形的信息 */if(!this.selectObjects[ent.objectName])this.selectObjects[ent.objectName]=[];this.selectObjects[ent.objectName].push(ent);if(ent.objectName ==='McDbLine'){if(!this.objectsDetails['McDbLine'])this.objectsDetails['McDbLine']=[];this.objectsDetails['McDbLine'].push({entity: ent,length:(ent as McDbLine).getLength().val });}elseif(ent.objectName ==='McDbArc'){if(!this.objectsDetails['McDbArc'])this.objectsDetails['McDbArc']=[];const length =(ent as McDbArc).getLength().val;const{ bugle }=this.getBugle((ent as McDbArc), length)this.objectsDetails['McDbArc'].push({entity: ent, length, bugle });}elseif(ent.objectName ==='McDbCircle'){if(!this.objectsDetails['McDbCircle'])this.objectsDetails['McDbCircle']=[];const radius =(ent as McDbCircle).radius;this.objectsDetails['McDbCircle'].push({entity: ent, radius });}elseif(ent.objectName ==='McDbBlockReference'){if(!this.objectsDetails['McDbBlockReference'])this.objectsDetails['McDbBlockReference']=[];const blkName =(ent as McDbBlockReference).blockName;this.objectsDetails['McDbBlockReference'].push({entity: ent, blkName });}elseif(ent.objectName ==='McDbPolyline'){if(!this.objectsDetails['McDbPolyline'])this.objectsDetails['McDbPolyline']=[];const length =(ent as McDbPolyline).getLength().val;this.objectsDetails['McDbPolyline'].push({entity: ent, length });}/** 渲染顺序 */
oldDrawOder.push({obj: ent,drawOrder: ent.drawOrder });
ent.drawOrder = e.drawOrder +1;const{ maxPt, minPt }= ent.getBoundingBox();if(!this.pt1 ||!this.pt2){this.pt1 = minPt;this.pt2 = maxPt;}else{if(minPt.x <this.pt1.x)this.pt1.x = minPt.x;if(minPt.y <this.pt1.y)this.pt1.y = minPt.y;if(maxPt.x >this.pt2.x)this.pt2.x = maxPt.x;if(maxPt.y >this.pt2.y)this.pt2.y = maxPt.y;}});this.classNames = Object.keys(this.selectObjects);/**
* 计算包围盒其他两个角点
*/this.pt3 =newMcGePoint3d(this.pt1.x,this.pt2.y);this.pt4 =newMcGePoint3d(this.pt2.x,this.pt1.y);/** 绘制识别图形包围盒 */this.pl =newMcDbPolyline()this.pl.addVertexAt(this.pt1);this.pl.addVertexAt(this.pt3);this.pl.addVertexAt(this.pt2);this.pl.addVertexAt(this.pt4);this.pl.isClosed =true;const num = MxFun.screenCoordLong2Doc(10);this.pt1.x -= num;this.pt1.y -= num;this.pt2.x += num;this.pt2.y += num;// 生成预览图let w = Math.abs(this.pt1.x -this.pt2.x);let h = Math.abs(this.pt1.y -this.pt2.y);if(w <1|| h <1)return;let jpg_width =300;let jpg_height =240;returnnewPromise<String>((resolve, reject)=>{
MxFun.getCurrentDraw().createCanvasImageData((imageData: String)=>{this.patternImgUrl = imageData;// 恢复绘制顺序
oldDrawOder.forEach(item=>{
item.obj.drawOrder = item.drawOrder;})
wipeoutId.erase();resolve(imageData)},{width: jpg_width,// 图片宽height: jpg_height,// 图片高range_pt1:this.pt1.toVector3(),// 截图范围角点1range_pt2:this.pt2.toVector3(),// 截图范围角点2});});};// 开始识别图形/**
* 1.查找初始包围盒
* 2.筛选符合条件的包围盒
* 3.比较包围盒内的每一个图元与需要识别的图元是否匹配
*/publicidentifyGraphics(corner1?: McGePoint3d, corner2?: McGePoint3d): any {/** 记录图形缩略图 */this.patternList.patternImgUrl =this.patternImgUrl;/**初始化图形位置列表 */this.graphBoxsPos.length =0;const mxcad = MxCpp.getCurrentMxCAD();this.dTolPt = mxcad.mxdraw.viewCoordLong2Cad(0.5);// 设置精度值if(!this.classNames)return;const array =this.classNames.map(name=>this.selectObjects[name].length);const minIndex = array.findIndex(element=> element === Math.min(...array));const str =this.classNames[minIndex];/** 识别块 */if(str ==='McDbBlockReference'){const refBlk =this.selectObjects['McDbBlockReference'][0].clone()as McDbBlockReference;const blkName = refBlk.blockName;// 图块名const{ position }=this.getBlkMidPt(refBlk);this.formPt = position;const rotate = refBlk.rotation;// 设置图块过滤器const filter =newMxCADResbuf([DxfCode.kEntityType,"INSERT"])const ss =newMxCADSelectionSet()if(corner1 && corner2){// 局部选择
ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);}else{// 全局选择
ss.allSelect(filter);};// 筛选出符合条件的块constboxs: any[]=[];
ss.forEach(id=>{const _blk = id.getMcDbEntity().clone()as McDbBlockReference;/** 块名相等=》计算旋转角度=》计算包围盒大小 */if(blkName === _blk.blockName){const _rotate = _blk.rotation;const angle = rotate >=0? _rotate - rotate : rotate - _rotate;const{position: _position, minPt, maxPt }=this.getBlkMidPt(_blk);if(this.classNames.length ===1){this.toPt = _position;this.identificationBBox(minPt, maxPt,0)}else{// 平移旋转初始包围盒。计算出最新包围盒的大小const _pl =this.pl.clone()as McDbPolyline;
_pl.move(this.formPt, _position);
_pl.rotate(_position, angle);let x =[], y =[];for(let i =0; i < _pl.numVerts(); i++){const pt = _pl.getPointAt(i).val;
x.push(pt.x);
y.push(pt.y);}const _minPt =newMcGePoint3d(Math.min(...x)-1, Math.min(...y)-1);const _maxPt =newMcGePoint3d(Math.max(...x)+1, Math.max(...y)+1);const res = boxs.filter(item=>{let ret1 = _minPt.distanceTo(item.pt1)<this.dTolPt && _maxPt.distanceTo(item.pt2)<this.dTolPt
let ret2 = _minPt.distanceTo(item.pt2)<this.dTolPt && _maxPt.distanceTo(item.pt1)<this.dTolPt
return ret1 || ret2
})if(!res.length){
boxs.push({pt1: _minPt,pt2: _maxPt });this.toPt = _position;// MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);this.identificationBBox(_minPt, _maxPt, angle);}}}});}/** 识别弧线 */elseif(str ==='McDbArc'){// 识别直线const refArc =this.selectObjects['McDbArc'][0].clone()as McDbArc;const length = refArc.getLength().val;// 弧线线长度// 圆弧凸度const{ bugle, midPt }=this.getBugle(refArc, length);const center = refArc.center;const vec = center.sub(midPt);// 设置弧线过滤器const filter =newMxCADResbuf([DxfCode.kEntityType,"ARC"])const ss =newMxCADSelectionSet()if(corner1 && corner2){// 局部选择
ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);}else{// 全局选择
ss.allSelect(filter);};// 筛选出符合条件的弧线constboxs: any[]=[];
ss.forEach(id=>{const _arc = id.getMcDbEntity().clone()as McDbArc;const _length = _arc.getLength().val;const{bugle: _bugle,midPt: _midPt }=this.getBugle(_arc, _length);/** 筛选出圆弧凸度与长度相同的弧线*/if(Math.abs(_length - length)<this.dTol && Math.abs(_bugle - bugle)<this.dTol){/** 分两种情况
* 1.直接旋转平移得到
* 2.镜像得到=》移动到目标位置+旋转角度+镜像翻转=》比较起始点位置
*/// 平移+旋转const _center = _arc.center;const _vec = _center.sub(_midPt);const angle = _vec.angleTo2(vec, McGeVector3d.kNegateZAxis);// 旋转平移初始包围盒。计算出最新包围盒的大小const _pl =this.pl.clone()as McDbPolyline;
_pl.move(midPt, _midPt);
_pl.rotate(_midPt, angle);let x =[], y =[];for(let i =0; i < _pl.numVerts(); i++){const pt = _pl.getPointAt(i).val;
x.push(pt.x);
y.push(pt.y);}const _minPt =newMcGePoint3d(Math.min(...x)-1, Math.min(...y)-1);const _maxPt =newMcGePoint3d(Math.max(...x)+1, Math.max(...y)+1);const res = boxs.filter(item=>{let ret1 = _minPt.distanceTo(item.pt1)<this.dTolPt && _maxPt.distanceTo(item.pt2)<this.dTolPt
let ret2 = _minPt.distanceTo(item.pt2)<this.dTolPt && _maxPt.distanceTo(item.pt1)<this.dTolPt
let ret3 = _midPt.distanceTo(item.midPt)<this.dTolPt
return(ret1 || ret2)&& ret3
})if(!res.length){
boxs.push({pt1: _minPt,pt2: _maxPt,midPt: _midPt });this.toPt = _midPt;this.formPt = midPt;this.identificationBBox(_minPt, _maxPt, angle);}}});}/** 识别直线 */elseif(str ==='McDbLine'){// 识别直线const refLine =this.selectObjects['McDbLine'][0].clone()as McDbLine;const length = refLine.getLength().val;// 直线长度const vec = refLine.endPoint.sub(refLine.startPoint);// 直线角度// 设置直线过滤器const filter =newMxCADResbuf([DxfCode.kEntityType,"LINE"])const ss =newMxCADSelectionSet()if(corner1 && corner2){// 局部选择
ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);}else{// 全局选择
ss.allSelect(filter);};// 筛选出符合条件的直线constlineArr: McDbLine[]=[];
ss.forEach(id=>{const line = id.getMcDbEntity().clone()as McDbLine;const _length = line.getLength().val;/**
* 1.未旋转
* 2.旋转过
*/if(Math.abs(_length - length)<this.dTol){
lineArr.push(line);}});// 筛选出符合条件的直线后,根据包围盒范围再次筛选出符合条件的直线组合constboxs: any[]=[];if(lineArr.length >0){
lineArr.forEach((item)=>{/**
* 将要识别的图形进行平移+旋转到目标位置
*/const ptArr =[item.startPoint, item.endPoint];
ptArr.forEach((point, index)=>{const _line = refLine.clone()as McDbLine;
_line.move(refLine.startPoint, point);const _vec = ptArr[1- index].sub(point);const angle = _vec.angleTo2(vec, McGeVector3d.kNegateZAxis);
_line.rotate(point, angle)const pt = _line.endPoint;const _pl =this.pl.clone()as McDbPolyline;
_pl.move(refLine.startPoint, point);
_pl.rotate(point, angle);const _refBoxPt1 = _pl.getPointAt(0).val;const _refBoxPt2 = _pl.getPointAt(1).val;const refVec1 = _refBoxPt1.sub(_line.startPoint);const refVec2 = _refBoxPt2.sub(_line.startPoint);if(pt.distanceTo(ptArr[1- index])<this.dTolPt && refVec1.isEqualTo(_refBoxPt1.sub(point))&& refVec2.isEqualTo(_refBoxPt2.sub(point))){let x =[], y =[];for(let i =0; i < _pl.numVerts(); i++){const pt = _pl.getPointAt(i).val;
x.push(pt.x);
y.push(pt.y);}const _minPt =newMcGePoint3d(Math.min(...x)-1, Math.min(...y)-1);const _maxPt =newMcGePoint3d(Math.max(...x)+1, Math.max(...y)+1);const res = boxs.filter(item=>{let r1 = _minPt.distanceTo(item.pt1)<this.dTolPt && _maxPt.distanceTo(item.pt2)<this.dTolPt
let r2 = _minPt.distanceTo(item.pt2)<this.dTolPt && _maxPt.distanceTo(item.pt1)<this.dTolPt
return r1 || r2
})if(!res.length){
boxs.push({pt1: _minPt,pt2: _maxPt });this.toPt = point;this.formPt = refLine.startPoint;// MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);this.identificationBBox(_minPt, _maxPt, angle);}}})})};}/** 识别圆 */elseif(str ==='McDbCircle'){const refCricle =this.selectObjects['McDbCircle'][0].clone()as McDbCircle;const radius = refCricle.radius;// 圆半径// 设置圆过滤器const filter =newMxCADResbuf([DxfCode.kEntityType,"CIRCLE"])const ss =newMxCADSelectionSet()if(corner1 && corner2){// 局部选择
ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);}else{// 全局选择
ss.allSelect(filter);};// 筛选出符合条件的圆
ss.forEach(id=>{const _circle = id.getMcDbEntity().clone()as McDbCircle;const _radius = _circle.radius;/** 筛选出圆半径相同的圆*/if(Math.abs(_radius - radius)<this.dTol){const _center = _circle.center;this.toPt = _center;this.formPt = refCricle.center;// 平移初始包围盒。计算出最新包围盒的大小const _pl =this.pl.clone()as McDbPolyline;
_pl.move(this.formPt,this.toPt);let x =[], y =[];for(let i =0; i < _pl.numVerts(); i++){const pt = _pl.getPointAt(i).val;
x.push(pt.x);
y.push(pt.y);}const _minPt =newMcGePoint3d(Math.min(...x)-1, Math.min(...y)-1);const _maxPt =newMcGePoint3d(Math.max(...x)+1, Math.max(...y)+1);// MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);this.identificationBBox(_minPt, _maxPt,0);}});}/** 识别多段线 */elseif(str ==='McDbPolyline'){/** 长度+顶点数+顶点对应的凸度相等=》旋转+平移后的顶点相等+有凸度线段的中点相等*/const refPoly =this.selectObjects['McDbPolyline'][0].clone()as McDbPolyline;const length = refPoly.getLength().val;const{ lengthArr, bugleArr, vertNum, vec, point }=this.getPlDetails(refPoly);// 设置多段线过滤器const filter =newMxCADResbuf([DxfCode.kEntityType,"LWPOLYLINE"])const ss =newMxCADSelectionSet()if(corner1 && corner2){// 局部选择
ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);}else{// 全局选择
ss.allSelect(filter);};// 筛选出符合条件的多段线constboxs: any[]=[];
ss.forEach(id=>{const _poly = id.getMcDbEntity().clone()as McDbPolyline;const _length = _poly.getLength().val;/** 筛选 长度+顶点数+顶点对应的凸度 相同的多段线*/if(Math.abs(_length - length)<this.dTol && _poly.isClosed === refPoly.isClosed){const{lengthArr: _lengthArr,bugleArr: _bugleArr,vertNum: _vertNum,vec: _vec,point: _point }=this.getPlDetails(_poly);const revBugleArr = _bugleArr.map(item=> item ===0? item :-item);// 全等const r1 = lengthArr.toString()=== _lengthArr.toString()&& bugleArr.toString()=== _bugleArr.toString();let r2 =false;if(!r1){// 反向
r2 = lengthArr.toString()===[..._lengthArr].reverse().toString()&& bugleArr.toString()===[...revBugleArr].reverse().toString();}const r3 = vertNum === _vertNum;// 镜像const r4 = lengthArr.toString()=== _lengthArr.toString()&& bugleArr.toString()===[...revBugleArr].reverse().toString();if((r1 || r2 || r4)&& r3){const poly = refPoly.clone()as McDbPolyline;const angle1 = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);// 如果是反向则方向取反const angle2 =(r2 ? _vec.negate(): _vec).angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);const angle = angle2 - angle1;
poly.move(point, _point);
poly.rotate(_point, angle);
poly.trueColor =newMcCmColor(255,0,0);let num =0;for(let i =0; i < vertNum; i++){const pt = poly.getPointAt(i).val;const width = poly.getWidthsAt(i);const _pt = r2 ? _poly.getPointAt(vertNum -1- i).val : _poly.getPointAt(i).val;const _width = r2 ? _poly.getWidthsAt(vertNum -1- i): _poly.getWidthsAt(i);if(pt.distanceTo(_pt)<this.dTolPt && width.val1 === _width.val1 && width.val2 === _width.val2){
num +=1}else{// 两个点且反向的情况if(vertNum ===2&& i ===0){const _pt1 = _poly.getPointAt(0).val;const _pt2 = _poly.getPointAt(1).val;const pt2 = poly.getPointAt(1).val;const _width = _poly.getWidthsAt(0);if(pt2.distanceTo(_pt2)<this.dTolPt && pt.distanceTo(_pt1)<this.dTolPt && width.val1 === _width.val2 && width.val2 === _width.val1){
num +=2;}}}};if(num === vertNum){// 旋转平移初始包围盒。计算出最新包围盒的大小const _pl =this.pl.clone()as McDbPolyline;
_pl.move(point, _point);
_pl.rotate(_point, angle);let x =[], y =[];for(let i =0; i < _pl.numVerts(); i++){const pt = _pl.getPointAt(i).val;
x.push(pt.x);
y.push(pt.y);}const _minPt =newMcGePoint3d(Math.min(...x)-1, Math.min(...y)-1);const _maxPt =newMcGePoint3d(Math.max(...x)+1, Math.max(...y)+1);const res = boxs.filter(item=>{let ret1 = _minPt.distanceTo(item.pt1)<this.dTolPt && _maxPt.distanceTo(item.pt2)<this.dTolPt
let ret2 = _minPt.distanceTo(item.pt2)<this.dTolPt && _maxPt.distanceTo(item.pt1)<this.dTolPt
return ret1 || ret2
})if(!res.length){
boxs.push({pt1: _minPt,pt2: _maxPt });this.toPt = _point;this.formPt = point;// MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);this.identificationBBox(_minPt, _maxPt, angle);}}}}});}/** 返回识别列表 */returnthis.patternList;}// 计算块中心点privategetBlkMidPt=(blkRef: McDbBlockReference)=>{const{ minPt, maxPt }= blkRef.getBoundingBox();const pt = minPt.clone().addvec(maxPt.sub(minPt).normalize().mult(maxPt.distanceTo(minPt)/2))return{position: pt, minPt, maxPt }}// 计算凸度privategetBugle=(arc: McDbArc,length: number)=>{const midPt = arc.getPointAtDist(length /2).val;const bugle = MxCADUtility.calcBulge(arc.getPointAtDist(0).val, midPt, arc.getPointAtDist(length).val).val;return{ bugle, midPt }}// 计算多段线的每一段线的长度和凸度privategetPlDetails=(pl: McDbPolyline)=>{const vertNum = pl.numVerts();constlengthArr: number[]=[];constbugleArr: number[]=[];letpoint1: McGePoint3d,point2: McGePoint3d;for(let i =0; i < vertNum; i++){if(i < vertNum -1){const p1 = pl.getPointAt(i).val;const p2 = pl.getPointAt(i +1).val;const length = pl.getDistAtPoint(p2).val - pl.getDistAtPoint(p1).val;const bugle = pl.getBulgeAt(i);
lengthArr.push(Number(length.toFixed(6)));
bugleArr.push(Number(bugle.toFixed(6)));if(i ===0){
point1 = p1;}if(i === vertNum -2){
point2 = p2;}}};const vec = point2.sub(point1)// 多段线角度const point = point1.clone().addvec(point2.sub(point1).normalize().mult(point2.distanceTo(point1)/2))return{ lengthArr, bugleArr, vertNum, vec, point }}// 将找到的实体数组与需要识别的实体数组比对private comparisonPhysical =(objects: any,maxPt: McGePoint3d,minPt: McGePoint3d,angle: number):any=>{const calssNames = Object.keys(this.selectObjects);if(!calssNames)return;let num =0;if(calssNames.includes('McDbLine')){let ents = objects['McDbLine'];const lineObjects =this.objectsDetails['McDbLine'];for(let i =0; i < lineObjects.length; i++){const element = lineObjects[i];let _line =(element.entity as McDbLine).clone()as McDbLine;
_line.move(this.formPt,this.toPt);
_line.rotate(this.toPt, angle);constindexArr: number[]=[];const mLineArr = ents.filter((item: McDbLine,index: number)=>{if(Math.abs(element.length - item.getLength().val)<this.dTol){let r1 = item.startPoint.distanceTo(_line.startPoint)<this.dTolPt && item.endPoint.distanceTo(_line.endPoint)<this.dTolPt;let r2 = item.startPoint.distanceTo(_line.endPoint)<this.dTolPt && item.endPoint.distanceTo(_line.startPoint)<this.dTolPt;if(r1 || r2) indexArr.push(index);return r1 || r2;}});if(mLineArr.length >0){
num +=1;
ents.splice(indexArr[0],1);
ents =[...ents];}else{break;}}};if(calssNames.includes('McDbArc')){let ents = objects['McDbArc'];const arcObjects =this.objectsDetails['McDbArc'];for(let i =0; i < arcObjects.length; i++){const element = arcObjects[i];let _arc =(element.entity as McDbArc).clone()as McDbArc;
_arc.move(this.formPt,this.toPt);
_arc.rotate(this.toPt, angle);const startPoint = _arc.getPointAtDist(0).val;const endPoint = _arc.getPointAtDist(element.length).val;constindexArr: number[]=[];const mArcArr = ents.filter((item: McDbArc,index: number)=>{const _length = item.getLength().val;const{bugle: _bugle }=this.getBugle(item, _length)if(Math.abs(element.length - item.getLength().val)<this.dTol && Math.abs(_bugle - element.bugle)<this.dTol){const _startPoint = item.getPointAtDist(0).val;const _endPoint = item.getPointAtDist(_length).val;let r1 = startPoint.distanceTo(_startPoint)<this.dTolPt && endPoint.distanceTo(_endPoint)<this.dTolPt;let r2 = startPoint.distanceTo(_endPoint)<this.dTolPt && endPoint.distanceTo(_startPoint)<this.dTolPt;if(r1 || r2){
indexArr.push(index);}return r1 || r2;}});if(mArcArr.length >0){
num +=1;
ents.splice(indexArr[0],1);
ents =[...ents];}else{break;}}};if(calssNames.includes('McDbCircle')){let ents = objects['McDbCircle'];const circleObjects =this.objectsDetails['McDbCircle'];for(let i =0; i < circleObjects.length; i++){const element = circleObjects[i];let _circle =(element.entity as McDbCircle).clone()as McDbCircle;
_circle.move(this.formPt,this.toPt);
_circle.rotate(this.toPt, angle);const center = _circle.center;const radius = _circle.radius;constindexArr: number[]=[];const mCiecleArr = ents.filter((item: McDbCircle,index: number)=>{const _center = item.center;const _radius = item.radius;const r = Math.abs(_radius - radius)<this.dTol && center.distanceTo(_center)<this.dTolPt;if(r){
indexArr.push(index)}return r;});if(mCiecleArr.length >0){
num +=1;
ents.splice(indexArr[0],1);
ents =[...ents];}else{break;}}};if(calssNames.includes('McDbBlockReference')){/** 平移+旋转=>比对包围盒大小+块名字 */let ents = objects['McDbBlockReference'];const blkObjects =this.objectsDetails['McDbBlockReference'];for(let i =0; i < blkObjects.length; i++){const element = blkObjects[i];let _blk =(element.entity as McDbBlockReference).clone()as McDbBlockReference;
_blk.move(this.formPt,this.toPt);
_blk.rotate(this.toPt, angle);const blkName = _blk.blockName;const{minPt: blkMinPt,maxPt: blkMaxPt }=this.getBlkMidPt(_blk);constindexArr: number[]=[];const mArcArr = ents.filter((item: McDbBlockReference,index: number)=>{const _blkName = item.blockName;if(_blkName === blkName){if(this.classNames.length ===1){
indexArr.push(index);returntrue}else{const{minPt: _blkMinPt,maxPt: _blkMaxPt }=this.getBlkMidPt(item);let r1 = blkMinPt.distanceTo(_blkMinPt)<this.dTolPt && blkMaxPt.distanceTo(_blkMaxPt)<this.dTolPt;if(r1){
indexArr.push(index);}return r1
}}});if(mArcArr.length >0){
num +=1;
ents.splice(indexArr[0],1);
ents =[...ents];}else{return;}}}if(calssNames.includes('McDbPolyline')){/** 平移+旋转=>比对凸度+点位置 */let ents = objects['McDbPolyline'];const plObjects =this.objectsDetails['McDbPolyline'];for(let i =0; i < plObjects.length; i++){const element = plObjects[i];let _poly =(element.entity as McDbPolyline).clone()as McDbPolyline;
_poly.move(this.formPt,this.toPt);
_poly.rotate(this.toPt, angle);const{ lengthArr, bugleArr, vertNum }=this.getPlDetails(_poly);constindexArr: number[]=[];const mPlArr = ents.filter((item: McDbPolyline,index: number)=>{const _length = item.getLength().val;if(Math.abs(_length - element.length)<this.dTol && _poly.isClosed === _poly.isClosed){const{lengthArr: _lengthArr,bugleArr: _bugleArr,vertNum: _vertNum }=this.getPlDetails(item);const revBugleArr = _bugleArr.map(item=> item ===0? item :-item);const r1 = lengthArr.toString()=== _lengthArr.toString()&& bugleArr.toString()=== _bugleArr.toString();let r2 =false;if(!r1){
r2 = lengthArr.toString()===[..._lengthArr].reverse().toString()&& bugleArr.toString()===[...revBugleArr].reverse().toString();}const r3 = vertNum === _vertNum;if((r1 || r2)&& r3){let _num =0;for(let i =0; i < vertNum; i++){const pt = _poly.getPointAt(i).val;const width = _poly.getWidthsAt(i);const _pt = r2 ? item.getPointAt(vertNum -1- i).val : item.getPointAt(i).val;const _width = r2 ? item.getWidthsAt(vertNum -1- i): item.getWidthsAt(i);if(pt.distanceTo(_pt)<this.dTolPt && width.val1 === _width.val1 && width.val2 === _width.val2){
_num +=1}else{// 两个点且反向的情况if(vertNum ===2&& i ===0){const _pt1 = item.getPointAt(0).val;const _pt2 = item.getPointAt(1).val;const _width = item.getWidthsAt(0);const pt2 = _poly.getPointAt(1).val;if(pt2.distanceTo(_pt2)<this.dTolPt && pt.distanceTo(_pt1)<this.dTolPt && width.val1 === _width.val2 && width.val2 === _width.val1){
_num +=2;}}}};if(_num === vertNum){
indexArr.push(index);}return _num === vertNum;}}});if(mPlArr.length >0){
num +=1;
ents.splice(indexArr[0],1);
ents =[...ents];}else{break;}}}if(num ===this.objectCount){// 匹配成功const pt = minPt.clone().addvec(maxPt.clone().sub(minPt).normalize().mult(minPt.distanceTo(maxPt)/2));const res =this.graphBoxsPos.filter(point=> pt.distanceTo(point)<this.dTolPt);if(!res.length){this.graphBoxsPos.push(pt)}};this.patternList.graphBoxsPos =[...this.graphBoxsPos];}// 识别包围盒是否符合要求private identificationBBox =(minPt: McGePoint3d,maxPt: McGePoint3d,angle: number):any=>{const _ss =newMxCADSelectionSet()
_ss.crossingSelect(minPt.x, minPt.y, maxPt.x, maxPt.y);const boxEntity ={};
_ss.forEach(id=>{const ent = id.getMcDbEntity();if(!boxEntity[ent.objectName]) boxEntity[ent.objectName]=[];
boxEntity[ent.objectName].push(ent);});let r =this.classNames.filter(name=> boxEntity[name]&&this.selectObjects[name].length <= boxEntity[name].length);if(r.length ===this.classNames.length){this.comparisonPhysical(boxEntity, maxPt, minPt, angle);}}};
调用 SearchSamePattern 类,实现图形识别功能,代码如下:
登录后复制
/** 获取需要识别的图形 */constgetIdentificationPattern=async()=>{// 获取需要识别的图形const ss =newMxCADSelectionSet();// 选择集const filter =newMxCADResbuf([DxfCode.kEntityType,"LINE,ARC,CIRCLE,LWPOLYLINE,INSERT"])if(!await ss.userSelect("请点击左键或拉框选择需要识别的图形:", filter))return;if(ss.count()==0)return;// 搜索图形const searchPattern =newSearchSamePattern();// 搜索图形简图地址const patternImgUrl =await searchPattern.setSelectset(ss);if(!patternImgUrl.value)return;// 获取识别结果const searchList = searchPattern.identifyGraphics();
console.log(searchList);}
版权归原作者 egzosn 所有, 如有侵权,请联系我们删除。